Формічні функції - конкурс мурашок королеви гори


104

Дивитися в прямому ефірі | Активні відповіді | Додати нову відповідь | Чат | Вихідний код | Таблиця лідерів

Нові турніри, коли це потрібно. Нові гравці та нові оновлення дуже вітаються.

Королева мурашка на танцполі із зміною кольорових плиток

Не справжні кадри гри.

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

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

Перегляд гри

Це змагання JavaScript, що означає, що ви можете спостерігати за грою в прямому ефірі у вашому браузері, натиснувши посилання нижче.

Натисніть тут, щоб переглянути гру, що грається наживо

Величезне спасибі Хельці Хомбі за оригінальні конкурси Stack Snippet King of the Hill, Red vs. Blue - Pixel Team Battlebots та Block Building Bot Flocks , які надали ідею про веб-браузер, на якому розміщено KotH та якісно повідомили код для цього.

Величезне спасибі також за всі відгуки та тестування від чудових людей в пісочниці та в чаті.

Таблиця лідерів

Зображення головних місць лідерів

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

Ці таблиця лідерів ґрунтуються на гравцях , як вони були в неділю 2 - го вересня 2018 року.

Скріншоти

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

зображення арени зображення арени зображення арени зображення арени зображення арени зображення арени зображення арени зображення арени зображення арени зображення арени зображення арени зображення арени зображення арени зображення арени зображення арени зображення арени

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

Арена

Арена - це тороїдальна (крайова обгортка) сітка квадратних комірок. Він має ширину 2500 і висоту 1000. Усі комірки починаються як колір 1.

Спочатку рівно 0,1% клітин буде містити їжу. 2500 частин їжі будуть розкидані рівномірно випадковим чином. Під час гри не буде введено нової їжі.

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

Здібності мурашника

  • Приціл: кожен мураш бачить 9 клітин у своєму сусідстві 3 на 3. Він не знає жодних інших мурах поза цим мікрорайоном. Він бачить вміст кожної з 9 клітин (інші мурахи та їжу), а також колір кожної клітини .
  • Ніякої пам’яті: кожен мурашник приймає свої рішення, виходячи з того, що бачить - він не пам’ятає, що робив у попередній черзі, і не має можливості зберігати інші стани, крім кольорів осередків арени.
  • Немає орієнтації: Мурашка не знає, де вона знаходиться або в який бік стикається - у неї немає поняття Північ. Район 3 на 3 буде представлений йому у випадково повернутій орієнтації, яка змінюється кожного повороту, щоб він не міг навіть ходити по прямій лінії, якщо у нього не будуть кольори, щоб керувати нею. (Зробити один і той же хід кожен поворот призводить до випадкової ходи, а не прямої.)
  • Переміщення, кольорове маркування та виробництво робітників: Див. Результат нижче.
  • Безсмертя: це високогірні мурахи, які не можуть померти. Ви можете сплутати суперників-мурашок, змінивши кольори навколо них або обмежуючи їх переміщення, оточуючи їх своїми 8 мурашками, але вони не можуть завдати шкоди крім цього.
  • Перевезення їжі: працівник може нести до 1 шматка їжі. Королева може нести довільну кількість їжі.
  • Передача їжі: Якщо працівник знаходиться поруч із королевою (у будь-якому з 8 напрямків), їжа буде автоматично передана одним із наступних способів:
    • Навантажений працівник, що примикає до власної королеви, передасть їжу своїй королеві.
    • Неопрацьований працівник, сусідній з ворожою королевою, вкраде 1 шматок їжі, якщо він присутній.

Робітник не може вкрасти у робітника, і королева не може вкрасти у королеви. Також робітник не може брати їжу у своєї королеви, і королева не може вкрасти у ворога.

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

Кодування

Забезпечте функціональний орган

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

Опублікуйте відповідь, що містить блок коду, що показує тіло функції JavaScript, і він буде автоматично включений до контролера (просто оновіть сторінку контролера). Ім’я гравця утворює заголовок відповіді у формі # PlayerName(яка буде врізана максимум до 40 символів у контрольних таблицях).

Ні стану, ні часу, ні випадкових

Функція не має доступу до глобальних змінних і не повинна зберігати стан між витками. Він може використовувати вбудовані функції, які не передбачають збереження стану. Наприклад, використання Math.abs()нормально, але Date.getTime()не повинно використовуватись.

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

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

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

Console.log

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

Вхід і вихід

Вхідні дані

Орієнтація входу буде обрана випадковим чином для кожного мурашника та для кожного ходу. Вхід буде обертатися на 0, 90, 180 або 270 градусів, але ніколи не відображатиметься.

Клітини нумеруються англійською мовою читання:

0 1 2
3 4 5
6 7 8

Мурашина функція отримає масив, який називається view, містить об'єкт для кожної з 9 видимих ​​комірок. Кожен об’єкт матиме наступне:

color: a number from 1 to 8
food: 0 or 1
ant: null if there is no ant on that cell, or otherwise an ant object

Якщо клітина містить мурашку, об’єкт мурашки матиме наступне:

food: 0 or more (maximum 1 for a worker)
type: 1 to 4 for a worker, or 5 for a queen
friend: true or false

Мураха може визначити свої власні дані, дивлячись на мурашки в центральній клітинці view[4].ant. Наприклад, view[4].ant.typeце 5 для королеви, або число від 1 до 4 для працівника (із зазначенням його типу).

Вихідні дані

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

cell: a number from 0 to 8 (mandatory)
color: a number from 1 to 8 (optional)
type: a number from 1 to 4 (optional)

Якщо colorі typeопущено або нуль, то cellвказує клітинку, до якої потрібно перейти.

Якщо colorне дорівнює нулю, зазначена комірка встановлюється на цей колір.

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

Приклади виходів:

{cell:0}: move to cell 0
{cell:4}: move to cell 4 (that is, do nothing, as 4 is the central cell)
{cell:4, color:8}: set own cell to color 8
{cell:6, type:1}: create a type 1 worker on cell 6
{cell:6, color:1}: set cell 6 to color 1
{cell:6, color:0}: equivalent to just `{cell:6}` - move rather than set color
{cell:6, type:0}: equivalent to just `{cell:6}` - move rather than create worker
{cell:6, color:0, type:0}: move to cell 6 - color 0 and type 0 are ignored

Недійсні результати:

{cell:9}: cell must be from 0 to 8
{cell:0, color:9}: color must be from 1 to 8
{cell:0, type:5}: type must be from 1 to 4 (cannot create a new queen)
{cell:4, type:1}: cannot create a worker on a non-empty cell
{cell:0, color:1, type:1}: cannot set color and create worker in the same turn

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

Тип робітника

Кожен працівник має тип , число від 1 до 4. Це не має ніякого значення для контролера і для гравця має справу з тим, як вони хочуть. Королева могла випускати всіх своїх робітників як тип 1 і надавати їм однакову поведінку, або вона могла виробляти декілька типів працівників з різною поведінкою, можливо, тип 1 як комбайни та тип 2 як охоронці.

Номер типу робітника призначається вами під час створення працівника, і його не можна буде потім змінювати. Використовуйте його, як вважаєте за потрібне.

Черговий порядок

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

Обмежити кількість гравців

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

Ліміт часу на оборот

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

Дискваліфікація

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

Гравець буде дискваліфікований за будь-яку з наведених нижче для будь-яких своїх мурах (королеви чи робітника):

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

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

Кілька відповідей та редагування

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

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

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

Оцінка балів

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

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

Чат

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

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


2
@DestructibleLemon заради того, щоб хтось читав ці коментарі, я відповів, що в чаті
trichoplax


7
Гей, я зробив річ ! Вам це може бути цікаво, оскільки він натхненний цим викликом і включає в себе тест Formic Functions .
Дейв

2
@Dave Ваш контролер надзвичайно швидкий :) - але дозвольте зазначити, що його оцінка, схоже, відрізняється від оригіналу у випадках, коли королеви прив'язуються до їжі в кінці гри. Оцінка повинна бути кількістю інших учасників, у яких королеви тримають (суворо) менше їжі. Наприклад, якщо у трьох гравців є 0 їжі в кінці, вони повинні набрати нуль для цієї гри, а не трьох.
GNiklasch

2
@GNiklasch спасибі; фіксований. Також я бачу, що ваш мураш домінує в грі зараз. Вражає!
Дейв

Відповіді:


20

Криміналістичні мурахи

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

// == Shared low-level helpers for all solutions ==

var QUEEN = 5;

var WHITE = 1;
var COL_MIN = WHITE;
var COL_LIM = 9;

var CENTRE = 4;

var NOP = {cell: CENTRE};

var DIR_FORWARDS = false;
var DIR_REVERSE = true;
var SIDE_RIGHT = true;
var SIDE_LEFT = false;

function sanity_check(movement) {
  var me = view[CENTRE].ant;
  if(!movement || movement.cell < 0 || movement.cell > 8) {
    return false;
  }
  if(movement.type) {
    if(movement.color) {
      return false;
    }
    if(movement.type < 1 || movement.type > 4) {
      return false;
    }
    if(view[movement.cell].ant || view[movement.cell].food) {
      return false;
    }
    if(me.type !== QUEEN || me.food < 1) {
      return false;
    }
    return true;
  }
  if(movement.color) {
    if(movement.color < COL_MIN || movement.color >= COL_LIM) {
      return false;
    }
    if(view[movement.cell].color === movement.color) {
      return false;
    }
    return true;
  }
  if(view[movement.cell].ant) {
    return false;
  }
  if(view[movement.cell].food + me.food > 1 && me.type !== QUEEN) {
    return false;
  }
  return true;
}

function as_array(o) {
  if(Array.isArray(o)) {
    return o;
  }
  return [o];
}

function best_of(movements) {
  var m;
  for(var i = 0; i < movements.length; ++ i) {
    if(typeof(movements[i]) === 'function') {
      m = movements[i]();
    } else {
      m = movements[i];
    }
    if(sanity_check(m)) {
      return m;
    }
  }
  return null;
}

function play_safe(movement) {
  // Avoid disqualification: no-op if moves are invalid
  return best_of(as_array(movement)) || NOP;
}

var RAND_SEED = (() => {
  var s = 0;
  for(var i = 0; i < 9; ++ i) {
    s += view[i].color * (i + 1);
    s += view[i].ant ? i * i : 0;
    s += view[i].food ? i * i * i : 0;
  }
  return s % 29;
})();

var ROTATIONS = [
  [0, 1, 2, 3, 4, 5, 6, 7, 8],
  [6, 3, 0, 7, 4, 1, 8, 5, 2],
  [8, 7, 6, 5, 4, 3, 2, 1, 0],
  [2, 5, 8, 1, 4, 7, 0, 3, 6],
];

function try_all(fns, limit, wrapperFn, checkFn) {
  var m;
  fns = as_array(fns);
  for(var i = 0; i < fns.length; ++ i) {
    if(typeof(fns[i]) !== 'function') {
      if(checkFn(m = fns[i])) {
        return m;
      }
      continue;
    }
    for(var j = 0; j < limit; ++ j) {
      if(checkFn(m = wrapperFn(fns[i], j))) {
        return m;
      }
    }
  }
  return null;
}

function identify_rotation(testFns) {
  // testFns MUST be functions, not constants
  return try_all(
    testFns,
    4,
    (fn, r) => fn(ROTATIONS[r]) ? ROTATIONS[r] : null,
    (r) => r
  );
}

function near(a, b) {
  return (
    Math.abs(a % 3 - b % 3) < 2 &&
    Math.abs(Math.floor(a / 3) - Math.floor(b / 3)) < 2
  );
}

function try_all_angles(solverFns) {
  return try_all(
    solverFns,
    4,
    (fn, r) => fn(ROTATIONS[r]),
    sanity_check
  );
}

function try_all_cells(solverFns, skipCentre) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i === CENTRE && skipCentre) ? null : fn(i)),
    sanity_check
  );
}

function try_all_cells_near(p, solverFns) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i !== p && near(p, i)) ? fn(i) : null),
    sanity_check
  );
}

function ant_type_at(i, friend) {
  return (view[i].ant && view[i].ant.friend === friend) ? view[i].ant.type : 0;
}

function friend_at(i) {
  return ant_type_at(i, true);
}

function foe_at(i) {
  return ant_type_at(i, false);
}

function foe_near(p) {
  for(var i = 0; i < 9; ++ i) {
    if(foe_at(i) && near(i, p)) {
      return true;
    }
  }
  return false;
}

function move_agent(agents) {
  var me = view[CENTRE].ant;
  var buddies = [0, 0, 0, 0, 0, 0];
  for(var i = 0; i < 9; ++ i) {
    ++ buddies[friend_at(i)];
  }

  for(var i = 0; i < agents.length; i += 2) {
    if(agents[i] === me.type) {
      return agents[i+1](me, buddies);
    }
  }
  return null;
}

function grab_nearby_food() {
  return try_all_cells((i) => (view[i].food ? {cell: i} : null), true);
}

function go_anywhere() {
  return try_all_cells((i) => ({cell: i}), true);
}

function colours_excluding(cols) {
  var r = [];
  for(var i = COL_MIN; i < COL_LIM; ++ i) {
    if(cols.indexOf(i) === -1) {
      r.push(i);
    }
  }
  return r;
}

function generate_band(start, width) {
  var r = [];
  for(var i = 0; i < width; ++ i) {
    r.push(start + i);
  }
  return r;
}

function colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function(c) {
      return colours[(colours.indexOf(c) + 1) % colours.length];
    }
  };
}

function random_colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function() {
      return colours[RAND_SEED % colours.length];
    }
  };
}

function fast_diagonal(colourBand) {
  var m = try_all_angles([
    // Avoid nearby checked areas
    (rot) => {
      if(
        !colourBand.contains(view[rot[0]].color) &&
        colourBand.contains(view[rot[5]].color) &&
        colourBand.contains(view[rot[7]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // Go in a straight diagonal line if possible
    (rot) => {
      if(
        !colourBand.contains(view[rot[0]].color) &&
        colourBand.contains(view[rot[8]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // When in doubt, pick randomly but avoid doubling-back
    (rot) => (colourBand.contains(view[rot[0]].color) ? null : {cell: rot[0]}),

    // Double-back when absolutely necessary
    (rot) => ({cell: rot[0]})
  ]);

  // Lay a colour track so that we can avoid doubling-back
  // (and mess up our foes as much as possible)
  if(!colourBand.contains(view[CENTRE].color)) {
    var prevCol = m ? view[8-m.cell].color : WHITE;
    return {cell: CENTRE, color: colourBand.next(prevCol)};
  }

  return m;
}

function follow_edge(obstacleFn, side) {
  // Since we don't know which direction we came from, this can cause us to get
  // stuck on islands, but the random orientation helps to ensure we don't get
  // stuck forever.

  var order = ((side === SIDE_LEFT)
    ? [0, 3, 6, 7, 8, 5, 2, 1, 0]
    : [0, 1, 2, 5, 8, 7, 6, 3, 0]
  );
  return try_all(
    [obstacleFn],
    order.length - 1,
    (fn, i) => (fn(order[i+1]) && !fn(order[i])) ? {cell: order[i]} : null,
    sanity_check
  );
}

function start_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => ((
      !protectedCols.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 2 : 0]].color) &&
      !colourBand.contains(view[rot[1]].color)
    )
      ? {cell: rot[right ? 5 : 3], color: colourBand.next(WHITE)}
      : null)
  ]);
}

function lay_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => {
      var ahead = rot[right ? 2 : 0];
      var behind = rot[right ? 8 : 6];
      if(
        colourBand.contains(view[behind].color) &&
        !protectedCols.contains(view[ahead].color) &&
        !colourBand.contains(view[ahead].color) &&
        !colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        return {cell: ahead, color: colourBand.next(view[behind].color)};
      }
    }
  ]);
}

function follow_dotted_path(colourBand, side, direction) {
  var forwards = (direction === DIR_REVERSE) ? 7 : 1;
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    // Cell on our side? advance
    (rot) => {
      if(
        colourBand.contains(view[rot[right ? 5 : 3]].color) &&
        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[0]].color) &&
        !colourBand.contains(view[rot[2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    },

    // Cell ahead and behind? advance
    (rot) => {
      var passedCol = view[rot[right ? 8 : 6]].color;
      var nextCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(passedCol) &&
        nextCol === colourBand.next(passedCol) &&

        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[right ? 0 : 2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    }
  ]);
}

function escape_dotted_path(colourBand, side, newColourBand) {
  var right = (side === SIDE_RIGHT);
  if(!newColourBand) {
    newColourBand = colourBand;
  }

  return try_all_angles([
    // Escape from beside the line
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        !colourBand.contains(view[rot[right ? 8 : 6]].color) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[rot[7]].color) ||
        colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        // not oriented, or in a corner
        return null;
      }
      return best_of([
        {cell: rot[right ? 0 : 2], color: newColourBand.next(approachingCol)},
        {cell: rot[right ? 3 : 5]},
        {cell: rot[right ? 0 : 2]},
        {cell: rot[right ? 6 : 8]},
        {cell: rot[right ? 2 : 0]},
        {cell: rot[right ? 8 : 6]},
        {cell: rot[right ? 5 : 3]}
      ]);
    },

    // Escape from inside the line
    (rot) => {
      if(
        !colourBand.contains(view[rot[7]].color) ||
        !colourBand.contains(view[rot[1]].color) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      return best_of([
        {cell: rot[3]},
        {cell: rot[5]},
        {cell: rot[0]},
        {cell: rot[2]},
        {cell: rot[6]},
        {cell: rot[8]}
      ]);
    }
  ]);
}

function latch_to_dotted_path(colourBand, side) {
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(approachingCol) &&
        view[rot[right ? 8 : 6]].color === colourBand.next(approachingCol) &&
        !colourBand.contains(view[rot[right ? 5 : 3]].color)
      ) {
        // We're on the wrong side; go inside the line
        return {cell: rot[right ? 5 : 3]};
      }
    },

    // Inside the line? pick a side
    (rot) => {
      var passedCol = view[rot[7]].color;
      var approachingCol = view[rot[1]].color;
      if(
        !colourBand.contains(passedCol) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      if((approachingCol === colourBand.next(passedCol)) === right) {
        return best_of([{cell: rot[3]}, {cell: rot[6]}, {cell: rot[0]}]);
      } else {
        return best_of([{cell: rot[5]}, {cell: rot[2]}, {cell: rot[8]}]);
      }
    }
  ]);
}


// == High-level logic begins here ==


var PARTNER = 1;
var SENTINEL = 2;

var COL_DANCING1 = 8;
var COL_DANCING2 = 7;
var SAFE_COLOURS = random_colour_band(colours_excluding([WHITE, COL_DANCING1]));

function pass_time() {
  // Wait patiently for the blockage to go away by setting
  // random cell colours (unless we're near the sentinel)
  for(var i = 0; i < 9; ++ i) {
    if(i !== 4 && friend_at(i) === SENTINEL) {
      return null;
    }
  }
  return {cell: 0, color: SAFE_COLOURS.next()};
}

function move_sentinel(me, buddies) {
  // Our job is to be a sentinel showing when the queen has wrapped around.
  // We are created first, so will move first.
  // We won't find any food.

  if(!buddies[QUEEN] && !buddies[PARTNER]) {
    // No ongoing dance; make sure our state is good for when they arrive
    return try_all_angles([
      {cell: CENTRE, color: WHITE},
      (rot) => ({cell: rot[1], color: COL_DANCING2}),
      (rot) => ((view[rot[0]].color === COL_DANCING1)
        ? {cell: rot[0], color: SAFE_COLOURS.next()}
        : null)
    ]);
  }

  // Dance when queen passes
  var danceStage = view[CENTRE].color;

  if(danceStage === WHITE) {
    // Dance has not begun yet, but queen & partner are nearby
    return try_all_angles((rot) => {
      if(friend_at(rot[5]) === QUEEN && friend_at(rot[8]) === PARTNER) {
        return {cell: CENTRE, color: COL_DANCING1};
      }
    });
  }

  if(danceStage === COL_DANCING1) {
    if(buddies[PARTNER]) {
      return null; // Wait for partner to see us
    }
    // Partner saw us @8 and moved down, queen followed.
    // We must also move down (will end up on a COL_DANCING2)
    return try_all_angles((rot) =>
      ((friend_at(rot[8]) === QUEEN) ? {cell: rot[7]} : null));
  }

  // Move towards queen counter-clockwise when she's diagonally connected
  return try_all_angles((rot) =>
    ((friend_at(rot[2]) === QUEEN) ? {cell: rot[1]} : null));
}

function move_partner(me, buddies) {
  // Our job is to travel with the queen and keep her oriented.
  // We are created second, so move after the sentinel.
  // Any food we find will immediately go to the queen, since
  // we are adjacent at all times.

  // Queen will be N of us; orient ourselves
  var rot = identify_rotation((rot) => friend_at(rot[1]) === QUEEN);

  if(!rot) {
    // Queen is lagging or lost;
    return null;
  }

  var danceStage = view[rot[0]].color;
  if(
    friend_at(rot[0]) === SENTINEL &&
    (danceStage === COL_DANCING1 || danceStage === COL_DANCING2)
  ) {
    // Dance down (queen will follow)
    return {cell: rot[7]};
  }

  if(view[rot[0]].ant) {
    // Queen is blocked
    return null;
  }

  // Lead queen if both can move
  return {cell: rot[3]};
}

function move_queen(me, buddies) {
  // Our job is to travel over the entire level collecting food.
  // We move last.

  if(buddies[PARTNER]) {
    // Partner will be S or SW of us; follow if they are ahead
    return try_all_angles((rot) =>
      (friend_at(rot[6]) === PARTNER) ? {cell: rot[3]} : null);
  }

  var rot = identify_rotation((rot) => friend_at(rot[3]) === SENTINEL);
  if(rot && view[rot[0]].color >= 7) {
    // Dance down (follow partner)
    return {cell: rot[7]};
  }

  // We're on our own, or the buddy strategy failed. Start again.

  rot = identify_rotation((rot) => friend_at(rot[5]) === SENTINEL);
  if(rot && me.food >= 1) {
    // Already have a sentinel; just need a partner
    return best_of([
      {cell: rot[7], type: PARTNER},
      {cell: rot[6], type: PARTNER},
    ]);
  } else if(me.food >= 2) {
    // Create sentinel first so that we'll know to create the partner next.
    // (ensure the sentinel is created on a white cell so that it won't
    // think it's dancing)
    return try_all_angles(
      (rot) => ((view[rot[5]].color === WHITE)
        ? {cell: rot[5], type: SENTINEL} : null),
      (rot) => ({cell: rot[5], color: WHITE})
    );
  }

  // Not able to start yet; fall back to lone behaviour:
  // Random-walk until we find or make a buddy
  return best_of([
    grab_nearby_food,
    fast_diagonal.bind(null, SAFE_COLOURS),
    go_anywhere
  ]);
}

return play_safe([move_agent([
  PARTNER, move_partner,
  SENTINEL, move_sentinel,
  QUEEN, move_queen,
]), pass_time]);

Криміналістичні мурахи застосовують науковий підхід до підмітання сітки. Після первинної шаленої боротьби за їжу будуть створені 2 робочі мурахи. Ролі:

Королева

Королева об'єднається з партнером для подорожі по прямій зі швидкістю світла. Жоден з них не розходиться для отримання їжі; вони просто хапають те, що натрапляють.

Партнер

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

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

Sentinel

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

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


Вони виконують дуже послідовно; між ними вони можуть змітати по 2 комірки в кожному кадрі, що понад 30000 кадрів означає 60000 клітин, а 0,1%, що містить їжу, означає кінцевий бал 60, якого вони досить послідовно досягають.


(і ось інший, який я підготував, поки питання було бета! - це я зробив на даний момент; я впевнений, що їх досить швидко поб'ють!)
Дейв,

Оцінка надзвичайно послідовна. Буде цікаво подивитися, як це впливає, коли арена заповнює більше конкурентів ...
trichoplax

Цікаво, чи допоможе додавання іншого партнера з іншого боку королеви?
К Чжан

1
@KZhang Я думаю, що це буде (теоретично це збільшить бал до ~ 90), але досить важко тримати їх синхронізовано разом із дозорним! Танець "перемістити всіх вгору на 2 пікселі" потребував певного часу. Перехід на 3 пікселі заблокував би один із трюків, на які я спирався (дозорний заздалегідь готуючи пробіли навколо себе).
Дейв

Верх першого лідеру ...
трихоплакс

18

Розсувні гірники 6.4

const DEBUG = false;
const ADD = (a,b) => a + b;
var toReturn;
var me = view[4].ant;
me.me = true; // for basedOn to know
var food = me.food;
var type = me.type;
var isQueen = type == 5;

// raw directions
const UL = 0; const U  = 1; const UR = 2;
const L  = 3; const C  = 4; const R  = 5;
const DL = 6; const D  = 7; const DR = 8;

// directions from the reference point
const ul = 16; const u  = 17; const ur = 18;
const l  = 19; const c  = 20; const r  = 21;
const dl = 22; const d  = 23; const dr = 24;
const rp = 16;

function allRots (arr) {
  return [arr,
  [arr[2], arr[5], arr[8],
   arr[1], arr[4], arr[7],
   arr[0], arr[3], arr[6]],

  [arr[8], arr[7], arr[6],
   arr[5], arr[4], arr[3],
   arr[2], arr[1], arr[0]],

  [arr[6], arr[3], arr[0],
   arr[7], arr[4], arr[1],
   arr[8], arr[5], arr[2]]];
}
var allVRots = allRots(view);

function rotateCW3([[a,b,c],[d,e,f],[g,h,i]]) {
  return [[g,d,a],[h,e,b],[i,f,c]]
}

function on (where, what) {
  if (Array.isArray(where)) return where.some(c=>on(c, what));
  if (Array.isArray(what)) return what.some(c=>on(where, c));
  return basedOn(get(where), what);
}
function find (what) {
  return view.findIndex(c=>basedOn(c, what));
}
function findAll (what) {
  return view.map((c,i)=>[c,i]).filter(c=>basedOn(c[0], what)).map(c=>c[1]);
}
function count (what) {
  return findAll(what).length;
}
function findRel (what) {
  return ref(find(what));
}
function findAllRel (what) {
  return findAll(what).map(c=>ref(c));
}
function found (what) {
  return find(what) != -1;
}
function get (dir) {
  if (Array.isArray(dir)) return dir.map(c=>get(c));
  return view[raw(dir)];
}
function deq (a, b) {
  return a==b || raw(a)==raw(b);
}

// returns a random number from 0 to 4, based on the rotation. Will always have a possibility of being 0
function random4 () {
  var scores = allRots(view.map(c=>c.color)).map((c) => {
    let cscore = 0;
    c.forEach((c) => {
      cscore*= 8;
      cscore+= c-1;
    });
    return cscore;
  });
  var bestscore = -1, bestindex = 1;
  scores.forEach((score, index) => {
    if (score > bestscore) {
      bestscore = score;
      bestindex = index;
    }
  })
  return bestindex;
}

function rotate (what, times) {
  for (var i = 0; i < times; i++) what = [2,5,8,1,4,7,0,3,6][what];
  return what;
}

function raw(dir) {
  if (dir&rp) return rotate(dir&~rp, selectedRot);
  return dir;
}

function ref(dir) {
  if (dir == -1) return -1;
  if (dir&rp) return dir;
  return rotate(dir, 4-selectedRot)|rp;
}

function move(dir, force) {
  if (Array.isArray(dir)) return dir.some(c=>move(c, force));
  dir = raw(dir);
  return result({cell:dir}, force);
}

function color(dir, col) {
  if (Array.isArray(dir)) return dir.some(cdir => !color(cdir, col));
  dir = raw(dir);
  if (view[dir].color == col) return true;
  result({cell:dir, color:Math.abs(col)});
  return false;
}

function rcolOf(what) {
  return Number.isInteger(what)? what : what.color;
}

function colOf(what) {
  return Math.abs(Number.isInteger(what)? what : what.color);
}
function sees(c1,c2) {
  c1 = raw(c1);
  c2 = raw(c2);
  return Math.abs(c1%3-c2%3)<2 && Math.abs(Math.floor(c1/3)-Math.floor(c2/3))<2;
}

function spawn(dir, t) {
  if (Array.isArray(t)) return t.some(c=>spawn(dir, c));
  if (Array.isArray(dir)) return dir.some(c=>spawn(c, t));
  dir = raw(dir);
  return result({cell:dir, type:t});
}
// repairs a single cell
function correct(dir) {
  dir = raw(dir);
  let col = colOf(selectedPt[dir]);
  if (col && view[dir].color != col) {
    color(dir, col);
    return false;
  }
  return true;
}
// if pattern is repaired, returns true, otherwise fixes one cell
// firstdirs is lowercase (if you do want it to be from the patterns POV)
function repair(firstdirs, onlyThose) {
  //log("FD",firstdirs);
  var found = [];
  view.forEach((v, i) => {
    let col = colOf(selectedPt[i]);
    if (col && v.color != col) {
      found.push(i);
    }
  });
  if (found.length == 0) return true;
  if (firstdirs && (firstdirs = firstdirs.map(c=>raw(c))).some(c=>found.includes(c))) {
    let dir = firstdirs.find(c=>found.includes(c));
    let col = colOf(selectedPt[dir]);
    color(dir, col);
    return false;
  }
  if (!onlyThose) {
    let dir = found[random4() % found.length];
    let col = colOf(selectedPt[dir]);
    color(dir, col);
    return false;
  } else return true;
}

function flatten (arr) {
  return arr.reduce((a,b)=>a.concat(b));
}

var selectedHp, selectedVp, selectedPt, selectedRot;

class Pattern {
  constructor(pattern, inherit) {
    this.pt = pattern;
    if (inherit) {
      this.vp = inherit.vp;
      this.hp = inherit.hp;
      this.rot = inherit.rot;
    } else {
      this.vp = 0;
      this.hp = 0;
      this.rot = 0;
    }
  }

  rotateClockwise() {
    var arr = [];
    for (var i = 0; i < this.pt[0].length; i++) {
      var sarr = [];
      for (var j = this.pt.length-1; j >= 0; j--) {
        sarr.push(this.pt[j][i]);
      }
      arr.push(sarr);
    }
    //log(arr);
    var res = new Pattern(arr, this);
    res.rot = (this.rot+1) % 4;
    return res;
  }

  select(x, y, w, h) {
    var res = new Pattern(this.pt.slice(y, y+h).map(c=>c.slice(x, x+w)), this);
    res.hp+= x;
    res.vp+= y;
    return res;
  }

  rots(dir) {
    var pts = [];
    var pt = new Pattern(this.pt, this);
    for (let i = 0; i < this.lengthIn(dir); i++) {
      pts.push(pt);
      pt = pt.rotate(dir);
    }
    return pts;
  }

  map(fn) {
    return new Pattern(this.pt.map(ln=>ln.map(fn)), this);
  }

  lengthIn(dir) {
    if (dir == U || dir == D) return this.pt.length;
    else if (this.pt.length > 0) return this.pt[0].length;
    else return 0;
  }
  rotate(dir) { // moves the center to that direction, shifting the side
    if (dir == R) {
      var res = new Pattern(this.pt.map(c=>((h,...t)=>t.concat(h))(...c)), this);
      res.hp++;
      return res;
    }
    if (dir == L) {
      var res = new Pattern(this.pt.map(a=>a.slice(-1).concat(a.slice(0,-1))), this);
      res.hp++;
      return res;
    }
    if (dir == D) {
      var res = new Pattern(((h,...t)=>t.concat([h]))(...this.pt), this);
      res.vp++;
      return res;
    }
    throw "rotate unimplemented dir!";
  }

  center(dir) { // moves the center to that direction
    if (dir == R) {
      var res = new Pattern(this.pt.map(c=>((h,...t)=>t.concat(0))(...c)), this);
      res.hp++;
      return res;
    }
    if (dir == L) {
      var res = new Pattern(this.pt.map(a=>[0].concat(a.slice(0,-1))), this);
      res.hp++;
      return res;
    }
    if (dir == D) {
      var res = new Pattern(((h,...t)=>t.concat([new Array(h.length)]))(...this.pt), this);
      res.vp++;
      return res;
    }
    throw "center unimplemented dir!";
  }

  setSize(xs, ys) {
    var arr = [];
    for (let y = 0; y < ys; y++) {
      var ca = [];
      for (let x = 0; x < xs; x++) {
        ca.push(this.pt[y % this.pt.length][x % this.pt[0].length]);
      }
      arr.push(ca);
    }
    return new Pattern(arr, this);
  }

  static add(pattern, action, scorer, presetRot) {
    if (Array.isArray(pattern)) pattern = new Pattern(pattern);
    pattern = pattern.setSize(3,3);
    var cpt = pattern.setSize(3,3);
    var orig = cpt.pt;
    for (let i = 0; i < 4; i++) {
      cpt = cpt.rotateClockwise();
      if (!presetRot || presetRot == cpt.rot) {
        cpt.action = action;
        cpt.scorer = scorer;
        cpt.raw = orig;
        cpt.view = allVRots[cpt.rot];
        allPatterns.push(cpt);
      }
    }
  }


  static choose() {
    var maxScore = -1e307;
    var nextScore = -1e308;
    var maxPt;
    allPatterns.forEach((c) => {
      // null  = easy
      // 0     = bad queen
      // false = no match
      // >0    = score
      var falseN = 0;
      var corrects = c.raw.reduce((a,b)=>a.concat(b)).map((guess, index) => {
        var bo = basedOn(c.view[index], guess, true);
        var ant = guess.ant;
        if (ant && basedOn(c.view[index], {ant})) bo+= 1;
        if (bo === 0) return 0;
        if (bo === false) return false;
        if (bo && rcolOf(guess) > 0) return bo;
        var easy = rcolOf(guess)<=0;
        if (easy) {
          falseN++;
          return null;
        }
        return bo;
      });
      var corrstring = corrects.map((chr,i)=>chr>0? (colOf(c.raw[Math.floor(i/3)][i%3])==1? "W" : "#") : chr===null? "-" : " ").join("");
      function match(pt) {
        return new RegExp(pt.replace(/@/g, "[#-W]").replace(/C/g, "[#-]")).test(corrstring);
      }
      var score = corrects.reduce(ADD)*9/(9-falseN);
      if (match(".?(...)?##.##.*")) {
        if (match("(...)?@@@@@@.*|.?@@.@@.@@.?")) score+= foundEnemy? 5 : 3;
        else score+= foundEnemy? 3 : 1;
      } else if (!foundEnemy) score = Math.min(score/2, 5);
      if (c.scorer instanceof Function) score = c.scorer(score, c, corrects, falseN, match);
      if (DEBUG && score > -1) log(
        "scored", score,
        "corr", /*corrects.map(c =>
          (c===false?"F":c===null?"N":c===true?"T":c)
        )*/corrstring,
        "pt", c.raw.map(c=>c.ant? "A"+c.ant.type : c), c.hp, c.vp);
      if (score >= maxScore) {
        nextScore = maxScore;
        maxScore = score;
        c.corrstr = corrstring;
        maxPt = c;
      }
    });
    var flattened = maxPt.pt.reduce((a,b)=>a.concat(b));
    Pattern.hardcorr = flattened.map((guess, index) => rcolOf(guess)<2? 0 : basedOn(view[index], guess)).reduce(ADD);
    Pattern.corrstr = maxPt.corrstr;
    Pattern.corr = flattened.map((guess, index) => basedOn(view[index], guess)).reduce(ADD);
    Pattern.incorr = 9-Pattern.corr;
    Pattern.confidence = maxScore-nextScore;
    selectedRot = maxPt.rot;
    Pattern.action = maxPt.action;
    selectedPt = flattened;
    selectedHp = maxPt.hp;
    Pattern.raw = maxPt.raw;
    Pattern.view = maxPt.view;
    selectedVp = maxPt.vp;
    Pattern.score = maxScore;
    if (DEBUG) log("score", maxScore, "confidence", Pattern.confidence, "corr", Pattern.corr, "hardc", Pattern.hardcorr, "pt", maxPt.pt);//, "fn", maxPt.action+""
  }
}
var allPatterns = [];
function clear() {
  allPatterns = [];
}
function adds(raw, action, scorer, presetRot) { // must get a 3x3 arr
  var pt = raw;
  var hp = raw.hp;
  var vp = raw.vp;
  for (let rot = 0; rot < 4; rot++) {
    let view = allVRots[rot];
    allPatterns.push({pt, action, scorer, rot, hp, vp, view, raw});
    if (rot!=4) pt = rotateCW3(pt);
  }
}
function refPt(...args) {
  clear();
  if (Array.isArray(args[0])) {
    if (args[0].length != 3) args[0] = args[0].slice(0,3);
    if (args[0][0].length != 3) args[0] = args[0].map(c=>c.slice(0,3));
    adds(...args);
  }
  else Pattern.add(...args);
  Pattern.choose();
}

/*
is the 2nd param a subset of the 1st param.
guess can be a number (color), or an object ({color:..,ant:..,..})
guess.ant can be "worker", "queen", "enemy", "enemyworker", "enemyqueen" with obvious meanings. Note that "friend" ≠ me
guess.ant.type can be an array, ORing

true - correct!
false - not correct
0 - notqueen doesn't match (aka very bad)

negativesEqual makes this always return true for negative colors, otherwise it treats negatives as regular colors
*/
function basedOn(real, guess, negativesEqual) {
  if (Array.isArray(real)) return real.some(c=>basedOn(c, guess, negativesEqual));
  if (Number.isInteger(guess)) guess = {color:guess};
  if (guess.notqueen && real.ant && real.ant.friend && real.ant.type==5) return 0;
  if (guess.not) {
    var bo = basedOn(real, guess.not, negativesEqual);
    if (bo) return 0;
  } 
  if (guess.color && Math.abs(guess.color) != real.color && !(negativesEqual && guess.color<0)) return false; // 0 handles itself
  if (guess.obstacle !== undefined) {
    if (guess.obstacle && !real.ant && !(food && real.food && !isQueen)) return false;
    if (!guess.obstacle && (real.ant || (food && real.food && !isQueen))) return false;
  }
  if (guess.badobstacle !== undefined) {
    if (guess.badobstacle && !(real.ant && !real.ant.friend) && !(food && real.food && !isQueen)) return false;
    if (!guess.badobstacle && ((real.ant && !real.ant.friend) || (food && real.food && !isQueen))) return false;
  }
  if (guess.ant) {
    if (!real.ant) return false;
    if (guess.ant == "worker"      &&!( real.ant.friend && real.ant.type!=5)) return false;
    if (guess.ant == "queen"       &&!( real.ant.friend && real.ant.type==5)) return false;
    if (guess.ant == "enemyqueen"  &&!(!real.ant.friend && real.ant.type==5)) return false;
    if (guess.ant == "enemyworker" &&!(!real.ant.friend && real.ant.type!=5)) return false;
    if (guess.ant == "friend" && (!real.ant.friend || real.ant.me)) return false;
    if (guess.ant == "enemy"  &&  real.ant.friend) return false;
    if (Number.isInteger(guess.ant) && real.ant.type != guess.ant) return false;
    if (guess.ant.friend !== undefined && guess.ant.friend !== real.ant.friend) return false;
    if (guess.ant.type !== undefined && !(Array.isArray(guess.ant.type)? guess.ant.type.some(c=>c == real.ant.type) : guess.ant.type == real.ant.type)) return false;
    if (guess.ant.food !== undefined && guess.ant.food !== real.ant.food) return false;
  }
  if (guess.food !== undefined && guess.food !== real.food) return false;
  // log("matched");
  return true;
}

function result (action, force) {
  if (!force) if (toReturn !== undefined) return 0;
  var color = action.color;
  var type = action.type;
  var cell = action.cell;
  if (type < 1 || type > 4) return false;
  if (!(cell >= 0 && cell <= 8)) return false;
  if (color < 1 || color > 8) return false;
  if (!color && ((view[cell].ant && cell != 4) || (isQueen? (view[cell].food && type) : (food && view[cell].food)))) return false; // can't walk onto ant, can't spawn on food, can't move to food with food
  if (!isQueen && type) return false;
  if (!isQueen && !color && food && view[cell].food) return false;
  if (isQueen && !food && type) return false;
  if (type && cell==C) return false;
  if (color && type) return false;

  toReturn = action;
  return true;
}

const WH = 1; // white   
const C1 = 6; // green   HW
const C2 = 5; // red     
const C3 = 8; // black   
const C4 = 2; // yellow  HW
const C5 = 4; // cyan    HW
const C6 = 7; // blue    HW
const C7 = 3; // purple  HW
// C1=GR,C2=BL,C4=YL,C5=DK
const ENEMY = {ant:"enemy"};
const foundEnemy = found(ENEMY);
  //-----------------------------------------------------------------------------------------------------------------------------------------------------\\
 //----------------------------------------------------------------------- MAIN CODE ---------------------------------------------------------------------\\
//---------------------------------------------------------------------------------------------------------------------------------------------------------\\

function log(...args) {
  if (!DEBUG) return;
  toLogRaw.push(args);
  // for (let i of args) {
  //   if (i === undefined) i = "undefined";
  //   var res = "";
  //   if (typeof i === 'string') res = i;
  //   else res = JSON.stringify(i);
  //   toLog+= res + " ";
  // }
  // toLog+= "\n";
}
if (DEBUG) {
  var toLog = "";
  var logMyLogs = false;
  var toLogRaw = [];
  log(type, view.map(c=>c.ant? "A"+c.ant.type : c.color));
}

const Ut = 1;
const Dt = 2;
const Ht = 4;
const Uo = {ant:{type:Ut,friend:true}};
const Do = {ant:{type:Dt,friend:true}};
const Ho = {ant:{type:Ht,friend:true}};
const Mo = {ant:{type:[Ut,Dt],friend:true}};
const Fo = {food:1};
const Qo = {ant:{type:5,friend:true}};
const EQo = {ant:{type:5,friend:false}};
const FRIEND = {ant:"friend"};
const OBSTACLE = {obstacle:true};
const FREE = {obstacle:false};
const BADOBSTACLE = {badobstacle:true};
const STARTINGFOOD = 6;
const LESSENFOOD = 160;
const ENDINGFOOD = 160;
const isMiner = type==Ut || type==Dt;
var friendCount = count(FRIEND);
if (isMiner) {
  var Mu = type==Ut? u : d;
  var Md = type==Ut? d : u;
  var Mur = Mu+1;
  var Mul = Mu-1;
  var Mdr = Md+1;
  var Mdl = Md-1;
}

const foodExt = [C3, C7, WH];
const rawRail = [
  [WH,-1,C7,-1], // 43 03 13 23
  [C6,WH,WH,-1], // 42 02 12 22

  [C4,C1,C2,C2],
  [C2,C4,C5,C5],
  [C3,C5,C1,C3],


//[C3,C1,C4,C4],
//[C4,C4,C5,C2],
//[C5,C2,C1,C3],

//[C3,C1,C2,C5],
//[C3,C3,C1,C5],
//[C2,C4,C5,C2],


//[C2,C1,C3,C5], // 41 01 11 21
//[C1,C5,C5,C4], // 40 00 10 20
//[C5,C4,C2,C3], // 41 01 11 21
  [C6,WH,WH,-1], // 42 02 12 22
  [WH,-1,C7,-1]  // 43 03 13 23
]
.map((ln,row)=>(row<2||row>4)? ln.map(c=>({not:{ant:{friend:true, type:[Ht, 5]}},color:c})) : ln); // queen can't be in the top & bottom 2 rows

function section(ln, action, scorer) {
  if (ln > 0) section(-ln, action, scorer);
  var sct = rawRail.slice(ln+2, ln+5);
  var parts;
  if (Math.abs(ln) != 2) {
    parts = [];
    for (let i = 0; i < 4; i++) {
      var cpt = sct.map(([a,b,c,d])=>[a,b,c]);
      cpt.hp = i;
      cpt.vp = ln;
      parts.push(cpt);
      if (i!=4) sct = sct.map(([a,b,c,d])=>[b,c,d,a]);
    }
  } else {
    var o = sct.map(c=>c.slice(0,3));
    o.vp = ln;
    o.hp = 0;
    parts = [o];
  }
  parts.map(c=>adds(c, action, scorer));
}

function sabotage(where) {
  if (on(where, 1)) repair([where], true);
  else color(where, 1);
}


section(0, ()=>{
  if (isMiner) {
    if (on([r,ur,dr], Fo)  ||  on([u,d], Fo) && on([l,ul,dl], Mo)) { // FAKE RAIL
      color(on([dr,d],Fo)? [dr,u,ul] : [dr,d,dl], C7);
    }
    else if ([l,ul,dl].every(c=>on(c,{ant:{}})) && !random4()) move([r,u,d,ur,dr]); // peer pressure
    /* AV */ else if (found(EQo)) sabotage(find(EQo));
    else if (repair()) {
      if (on(r,Mo) && (random4() > 1 || random4() && friendCount > 3)) move([l,Mul,Mu,Mdl,Md]);
      if (on([Mu,Mur], ENEMY)) move([R, D, DR]); // move somewhere away from enemy
      else if (on(r, Qo) && on([l,ul,dl], {ant:{type:[Ut,Dt],food:1,friend:true}})) move([Mu, Mul, l, Mdl]); // make place for miners with food; Possibly stuck
      else if (on(r, Qo)) move([Mu, Mul]); // don't do stupid things around queen
      else if (on(l, Qo) && !foundEnemy) move([Mul, Mdl, Mu, Md]); //.. I've done stupid things around queen
      else if (on([Mu,Mur], OBSTACLE)) { // up is blocked :/
        if (random4()) move(on(Mu, FRIEND)? [Mur, r] : r);
        else move([r, Mul, Md, Mul, l, Mdl]);
      }
      else move([Mu, r, Mur, Md, Mdr]); // move along
    }
  } else if (isQueen) {
    var HM = Pattern.view.map(c=>+basedOn(c, Ho));
    var helperRows = [HM.slice(0,3),HM.slice(6,9)].map(c=>c.lastIndexOf(1)).map((c,i) => (c==0 && on(i==0? u : d, OBSTACLE)) ? -1 : c);
    var minH = Math.min(helperRows[0],helperRows[1]);
    var maxH = Math.max(helperRows[0],helperRows[1]);
    if (on(r, FRIEND) && [ur,dr].every(c=>on(c, ENEMY))) move([l,ul,dl]);
    if (found(EQo)) { // vampire?
      move(random4()%2? [ur,dr] : [dr,ur]);
      var eQueenRel = (findRel(EQo)-rp)%3;
      if (eQueenRel == 0) move(r,ur,dr);
      spawn(Mu, [u,d,r,ur,dr]);
    }
    if (foundEnemy) // spawn helpers against enemies
      if (food && minH == -1 && count(Ho) < 2) {
        if (helperRows[0] == -1) spawn([u,ur,ul],Ht);
        else                     spawn([d,dr,dl],Ht);
      }
    if ([r,ur,dr].every(c=>on(c, ENEMY))) move([ul,dl,l,u,d]); // OH GOD NO WHY
    if ((minH == -1 || maxH == 2) && on(r, [ENEMY,Ho]) && Pattern.incorr < 2 && count({ant:{}})-1 != count(Ho))
      move(on(ur, ENEMY)? [d,u,dr,ur,ul,dl,l] : [u,d,ur,dr,ul,dl,l]); // initialize transporting around enemy
    if ((!random4() && on(l, OBSTACLE) && on([ul, dl], OBSTACLE)) && Pattern.corr >= 7) move(r); // move forward sometimes if left is 2/3s full
    else if ([r,ur,dr].every(c=>c.ant && !c.ant.friend)) move([l,ul,dl]);
    else if (food && minH > 0 && (
        count(Mo) == 0 && selectedHp != 1 && Pattern.corr != 9 && food < LESSENFOOD
      ||
        //Pattern.corr === 9 && [u,d].every(c=>on(c,Ho)) && selectedHp === 1 && food >= LESSENFOOD && food < ENDINGFOOD && random4() < 2
        Pattern.corr === 9 && selectedHp == 0 && count(Mo) === 0 && food >= LESSENFOOD && food < ENDINGFOOD && random4() < 2
      )) { // spawn miners
      if (random4()%2) spawn([u,ul], Ut);
      else             spawn([d,dl], Dt);
    } else if (repair()) {
      if (food && minH == -1 && count(Ho) < 2) { // spawn helpers
        if (helperRows[0] == -1) spawn([u,ur,ul],Ht);
        else                     spawn([d,dr,dl],Ht);
      }
      else if (selectedHp != 1  ||  selectedHp==1 && /*(*/(maxH==2 || minH == -1 && helperRows.includes(2)) && (!random4() || food < LESSENFOOD || found(Mo))  ||  foundEnemy) move(r); // move forwards
      else if (on(ul, Do) && on(dl,Uo) && on(l, {ant:{}})) move(r); // miners are in wrong places
    }
  } else { // helper
    var repaired = repair();
    var queenRel = (findRel(Qo)-rp)%3;
    var dir = queenRel==0? 0 : 1;
    if (repair()) {
      if (on(r, EQo) && [u,d,ur,dr].map(c=>on(c, ENEMY)? 1 : 0).reduce(ADD) >= 3) move(c); // protect the queen from the evils ahead
      else if (on(r, Qo)) move([u,d,ur,dr,ul,dl,l]);
      else move((on([d,dr,dl], Ho)? [u+dir,d+dir] : [d+dir,u+dir]).concat([u+(1-dir), d+(1-dir)]), !random4());
    }
  }
})

section(1, ()=>{
  const A = selectedVp > 0? d : u; // away
  const I = selectedVp > 0? u : d; // in
  const AR = A+1;
  const AL = A-1;
  const IR = I+1;
  const IL = I-1;

  if (isMiner) {
    var queenRel = (findRel(Qo)-rp)%3;
    if (on([r,IR], Fo)) color([r,IR, I], C7); // FAKE RAIL
    else if ([l,IL].every(c=>on(c,{ant:{}})) && !random4()) move([r,IR]); // peer pressure
    /* AV */ else if (found(EQo)) sabotage(find(EQo));
    else if ((found(EQo) && random4() && Pattern.dist <= 1 && on(find(EQo), 1)) || repair()) {
      if (on(I, Qo)) move(l); // what am I doing here?
      else if (A == Mu) { // my dir!
        if (!food && selectedHp == 0 && (on(r,Ho) && on(IR, Qo)  ||  count(Mo) >= 6)) move(A); // move out!
        else if (on(IR,Qo) && on([l,IL], {ant:{type:[Mu,Md],friend:true,food:1}})) move(C); // waiting in line :D
        else if (on(IR,Qo)) move(C); // waiting in line :D
        else if (random4()) move([r, I, IR]);
        else move([r, I, IR, l, IL]);
      } else { // not my dir
        // TODO fix \\ if (selectedHp == 0 && count(Mo) >= 6 && food) move(A); // fake rail escape
        if (random4()) {
          move([I, IR, IL, l]);
        } else {
          move([r, I, IR, IL, l]);
        }
      }
    }
  } else if (isQueen) {
    if (found(EQo)) { // vampire?
      var eQueenRel = (findRel(EQo)-rp)%3;
      if (eQueenRel==0) move(r, IR);
      spawn(Mu, [r,IR]);
      spawn(Md, I);
    }
    /* AV */ if (food > 70 && (
      [IR,IL,I,l,r].every(c=>on(c,ENEMY)) // completely encased
      || on(IR, EQo) && [r,I].every(c=>on(c, ENEMY)) // getting leeched
      || on(I, EQo) && [r,l,IL,IR].map(c=>get(c)).map(c=>c.ant? (c.ant.friend? 1 : -1) : 0).reduce(ADD) < 0 // leeched
    )) move([A,AR,AL]); // BAD NEWS COMPLETELY DEAD
    if (!random4() || found(EQo) || repair())
      move(random4()? [IR,r,I,l] : [IR,r,I]);
  } else { // helper
    var queenRel = (findRel(Qo)-rp)%3;
    /* AV */ if (on(r,Qo) && on([I,IR], EQo)) move([IR,I,l,IL]);
    if (on(l, Qo)) { if (!random4() || repair()) move(r) } // queen's transporting
    if (on(I, Qo) && on(IR, {ant:"enemyworker"})) { if (!random4() || repair()) move(r) } // queen needs to transport
    // what was this? if ([l,IL,I].every(c=>on(c,OBSTACLE)) && (count(ENEMY) > 1 || find(EQo)) && !random4()) move([r,ur]);
    if ((selectedVp < 0? /...[#-W]{6}/ : /[#-W]{6}.../).test(Pattern.corrstr) && queenRel == 2 && count(Ho) == 1) move(r); // move forward without repairing
    if (!random4() && queenRel == 1 && selectedHp == 1 && on(AL, {ant:{}})) move(r); // something is out; don't repair
    else if (repair([r,l,A,AR,AL])) {
      if (on(r, ENEMY) && on(I, Qo) && [l,IL].every(c=>on(c,FRIEND))) move(IR); // protect from vampire
      if (on(r, ENEMY) && on(IL, Qo) && [l,IR].every(c=>on(c,FRIEND))) move(I);
      if (on(r, ENEMY) && !get(r).ant.food && on(I, Qo)) move(IR);
      if (queenRel == 1 && selectedHp == 1 && on(AL, {ant:{}})) move(r); // something is out
      else if (on([l,r], Ho)) { // move to the other side
        if (found(Qo)) move([I]); // TODO integrate ,IL,IR
        else move([l,r]);
      }
      else if (queenRel == 2) move(r); // move forward
    }
  }
}, (pscore, pt, corrects, falseN, match) => {
  if (match(".?(...)?@@.@@.*") && !foundEnemy) {
    if (!match(pt.vp>0? ".?@@.@@.*" : ".?...@@.@@.*")) pscore/=2;
  }
  return pscore;
})

if (isMiner) {
  section(2, () => {
    const A = selectedVp > 0? d : u; // away
    const I = selectedVp > 0? u : d; // in
    if (on(A,OBSTACLE)) move(I);
    else if (repair()) move(food? I: Mu);
  }, (pscore, pt, corrects, falseN, match) => match(pt.vp>0? "@@@.@...." : "....@.@@@")? match("@@@@@@@@@")? 100 : ((pt.vp>0) == (type==Dt)? 13 : 10) : 0);
  if (type==Dt) foodExt.reverse();
  if (!found(Ho) && !found(Qo)) {
    var lns = [rawRail[0], rawRail[1]].map(c=>c.slice(0,3));
    [[lns[0],lns[1],lns[0]], [lns[1],lns[0],lns[1]]].map(c=>{
      adds(c, () => {
        var onL;
        if (!food && ((onL = on(l, Fo)) || on(r, Fo))) {
          var foodpt = Pattern.raw.map((ln, i) => [ln[0], foodExt[i], ln[2]]);
          refPt(foodpt,undefined,undefined,selectedRot);
          if (repair()) move(onL? l : r);
        }
        else if (repair([l,r,ul,ur,dl,dr], on([Mul, Mur, Mdl, Mdr, l, r], {ant:{friend:true,type:[Ut,Dt],food:1}}) ||  on([Mu, Md], Mo))) {
          move(food? [Md, Mu] : [Mu, Md]);
        }
      }, (pscore, pt, corrects, falseN, match) => {
        var score = 0;
        var dMatch = match("...@.@@@@");
        var uMatch = match("@.@@.@...");
             if ((type==Ut ^ food) && dMatch) score = 15;
        else if ((type==Dt ^ food) && uMatch) score = 15;
        else if (uMatch || dMatch) score = 6;
        if ([0,2,3,5,6,8].some(c=>basedOn(pt.view[c], FRIEND) && !pt.view[c].ant.food)) score = 0;
        return score;
      });
      if (food) {
        var extp = c.map((ln, i) => [ln[0], foodExt[i], ln[2]]);
        [extp.map(([a,b,c])=>[0,a,b]), extp.map(([a,b,c])=>[b,c,0])].forEach((pt,i) => adds(pt, () => {
          move(i? l : r);
        }, (pscore, pt, corrects, falseN, match) => match("@@@@@@@@@")? 100 : 0));
      }
    });
  }
}

Pattern.choose();
var confident = ((Pattern.confidence >= 1 && (Pattern.score > 4 || Pattern.corr >= 4)) || (Pattern.score >= 9 && Pattern.confidence > 0.05)); // && (selectedHp !=  || !found(Qo));
var failAction = () => {
  if (foundEnemy) {
    log(view);
    log("dead around enemy :/");
    logMyLogs = true;
  }
  if (isQueen) {
    if (found(EQo)) {
      move([8-(find(EQo)-rp) + rp]);
      move(random4()%2? U : UR);
    }
    if (foundEnemy) move(random4()%2? U : UR);
  } else {
    // if (!found(Qo) && found(Fo)) move(find(Fo));
    var enemyPlace = find(ENEMY);
    if (enemyPlace !== -1) color(enemyPlace, get(enemyPlace).color==1? C3 : WH);
  }
}
if (!confident) Pattern.action = failAction;

if (isMiner) {
  if ((Pattern.hardcorr >= 4 || Pattern.score > 5) && confident) Pattern.action();
  else {
    failAction();
  }
} else if (isQueen) {
  if ((Pattern.hardcorr >= 6 || food > STARTINGFOOD+2 || friendCount>1 || found(Mo) || Pattern.score > 6 || (false)) && confident) Pattern.action();
  else if (food >= STARTINGFOOD && friendCount == 1) {
    clear();
    Pattern.add([[1,{ant:Ho.ant,color:1},1],
                 [1,1,1],
                 [1,1,1]], ()=>spawn([ur,ul],Ht));
    Pattern.add([[1,1,{ant:Ho.ant,color:1}],
                 [1,1,1],
                 [1,1,1]], ()=>spawn([ur,u],Ht));
    Pattern.choose();
    if (repair()) Pattern.action();
  } else if (food == 0 && friendCount == 0) { // diagonal search
    if (found(Fo)) {
      move(find(Fo));
    } else {
      clear();
      Pattern.add([[WH,WH,WH],
                   [WH,C1,WH],
                   [C1,WH,WH]], ()=>move(ur));
      Pattern.add([[WH,WH,WH],
                   [WH,WH,WH],
                   [C1,WH,WH]], ()=>color(C, C1));
      Pattern.add([[WH,WH,WH],[WH,WH,WH],[WH,WH,WH]], ()=>color(DL, C1));
      Pattern.choose();
      if (Pattern.corr == 9) Pattern.action();
      else move(random4()? [DL,UL,DR,UL] : [D,L,U,R]);
    }
  } else if (food == 1 && friendCount == 0) spawn([U,L,D,R,UL,DL,UR,DR], Ht);
  else if (friendCount == 1) lightSpeed();
  else if (friendCount > 0) {
    var pt = new Pattern(rawRail).select(0,2,4,3).rotate(L).rotate(L).pt;
    pt[1][2] = {color:pt[1][2], ant:{type:Ht, friend:true}};
    refPt(pt);
    repair([c,u,d,ur,dr]);
  } // TODO wtf to do after this
  else Pattern.action(); // eh fuck it
} else if (type == Ht) {
  if (confident && (Pattern.score >= 4 || Pattern.hardcorr >= 5 || friendCount>1)) Pattern.action();
  else if (found(Qo)) lightSpeed();
  else if (Pattern.hardcorr >= 3 && confident) repair();
}

function lightSpeed() {
  var other = find(isQueen? Ho : Qo);
  var orth = other%2;
  if (isQueen || (view[other].ant.food < STARTINGFOOD && count(Ho) == 1)) { // LS
    if (orth && found(Fo)) { // grab easy food
      var fp = find(Fo);
      if (sees(other, fp)) move(fp);
      else {
        refPt([[0,FRIEND,0],
                     [0,0,0],
                     [0,0,0]]);
        move(l);
      }
    }
    clear();
    // Pattern.when(U,find(FRIEND), ()=>isQueen? move(ul) : move(ur)); when I'm not lazy imma make this a replacement of the below
    Pattern.add([[0,FRIEND,0],
                 [0,0,0],
                 [0,0,0]], ()=>isQueen? move(ul) : move(ur));
    Pattern.add([[0,0,FRIEND],
                 [0,0,0],
                 [0,0,0]], ()=>move(u));
    Pattern.choose();
    Pattern.action();
  }
}

if (DEBUG) log("END", type, view.map(c=>c.ant? "A"+c.ant.type : c.color));
if (DEBUG && logMyLogs) {
  //for (let i = 0; i < toLog.length; i+=800)
  //  console.log(toLog.substring(i,i+800));
  for (let i of toLogRaw) console.log(...i);
}
if (toReturn) return toReturn;
else return {cell:4};

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


Спочатку цього разу знову на лідері лідерів ...
trichoplax

14

Планер

Планер в діїПланер повертає ліворучПланер повертає праворуч

//console.log(JSON.stringify(view))
var TRAIL = 6;
var SPAWN = 3;
var IDLE = 4;
var FOOD_THRESHOLD = 150;
var SPAWN_MIN = 3;
var HIGHWAY_COLORS = [7,6,4,2,3];
var HIGHWAY_THRESHOLD = 70;
var ret = {cell:4};
if(isOnHighway()) {
    var cont = true;
    //== Make best guess to if in a glider formation ==//
    if(view[4].ant.type == 5) {
        if((findWorker(1) >= 0 && findWorker(4) >= 0) || view[4].ant.food < HIGHWAY_THRESHOLD) {
            cont = false;
        }
    }
    else if(view[4].ant.type == 4) {
        if(findWorker(1) >= 0 && findWorker(5) >= 0) {
            cont = false;
        }
    }
    else if(view[4].ant.type == 3) {
        if(findWorker(2) >= 0 && findWorker(5) >= 0) {
            cont = false;
        }
    }
    else if(view[4].ant.type == 2) {
        if(findWorker(5) < 0) {
            var pos3 = findWorker(3);
            if(pos3 >= 0 && view[pos3].ant.food == 0) {
                cont = false;
            }
        }
        else if(findWorker(3) >= 0 || (findWorker(1) >= 0 && view[findWorker(5)].color == SPAWN))
            cont = false;
    }
    else if(view[4].ant.type == 1) {
        if(findWorker(5) < 0) {
            var pos4 = findWorker(4);
            if(pos4 >= 0 && view[pos4].ant.food == 0) {
                cont = false;
            }
        }
        else if(!isHighwayCenter())
            cont = false;
    }
    if(findWorker(5) >= 0) {
        for(var i=0;i<9;i++) {
            if(view[i].ant != null && !view[i].ant.friend && view[i].ant.type == 5) {
                if(view[i].ant.food > 10 || view[i].ant.food == 0)
                    cont = true;
                else
                    cont = false;
            }
        }
    }
    //== End guesswork ==//
    if(cont) {
        ret = highwayRobbery();
        if(view[4].ant.type == 1) {
            //try to repair
            var curIndex = HIGHWAY_COLORS.indexOf(view[4].color);
            var prvCol = HIGHWAY_COLORS[(curIndex+1)%HIGHWAY_COLORS.length];
            var nxtCol1 = HIGHWAY_COLORS[(curIndex+HIGHWAY_COLORS.length-1)%HIGHWAY_COLORS.length];
            var nxtCol2 = HIGHWAY_COLORS[(curIndex+HIGHWAY_COLORS.length-2)%HIGHWAY_COLORS.length];
            var nxtCol3 = HIGHWAY_COLORS[(curIndex+HIGHWAY_COLORS.length-3)%HIGHWAY_COLORS.length];
            var prevAt = -1;
            for(var i=0;i<9;i++) {
                if(i%2 == 1 && view[i].color == prvCol && view[deRotate(i,1)].color == nxtCol1 && view[deRotate(i,-1)].color == nxtCol1) prevAt = i;
            }
            if(prevAt >= 0) {
                // yep, brute force it. Because I'm lazy.
                var goNxt = 8-prevAt;
                if(view[deRotate(goNxt,1)].color == nxtCol3 && view[deRotate(goNxt,-1)].color == prvCol) ret = {cell:goNxt};
                else if(view[deRotate(goNxt,1)].color == prvCol && view[deRotate(goNxt,-1)].color == nxtCol3) ret = {cell:goNxt};
                else if(view[goNxt].color != nxtCol1) ret = {cell:goNxt,color:nxtCol1};
                else if(view[deRotate(goNxt,2)].color != nxtCol2) ret = {cell:deRotate(goNxt,2),color:nxtCol2};
                else if(view[deRotate(goNxt,-2)].color != nxtCol2) ret = {cell:deRotate(goNxt,-2),color:nxtCol2};
                else if(view[deRotate(goNxt,1)].color != nxtCol3) ret = {cell:deRotate(goNxt,1),color:nxtCol3};
                else if(view[deRotate(goNxt,-1)].color != nxtCol3) ret = {cell:deRotate(goNxt,-1),color:nxtCol3};
                else ret = {cell:goNxt};
                ret = sanityCheck(ret);
                return ret;
            }
        }
        if(view[4].ant.type == 5 && isHighwayCenter()) {
            if(ret.cell >= 0) {
                ret = {cell:8-ret.cell};
                if(view[4].color == SPAWN && (view[4].ant.food > 90 || view[4].ant.food % 7 == 0) && getHighestWorker() == 0 && (view[4].ant.food < 140 || view[4].ant.food % 9 == 0) && view[0].color == 2 && view[4].ant.food > 50 && view[4].ant.food < 200) {
            //fine
                    if(view[4].ant.food % 10 < 5)
                        ret = {cell:deRotate(ret.cell,3),type:3};
                }
                if(view[ret.cell].ant != null && !view[ret.cell].ant.friend && view[ret.cell].ant.food == 0 && view[4].ant.food > 0) {
                    if(view[deRotate(ret.cell,1)].ant == null)
                        ret = {cell:deRotate(ret.cell,1),type:3};
                    if(view[deRotate(ret.cell,-1)].ant == null)
                        ret = {cell:deRotate(ret.cell,1),type:3};
                }
            }
            for(var i=0;i<9;i++) {
                if(view[i].ant != null && !view[i].ant.friend && view[i].ant.type == 5) {
                    ret = {cell:8-i};
                }
            }
        }
        if(ret.cell >= 0) {
            for(var i=0;i<9;i++) {
                if(view[i].ant != null && !view[i].ant.friend && view[i].ant.type == 5) {
                    var rr = basicHighwayMove();
                    if(rr.cell >= 0 && view[4].ant.type != 5)
                        ret = {cell:deRotate(rr.cell,-2)};
                }
            }
            if(view[ret.cell].ant != null) {
                var n = HIGHWAY_COLORS.indexOf(view[4].color) + 1;
                var nextMove = HIGHWAY_COLORS[n % HIGHWAY_COLORS.length];
                for(var i=0;i<9;i++) {
                    if(view[i].color == nextMove) {
                        if(view[i].ant == null) {
                            ret = {cell:i};
                            break;
                        }
                    }
                }
                if(view[4].ant.type == 5) ret = {cell:8-ret.cell};
            }
        }
        if(view[4].ant.type == 5) {
            var foodedEnemy = false;
            for(var i=0;i<9;i++) {
                if(getNumWorkers(3) >= 2) break;
                if(i != 4 && view[i].ant != null && view[i].ant.type == 5 && view[i].ant.food > 25) {
                    if(view[deRotate(i,1)].ant == null) {
                        ret = {cell:deRotate(i,1),type:3};
                    }
                    else if(view[deRotate(i,-1)].ant == null) {
                        ret = {cell:deRotate(i,-1),type:3};
                    }
                    else if(i%2 == 1 && view[deRotate(i,2)].ant == null) {
                        ret = {cell:deRotate(i,2),type:3};
                    }
                    else if(i%2 == 1 && view[deRotate(i,-2)].ant == null) {
                        ret = {cell:deRotate(i,-2),type:3};
                    }
                }
                if(view[i].ant != null && !view[i].ant.friend && view[i].ant.type == 3 && view[8-i].ant == null) {
                    if(i == ret.cell) {
                        ret = {cell:deRotate(i,1)}
                    }
                    else {
                        return {cell:8-i};
                    }
                }
                if(view[i].ant != null && !view[i].ant.friend && view[i].ant.food == 0) {
                    foodedEnemy = true;
                }
            }
        }
        var numAnts = 0;
        for(var i=0;i<9;i++) {
            if(view[i].ant != null)
                numAnts++;
        }
        if(numAnts > 2 && sanityCheck(ret).cell == 4) {
            ret = {cell:findOpenSpace(0,1)};
        }
        if(view[4].ant.type == 3) {
            if(getNumWorkers(5) > 0) {
                for(var i=0;i<9;i++) {
                    if(view[i].ant != null && !view[i].ant.friend && view[i].ant.type == 5) {
                        ret = {cell:4};
                    }
                }
            }
        }
        if(view[4].ant.type == 4 && getNumWorkers(1) && isHighwayCenter()) {
            var workerPos = findWorker(1);
            for(var i=0;i<9;i++) {
                if(!areAdjacent(i,workerPos)) ret = {cell:i};
            }
        }
        if(ret.cell == -1) {
            if(isHighwayCenter()) {
                for(var i=0;i<9;i++) {
                    var p1 = deRotate(i,3);
                    var p2 = deRotate(i,-3);
                    if(view[i].color == view[p1].color && view[i].color == view[p2].color) {
                        ret = {cell:8-i};
                    }
                }
                if(view[4].ant.type == 1 || view[4].ant.type == 5) {
                    ret = {cell:8-ret.cell};
                }
            }
        }
        if(ret.cell >= 0)
            return sanityCheck(ret);
    }
}

switch(view[4].ant.type) {
    case 5:
        ret = doQueen();
        break;
    case 1:
    case 2:
        ret = doSweep();
        break;
    case 3:
    case 4:
        ret = doGuide();
        break;
    default:
        break;
}
//basic sanity check
ret = sanityCheck(ret);
return ret;

function sanityCheck(ret) {
    if(!ret || ret.cell < 0 || ret.cell > 8) {
        return {cell:4};
    }
    if(ret.color) {
        return ret;
    }
    if((ret.cell != 4 && view[ret.cell].ant != null) || (view[ret.cell].food > 0 && (view[4].ant.food > 0 && view[4].ant.type < 5))) {
        return {cell:4};
    }
    if(ret.type && (view[ret.cell].ant != null || view[ret.cell].food > 0 || view[4].ant.food == 0 || view[4].ant.type < 5)) {
        return {cell:4};
    }
    return ret;
}

function doQueen() {
    if((view[4].ant.food == SPAWN_MIN || (view[4].ant.food >= SPAWN_MIN && view[4].ant.food < FOOD_THRESHOLD && (view[4].ant.food % 3 == 1 || isOnHighway()))) && getHighestWorker() <= 1 ) {
        //prep for first ant
        var s0 = view[0].ant;
        var s1 = view[1].ant;
        var s2 = view[2].ant;
        var s3 = view[3].ant;
        var s5 = view[5].ant;
        var s6 = view[6].ant;
        var s7 = view[7].ant;
        var s8 = view[8].ant;
        var nullCount = 0 + (s0 == null?1:0) + (s1 == null?1:0) + (s2 == null?1:0) + (s3 == null?1:0) + (s5 == null?1:0) + (s6 == null?1:0) + (s7 == null?1:0) + (s8 == null?1:0);
        var nullCount2 = 0 + (s0 == null || s0.friend?1:0) + (s1 == null || s1.friend?1:0) + (s2 == null || s2.friend?1:0) + (s3 == null || s3.friend?1:0) + (s5 == null || s5.friend?1:0) + (s6 == null || s6.friend?1:0) + (s7 == null || s7.friend?1:0) + (s8 == null || s8.friend?1:0);
        if(nullCount >= 7 && nullCount2 >= 8 && view[1].food == 0 && view[3].food == 0 && view[5].food == 0 && view[7].food == 0) {
            var high = getHighestWorker();
            if (high <= 1 && view[4].color != SPAWN && !isOnHighway()) {
                // 50% chance of delaying the respawn by 1 additional move away from where we exploded
                // reduces the chance of a second, immediate explosion
                var pos1 = findWorker(1);
                if(findFirstTrail() < 2 && view[4].ant.food > SPAWN_MIN+1 && pos1 < 0) return foreverAlone();
                if(pos1 >= 0) {
                    var space = deRotate(pos1,2);
                    if(view[space].ant != null) return {cell:findOpenSpace(0,1)};
                }
                return {cell:4,color:SPAWN};
            }
            //spawn first ant
            else if(view[4].color == SPAWN) {
                var pos1 = findWorker(1);
                if(pos1 < 0)  {
                    pos1 = findFirstTrail();
                    if(pos1 % 2 == 0) pos1 = deRotate(pos1,1);
                    else pos1 = deRotate(pos1,4);
                }
                var space = findOpenSpace(pos1,2);
                var high = getHighestWorker();
                if(space < 0) return {cell:4,color:TRAIL}
                if(high == 0) { //no workers
                    return {cell:space,type:1};
                }
                else if(high < 4) { //1 worker of type:high
                    return {cell:space,type:high+1};
                }
                else { //1 worker of type 4
                    //we have all workers, skip!
                }
            }
        }
        else {
            return foreverAlone();
        }
    }
    else if(view[4].ant.food == 1 && getHighestWorker() == 0 ) {
        var space = findOpenSpace(1,2);
        return {cell:space,type:1};
    }
    else if(view[4].ant.food >= 1 && getHighestWorker() < 4 && findWorker(1) >= 0) {
        //spawn remaining ants
        if(view[4].color == SPAWN && !isHighwayCenter()) {
            var pos1 = findWorker(getHighestWorker());
            var space = deRotate(pos1,2);
            var high = getHighestWorker();
            if(space < 0 || view[space].ant != null) return {cell:findOpenSpace(0,1)};
            if(high == 0) { //no workers
                return {cell:space,type:1};
            }
            else if(high < 4) { //1 worker of type:high
                return {cell:space,type:high+1};
            }
            else { //1 worker of type 4
                //we have all workers, skip!
            }
        }
    }
    if(view[4].color == SPAWN && getNumWorkers(3) == 1 && getNumWorkers(4) == 1) {
        var one = getNumWorkers(1);
        var two = getNumWorkers(2);
        if((one ^ two) == 1 && (findWorker(1) % 2 == 0 || findWorker(2) % 2 == 0))
            return {cell:4,color:1};
    }
    if(getNumWorkers(1) == 0 && getNumWorkers(2) == 0) {
        if(getNumWorkers(4) == 1 && getNumWorkers(3) == 0) {
            var pos4 = findWorker(4);
            if(view[deRotate(pos4,1)].ant == null && view[deRotate(pos4,2)].ant == null && findWorker(4) % 2 == 1) {
                //finish rotate with only one glider arm
                return {cell:4};
            }
        }
        return foreverAlone();
    }
    else if(getNumWorkers(1) >= 1 && getNumWorkers(2) >= 1 && getNumWorkers(3) >= 1 && getNumWorkers(4) >= 1) {
        if(view[4].color != 2 && findWorker(1)%2 == 1 && findWorker(2)%2 == 1) {
            return {cell:4,color:TRAIL};
        }
        //move diagonally
        var pos = findWorker(4);
        pos = deRotate(pos,1);
        var checkpos = view[deRotate(pos,4)];
        if(checkpos.ant != null && checkpos.ant.friend) {
            if(checkpos.ant.type == 2)
                return {cell:4};
            if(checkpos.ant.type == 1)
                return {cell:4};
        }
        if(view[pos].ant) return {cell:4,color:1};
        return {cell:pos};
    }
    else {
        var pos = findWorker(4);
        if(pos < 0) {
            //if gliding along with only a buddy
            pos = findWorker(1);
            if(pos >= 0 && view[deRotate(pos,2)].food > 0 && view[deRotate(pos,1)].food == 0) {
                return {cell:4};
            }
        }
        if(pos < 0) {
            var s1 = view[1].ant;
            var s3 = view[3].ant;
            var s5 = view[5].ant;
            var s7 = view[7].ant;
            //return {cell:999}
            if(s1 == null) {
                if(s3 == null) {
                    return {cell:0};
                }
                if(s5 == null) {
                    return {cell:2};
                }
            }
            if(s7 == null) {
                if(s3 == null) {
                    return {cell:6};
                }
                if(s5 == null) {
                    return {cell:8};
                }
            }
            return {cell:4};
        }
        pos = deRotate(pos,1);
        var checkpos1 = view[pos];
        for(var i=0;i<9;i++) {
            if(i != 4 && view[i].ant != null && view[i].ant.type == 5 && view[i].ant.food > 2) {
                if(i%2==0) {
                    if(view[deRotate(i,1)].ant == null) return {cell:deRotate(i,1),type:3};
                    if(view[deRotate(i,-1)].ant == null) return {cell:deRotate(i,-1),type:3};
                }
                else {
                    if(view[deRotate(i,1)].ant == null) return {cell:deRotate(i,1),type:3};
                    if(view[deRotate(i,-1)].ant == null) return {cell:deRotate(i,-1),type:3};
                    if(view[deRotate(i,2)].ant == null) return {cell:deRotate(i,2),type:3};
                    if(view[deRotate(i,-2)].ant == null) return {cell:deRotate(i,-2),type:3};
                }
                return {cell:4};
            }
        }
        if(checkpos1.ant != null && view[deRotate(pos,1)].ant != null && !view[deRotate(pos,1)].ant.friend) {
            return foreverAlone();
        }

        var checkpos2 = view[deRotate(pos,4)];
        var checkpos3 = view[deRotate(checkpos,1)];
        if(checkpos1.ant != null && checkpos1.ant.friend && checkpos1.ant.type == 1 && checkpos2.ant != null && checkpos2.ant.friend && checkpos2.ant.type == 2 && checkpos3.ant != null && checkpos3.ant.friend && checkpos3.ant.type == 3) {
            //move out of spawn orientation
            return {cell:4};
        }
        if(view[pos].ant != null) {
            if(checkpos2.ant == null && checkpos1.ant == null) {
                return {cell:8-pos};
            }
            if(!view[pos].ant.friend) {
                return foreverAlone();
            }
            if(view[4].color == TRAIL) return foreverAlone();
            return {cell:4,color:TRAIL};
        }
        if(8 - findWorker(3) == findWorker(4)) {
            //finish rotate to the right
            return {cell:4};
        }
        if((view[deRotate(pos,1)].food > 0 || view[deRotate(pos,2)].food > 0) && view[deRotate(pos,1)].ant == null && view[4].color != TRAIL) {
            if(findWorker(1) < 0 || view[deRotate(findWorker(1),1)].food == 0) {
                return {cell:4};
            }
        }
        return {cell:pos};
    }
    return {cell:100+view[4].ant.type}; //oh god
}

//guides sit next to the queen
function doGuide() {
    var queenPos = findWorker(5);
    var ty = view[4].ant.type==3?2:1;
    var dir = view[4].ant.type==3?1:-1;
    if(queenPos >= 0 && queenPos%2 == 1 && view[queenPos].color == SPAWN) {
        if(view[deRotate(queenPos,dir*2)].ant == null) {
            return {cell:4};
        }
    }
    if(queenPos < 0 || findWorker(ty) < 0) {
        if(findWorker(ty) >= 0 && view[0].color != IDLE) return {cell:0,color:IDLE}
        return firebreak();
    }
    var checkpos = view[deRotate(queenPos,-2*dir)];
    if(view[4].ant.type==4 && checkpos.ant != null && checkpos.ant.friend && checkpos.ant.type == 1) {
        //attempt rotate
        return {cell:deRotate(queenPos,-dir)};
    }
    checkpos = view[deRotate(queenPos,4)];
    if(checkpos.ant != null && checkpos.ant.friend && checkpos.ant.type == ty) {
        //attempt rotate
        if(getNumWorkers(ty) == 1) {
            return {cell:4};
        }
    }
    var pos = deRotate(queenPos,dir);
    if(pos >= 0 && view[4].ant.type==3 && findWorker(4) < 0) {
        //wait for rotate
        if(view[4].color == TRAIL) {
            return {cell:4};
        }
    }
    if(pos >= 0 && findWorker(2) >= 0 && view[deRotate(findWorker(2),1)].ant != null) {
        //rotate
        return {cell:4,color:TRAIL};
    }
    if(pos < 0) pos = 4;
    else if(view[pos].ant != null) return {cell:deRotate(queenPos,4)};
    if(pos == 4 && view[queenPos].color == TRAIL) return {cell:queenPos,color:1};
    return {cell:pos};
}

//sweepers sit next to guides
function doSweep() {
    var queenPos = findWorker(5);
    var followType = view[4].ant.type==1?4:3;
    var pos = findWorker(followType);
    if(pos % 2 == 0 && getNumWorkers(followType) > 1) {
        //if there's more than one worker #4, we want to use the best one
        for(var i=pos+1;i<9;i++) {
            if(i != 4 && view[i].ant != null) {
                if(view[i].ant.friend && view[i].ant.type == followType) {
                    pos = i;
                    break;
                }
            }
        }
    }
    if(queenPos >= 0 && queenPos%2 == 1 && view[queenPos].color == SPAWN) {
        var p = findWorker(view[4].ant.type);
        if(p >= 0 && (deRotate(p,1) == queenPos || deRotate(p,-1) == queenPos)) {
            return {cell:8-queenPos};
        }
        return {cell:4};
    }
    if(queenPos >= 0 && pos < 0) {
        //if Worker #1 is the only ant besides the queen:
        //TODO
    //good
        if(view[queenPos].ant.food <= SPAWN_MIN || !(view[queenPos].ant.food < FOOD_THRESHOLD && view[queenPos].ant.food % 3 == 1 && !isOnHighway())) {
            var go = deRotate(queenPos,-1);
            if((view[deRotate(queenPos,-2)].food > 0 || view[deRotate(queenPos,-3)].food > 0 || (queenPos %2 == 1 && view[deRotate(queenPos,-3)].food > 0)) && view[go].food == 0) {
                go = deRotate(queenPos,2);
                //return {cell:4};
            }
            return {cell:go};
        }
        else if(view[queenPos].ant.food < FOOD_THRESHOLD && view[queenPos].ant.food % 3 == 1) {
            return {cell:4};
        }
    }
    if(queenPos >= 0) {
        var dir = view[4].ant.type==1?1:-1;
        //var checkpos = view[deRotate(pos,-dir)];
        var moveTo = deRotate(pos,dir);
        if(moveTo >= 0 && view[moveTo].ant != null && view[moveTo].ant.friend && view[moveTo].ant.type == 5) {
            moveTo = deRotate(pos,-dir);
        }
        if(view[4].ant.type == 2 && findWorker(1) < 0 && view[queenPos].color != TRAIL) {
            moveTo = 4;
        }
        return {cell:moveTo};
    }
    else {
        if(pos < 0) return {cell:4}; //firebreak();
        var dir = view[4].ant.type==1?-1:1;
        var moveTo = deRotate(pos,dir);
        if(view[4].ant.food > 0 && view[moveTo].food > 0) {
            //have food, attempt to give to queen
            moveTo = deRotate(pos,-dir);
        }
        if(view[4].ant.type==1 && pos >= 0 && (view[deRotate(pos,dir*2)].food > 0 || view[deRotate(pos,dir*3)].food > 0)) {
            //attempt rotate
            moveTo = deRotate(pos,-dir);
        }
        if(view[4].ant.type==2 && pos >= 0 && (view[deRotate(pos,dir*2)].food > 0 || view[deRotate(pos,dir*3)].food > 0)) {
            //attempt rotate
            moveTo = deRotate(pos,-dir);
        }
        if(moveTo >= 0 && view[moveTo].ant != null && view[moveTo].ant.type == 5) {
            if(view[moveTo].ant.friend)
                moveTo = deRotate(moveTo,dir*2);
            else
                moveTo = deRotate(pos,-dir);
        }
        return {cell:moveTo};
    }
    return {cell:100+view[4].ant.type};//oh god
}

function foreverAlone() {
    var s0 = view[0].ant;
    var s1 = view[1].ant;
    var s2 = view[2].ant;
    var s3 = view[3].ant;
    var s5 = view[5].ant;
    var s6 = view[6].ant;
    var s7 = view[7].ant;
    var s8 = view[8].ant;
    //good
    if(!(s0 == null && s1 == null && s2 == null && s3 == null && s5 == null && s6 == null && s7 == null && s8 == null) && view[4].color == TRAIL) {
        if (view[0].color == TRAIL && !view[8].ant && view[8].color != TRAIL) return {cell: 8};
        else if (view[2].color == TRAIL && !view[6].ant && view[6].color != TRAIL) return {cell: 6};
        else if (view[6].color == TRAIL && !view[2].ant && view[2].color != TRAIL) return {cell: 2};
        else if (view[8].color == TRAIL && !view[0].ant && view[0].color != TRAIL) return {cell: 0};
        //Can't find color, or path is blocked? try diagonals regardless of color
        else if (!view[0].ant) return {cell: 0};
        else if (!view[2].ant) return {cell: 2};
        else if (!view[6].ant) return {cell: 6};
        else if (!view[8].ant) return {cell: 8};
        //Everything else failed? Stay put.
        else return {cell: 4};
    }
    //good
    if (view[4].color == TRAIL) { //If on colored square, try to move
        var totGreen = 0;
        for (var i = 0; i < 9; i++) { //Look for food
            if (view[i].food) {
                return {cell: i};
            }
            if(view[i].color == TRAIL) totGreen++;
        }
        var ret = getTrailMove();
        if(view[deRotate(ret.cell,1)].color == TRAIL && totGreen <= 4) ret.cell = deRotate(ret.cell,-1);
        else if(view[deRotate(ret.cell,-1)].color == TRAIL && totGreen <= 4) ret.cell = deRotate(ret.cell,1);
        return ret;
    } else { //If not on colored square, look for food, or set current color to 2.
        for (var i = 0; i < 9; i++) { //Look for enemies
            if (i != 4 && view[i].ant != null && !view[i].ant.friend) {
                var r = findOpenSpace(8-i,1);
                if(view[r].color == TRAIL) r = deRotate(r,1);
                return {cell: r};
            }
        }
        return {cell: 4, color:TRAIL};
    }
}

function getTrailMove() {
    if (view[0].color == TRAIL && !view[8].ant && view[8].color != TRAIL) return {cell: 8};
    else if (view[2].color == TRAIL && !view[6].ant && view[6].color != TRAIL) return {cell: 6};
    else if (view[6].color == TRAIL && !view[2].ant && view[2].color != TRAIL) return {cell: 2};
    else if (view[8].color == TRAIL && !view[0].ant && view[0].color != TRAIL) return {cell: 0};
    //Can't find color, or path is blocked? try diagonals regardless of color
    else if (!view[0].ant) return {cell: 0};
    else if (!view[2].ant) return {cell: 2};
    else if (!view[6].ant) return {cell: 6};
    else if (!view[8].ant) return {cell: 8};
    //Everything else failed? Stay put.
    else return {cell: 4};
}

function firebreak() {
    var ret = -1;
    if(findWorker(5) >= 0) {
        return {cell:8-findWorker(5)};
    }
    if(view[4].color != 5) {
        var myView = [0,0,0,0,0,0,0,0,0]
        for(var i=0; i < 9; i++) {
            myView[i] = view[i].color
            if(view[4].ant.food > 0 && view[i].food > 0) {
                myView[i] = 8;
            }
            if(view[i].ant != null && !view[i].ant.friend) return {cell:findOpenSpace(deRotate(i,2),1)};
        }
        var ret = clearAhead(myView);
        if(ret == null)
            return {cell:4,color:5};
        else {
            if(!(view[ret.cell].ant != null && view[ret.cell].ant.friend == false) && (view[4].ant.food == 0 || view[ret.cell].food == 0))
                return ret;
            return {cell:4,color:5};
        }
    }
    if(view[1].color == 5 && view[3].color == 5 && view[5].color == 5 && view[7].color == 5) {
        if(view[0].color != 8) return {cell:0,color:8};
        if(view[1].color != 8) return {cell:1,color:8};
    }
    if(view[1].color == 5 && view[7].color != 5) ret = {cell:7};
    if(view[3].color == 5 && view[5].color != 5) ret = {cell:5};
    if(view[5].color == 5 && view[3].color != 5) ret = {cell:3};
    if(view[7].color == 5 && view[1].color != 5) ret = {cell:1};
    if(view[1].color != 5 && view[3].color != 5 && view[5].color != 5 && view[7].color != 5) ret = {cell:1};
    if((view[1].color == 5 && view[7].color == 5) || (view[3].color == 5 && view[5].color == 5)) ret = {cell:0};
    var loop = 0;
    while(ret.cell >= 0 && ((view[ret.cell].food > 0 && view[4].ant.food > 0) || view[ret.cell].ant != null) && loop < 9) {
        loop++;
        ret.cell = (ret.cell + 2) % 9;
    }
    if(loop < 9 && ret.cell >= 0) return ret;
    return {cell:4};
}

//7,6,4,2,3
//O7,D2

function highwayRobbery() {
    var move = basicHighwayMove();
    if(move.cell >= 0 && view[move.cell].ant != null) {
        var n = HIGHWAY_COLORS.indexOf(view[4].color) + (view[4].color%2==0?1:HIGHWAY_COLORS.length);
        var nextMove = HIGHWAY_COLORS[n % HIGHWAY_COLORS.length];
        for(var i=0;i<9;i++) {
            if(view[i].color == nextMove) {
                return {cell:i};
            }
        }
    }
    return move;
}

function basicHighwayMove() {
    var isQueen = view[4].ant.type == 5;
    if(isHighwayCenter()) {
        var n = HIGHWAY_COLORS.indexOf(view[4].color) + 1;
        var nextMove = HIGHWAY_COLORS[n % HIGHWAY_COLORS.length];
        for(var i=0;i<9;i++) {
            if(view[i].color == nextMove) {
                if(view[i].ant == null)
                    return {cell:i};
                else {
                    return {cell:deRotate(i,1)};
                }
            }
        }
    }
    else {
        if(view[4].color == 7) {
            //move diagonal to yellow (2)
            for(var i=0;i<9;i++) {
                if(i != 4 && i % 2 == 0 && view[i].color == 2) {
                    return {cell:i};
                }
            }
        }
        else {
            //move orthogonal to blue (7)
            for(var i=0;i<9;i++) {
                if(i % 2 == 1 && view[i].color == 7) {
                    //try ortho yellow first
                    for(var j=0;j<9;j++) {
                        if(j % 2 == 1 && view[j].color == 2 && areAdjacent(i,j))
                            return {cell:j};
                    }
                    return {cell:i};
                }
            }
            //if orthogonal blue doens't exist...
            //...try diagonal to magenta
            for(var i=0;i<9;i++) {
                if(i != 4 && i % 2 == 0 && view[i].color == 3) {
                    return {cell:i};
                }
            }
            if(view[4].color != 2) {
                //...try diagonal blue
                for(var i=0;i<9;i++) {
                    if(i % 2 == 0 && view[i].color == 7)
                        return {cell:i};
                }
            }
            //...and orthogonal yellow
            for(var i=0;i<9;i++) {
                if(i % 2 == 1 && view[i].color == 2)
                    return {cell:i};
            }
        }
        var n = HIGHWAY_COLORS.indexOf(view[4].color) + 1;
        var nextMove = HIGHWAY_COLORS[n % HIGHWAY_COLORS.length];
        for(var i=0;i<9;i++) {
            if(view[i].color == nextMove) {
                return {cell:i};
            }
        }
    }
    return {cell:-1};
}

function isOnHighway() {
    var match = 0;
    var nxt = HIGHWAY_COLORS[(HIGHWAY_COLORS.indexOf(view[4].color)+1) % HIGHWAY_COLORS.length];//4
    var prv = HIGHWAY_COLORS[(HIGHWAY_COLORS.indexOf(view[4].color)+HIGHWAY_COLORS.length-2) % HIGHWAY_COLORS.length];//6
    for(var i=0;i<9;i++) {
        if(HIGHWAY_COLORS.indexOf(view[i].color) >=0 && (i == 4 || view[i].color != view[4].color))
            match++;
    }
    if(match >= 5) {
        //7,6,4,2,3


        if((view[1].color == nxt && view[7].color == prv)||(view[1].color == prv && view[7].color == nxt) || 
            (view[3].color == nxt && view[5].color == prv)||(view[3].color == prv && view[5].color == nxt)) {
            return true;
        }
        if((view[1].color == view[8].color && (view[1].color == nxt || view[1].color == prv))||(view[1].color == view[6].color && (view[1].color == nxt || view[1].color == prv)) || 
            (view[3].color == view[2].color && (view[3].color == nxt || view[3].color == prv))||(view[3].color == view[8].color && (view[3].color == nxt || view[3].color == prv))) {
            return true;
        }
        if((view[0].color == view[7].color && (view[0].color == nxt || view[0].color == prv))||(view[2].color == view[7].color && (view[2].color == nxt || view[2].color == prv)) || 
            (view[0].color == view[5].color && (view[0].color == nxt || view[0].color == prv))||(view[6].color == view[5].color && (view[6].color == nxt || view[6].color == prv))) {
            return true;
        }
        if(isHighwayCenter()) {
            return true;
        }
    }
    return false;
}

function isHighwayCenter() {
    if(HIGHWAY_COLORS.indexOf(view[4].color) >=0 && (((view[0].color != view[8].color || view[2].color != view[6].color) && view[4].ant.type == 1) || (view[0].color != view[8].color && view[2].color != view[6].color))){
        var m1 = view[1].color == view[7].color;
        var m2 = view[2].color == view[8].color;
        var m3 = view[0].color == view[6].color;
        var m4 = view[0].color != 1 && view[2].color != 1;
        if((m1?1:0)+(m2?1:0)+(m3?1:0) >= 2 && m4) {
            if(view[3].color != view[5].color && ((view[2].color != view[5].color && view[8].color != view[5].color) || view[4].ant.type == 1) && ((view[3].color != view[0].color && view[3].color != view[6].color) || view[4].ant.type == 1)) {
                return true;
            }
        }
        m1 = view[3].color == view[5].color;
        m2 = view[0].color == view[2].color;
        m3 = view[6].color == view[8].color;
        m4 = view[0].color != 1 && view[6].color != 1;
    //good
        if((m1?1:0)+(m2?1:0)+(m3?1:0) >= 2 && m4) {
            m1 = view[1].color != view[7].color;
            m2 = (view[0].color != view[1].color && view[1].color != view[2].color);
            m3 = (view[6].color != view[7].color && view[7].color != view[8].color);
            if(m1 && m2 && m3) {
                return true;
            }
            if(view[4].ant.type == 1 && ((m1?1:0)+(m2?1:0)+(m3?1:0)) >= 2) {
                return true;
            }
        }
    }
    return false;
}

function deRotateSide(m, amt) {
    return deRotate(m,amt*2);
}

/**Positive amount is clockwise**/
function deRotate(m, amt) {
    var rotationsCW = [1,2,5,8,7,6,3,0];
    var rotationsCCW = [3,6,7,8,5,2,1,0];
    if(m == 4 || m < 0 || m > 8 || amt == 0) return m;
    if(amt > 0)
        return rotationsCW[(rotationsCW.indexOf(m)+amt)%8];
    amt = -amt;
    return rotationsCCW[(rotationsCCW.indexOf(m)+amt)%8];
}

function areAdjacent(A, B) {
    if(A == 4 || B == 4 || A == B) return true;
    if(A % 2 == 0 && B % 2 == 0) return false;
    if(A % 2 == 1 && B % 2 == 0) return areAdjacent(B,A);
    if(A % 2 == 1 && B % 2 == 1) return !(8-A == B || 8-B == A);
    if(A == 0 && (B == 1 || B == 3)) return true;
    if(A == 2 && (B == 1 || B == 5)) return true;
    if(A == 6 && (B == 3 || B == 7)) return true;
    if(A == 8 && (B == 5 || B == 7)) return true;
    return false;
}

function findFirstTrail() {
    var pos = 0;
    var b = 0;
    while(view[pos].color != TRAIL && b < 8) {
        pos=deRotate(pos,1);
        b++;
    }
    return pos;
}

function clearAhead(sides) {
    var c=0;
    for(var i=0;i<9;i++) {
        if(view[i].color == 5) c++;
        if(view[i].color == 5 && i%2 == 0) c+=10;
    }
    if(c == 2) {
        if(view[0].color == 5 || view[2].color == 5 || view[6].color == 5 || view[8].color == 5) {
            return {cell:4,color:5};
        }
        if(view[0].ant == null)
            return {cell:0};
        if(view[2].ant == null)
            return {cell:2};
        if(view[6].ant == null)
            return {cell:6};
        if(view[8].ant == null)
            return {cell:8};
    }
    c = 0;
    sides[4] = 0;
    var toMatch =[{state:[1,1,1,
                          2,0,2,
                          0,1,0]},
                 {state:[0,2,1,
                         1,0,1,
                         0,2,1]},
                 {state:[0,1,0,
                         2,0,2,
                         1,1,1]},
                 {state:[1,2,0,
                         1,0,1,
                         1,2,0]}];
    for(var m=0;m<4;m++) {
        var score=0;
        for(var j=0;j<9;j++) {
            if(j!=4) {
                if(sides[j] == 5 && toMatch[m].state[j] == 1) {
                    score++;
                }
                if(sides[j] != 5 && (toMatch[m].state[j] == 0 || toMatch[m].state[j] == 2)) {
                    score++;
                }
                if(sides[j] == 5 && toMatch[m].state[j] == 2) {
                    score--;
                }
            }
        }
        if(score >= 6) {
            var clearOrder=[1,0,2];
            for(var r=0;r<clearOrder.length;r++) {
                var s = deRotateSide(clearOrder[r],m);
                if(view[s].color == 5) {
                    if(view[s].ant == null)
                        return {cell:s,color:8};
                    else
                        return {cell:4};
                }
            }
        }
    }
    return null;
}

function findOpenSpace(pos, dir) {
    if(pos > 8 || pos < 0) return pos;
    var b = 0;
    while(view[pos].ant != null && b < 8) {
        pos=deRotate(pos,dir);
        b++;
    }
    return pos;
}

function getHighestWorker() {
    var r=0;
    for(var i=0;i<9;i++) {
        if(i != 4 && view[i].ant != null) {
            if(view[i].ant.friend && view[i].ant.type > r) r = view[i].ant.type;
        }
    }
    return r;
}

function getNumWorkers(type) {
    var r=0;
    for(var i=0;i<9;i++) {
        if(i != 4 && view[i].ant != null) {
            if(view[i].ant.friend && view[i].ant.type == type) r++;
        }
    }
    return r;
}

function findWorker(type) {
    for(var i=0;i<9;i++) {
        if(i != 4 && view[i].ant != null) {
            if(view[i].ant.friend && view[i].ant.type == type) return i;
        }
    }
    return -1;
}

422% Завершіть
422%, оскільки оновлення було 4/22 Цей жарт надто довго тривав

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

Оновлення 8/1

Виходить вдвічі частіше, в результаті чого на 15% більше зібраної їжі

Оновлення 8/2

Додано стійкість і застрягання.

Оновлення 9/9

Доданий код відродженим працівникам 2 і 3, коли ця рука відсутня.

Оновлення 9/12

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

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

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

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

Частина нового коду включена з мого іншого запису, Чорна діра .

Оновлення 9/13

Виправлена ​​помилка в коді переміщення нересту-орієнтації. Пара 2ручки s перетворилася на TRAILs.

Оновлення 1/21

  • Планер тепер може повернути ліворуч (очікування анімації).
  • Рання гра прискорюється нерестом одного працівника, як тільки є їжа.
  • Ковзання однією рукою може також повертати обидва боки.
  • Піднято поріг "Не відроджувати робітників"
  • "Не витрачайте їжу, намагаючись знову відмовитись і не вдається" зменшено з %5до%3

Оновлення 1/25

  • Виправлено деякі сценарії тупикових ситуацій, підвищивши ефективність на кілька очок у деяких матчах.

Оновлення 2/20

  • Розглянуто декілька сценаріїв вибуху / глухого кута, що робить конфігурації 5 та 3 мурашок більш стійкими. Декілька продуктів харчування можуть призвести до того, що Планер заплутається і буде або замикати (багаторазово обертатися назад і назад), або вибухати.
    • Один вибух був результатом того, як мураха перемістилася на їжу під час останнього кроку обертання, тоді припускаючи, що "лайно, я їжу, приношу себе в жертву королеві", коли цього не потрібно було (стандартний хід був дійсний ).
    • Один вибух був результатом того, що клітина під королевою вже була TRAIL(зелена), спричиняючи один з кроків королеви "залишатися нерухомим" (обробляється за допомогою резервного звору, щоб foreverAlone()змусити королеву відійти від групи. Виправлено це, змінивши стаціонарну охорону колір клітини королеви безпосередньо перед тим, як королева вчинить свої дії.
    • Більш бажано, ніж виправляти код королеви, оскільки це дозволяє нам уникнути тупикових ситуацій, які можуть виникнути в іншому випадку, утримуючи резервну копію та foreverAlone()вивести королеву, навіть якщо ми не можемо зберегти її крила.
  • foreverAlone()функція оновлена ​​для переміщення на їжу, лише якщо клітина під королевою TRAIL(зелена). Дуже незначні втрати ефективності.
  • Відновлення планера Morebetter знаходить сусідню зелену клітинку, зміни findFirstYellow()та її використання. Це в поєднанні з попередньою кулею призводить до кращого уникнення раніше помічених осередків та / або місця недавнього вибуху: новостворене планер - у великій більшості випадків - рухатиметься у напрямку не вздовж в'їзду королеви. траєкторія.
    • Функція перейменована: Жовтий довгий-довгий час не був кольором слідів королеви.
    • Функція також була неправильно розташованою "першою коміркою сліду" через перевернуту логічну перевірку: while ==замістьwhile !=

Оновлення 2/25

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

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

Працівник 1 спробує здійснити ремонт центрального коридору шосе та виконує переривання найкращої здогадки, якщо він опиниться на місці Шосе, яке НЕ залізничне (а не ремонт помилкового центру). Мандрує в тому ж напрямку, що і королева (проти працівників 2, 3 і 4).

Робітники 2, 3 і 4 намагаються здійснити крадіжку наїзду на королеву Шосе, а потім повернутися до своєї власної королеви, яка нереститься працівників, коли вона ...

  • не може бачити жодного власного працівника
  • сидить на пурпуровій
  • є їжа між порогами (з м'якими межами з обох боків)
  • модифікований простим випадковим чином

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

Це була тактика, яку я планував писати у «Вампір», але перша спроба Вампіра заблокувати королеву Шосе і просто протікати кровотечею, тому не було стимулів створювати таку поведінку. Код виявлення на шосе написаний з нуля і відрізняється від вампіра, хоча дзеркальне виявлення (IsHighwayCenter) дуже схоже, саме завдяки простоті шаблону.

Оновлення 3/16

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

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

Незначні настрої

  • Королева більше не намагається почати нерест, коли виявить, що знаходиться на трасі
  • Королева більше не горить через Калорії, намагаючись завершити формування планера, який ніколи не завершиться (нереститься працівник 4, працівник 4 не бачить працівника 1, працівник 4 відходить, повторити)
  • Налаштовані значення модуля продовольства, що відповідають рівню кольорового циклу шосе.
  • Виправлено "обертання фінішу 1 рукою", що спричинило тупик
  • Незначні виправлення до isOnHighway()чека
  • Змушені мурахи рухаються на ортогональний жовтий колір перед тим, як перейти на ортогональну синю клітинку під час руху на шосе (економить 1 поворот, знаходячи центр, після того, як він від'єднався).

Оновлення 3/26

Більш незначні налаштування.

Оновлення 4/21

Незначні виправлення.


Вільно базується на Steamroller .

Збирає 4 їжі, залишаючи після себе жовтий слід. Після того, як у нього є чотири продукти харчування, він виробляє один тип кожного робітника навколо себе. Потім воно починає ковзати вздовж максимальної тяги. Переміщення анімації вгорі займає 1 ігровий поворот для завершення: коли робочий 1 виробляється спочатку, він виконує спочатку, потім 2, 3 і 4, перш ніж закінчити чергу з королевою. Повернення анімації займає 2 витки, короткі паузи - це коли королева виступає, {cell:4}а їжа рухається, коли робітники 3, 4, а королева виконує все {cell:4}, щоб підготуватися до подальшого плавання.


Мені подобається ця ідея про бота
Destructible Lemon

1
@DestructibleLemon Дякую! Я думав про Steamroller / Piercer і думав собі: "тримайся, королева триває останнього. Напевно, я можу отримати п’ять мурашок, щоб грати добре ..." схопив папірець, відірвав кілька квадратів і почав їх рухати. Це спрацювало, тому я записав якийсь код. Довелося зробити багато поводження з помилками та виправлення помилок для крайових справ (початковий бот застряг або спожив всю їжу, яку він міг би зробити і зробити, додавши операцію повороту, введено безліч кращих сценаріїв випадків, коли це порушить формування та крах. ).
Draco18s

@trichoplax, поки він не вріжеться в іншого мураха?
Руйнуючий лимон

@DestructibleLemon eeeehhhh ... можливо. Принаймні не дискваліфікується ...
Draco18s

3
@KZhang О, я. Шукайте коментарі "// спроба повернути"
Draco18s

13

Вітряк

Ось вітряк ... чекаючи на Людину з Ла-Манчі (або Вампір з Ла-Манчі?), Яка під'їде, щоб її збити.

Вітряк, рання середня стадія гри

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

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

var AJM=1;var ASM=2;var AE=3;var ASF=4;var AQ=5;var RW=true;var EFCO=false;var THC=1;var TH0=0;var TH1=15;var TH2=17;var TH3=67;var TH4=120;var TH5=390;var THX=15;var THFCO1=9;var THFCO2=26;var THFCO3=75;var RM1=7;var RD1=4;var RM2=19;var RD2=THX;var PW=1;var PY=2;var PP=3;var PC=4;var PR=5;var PG=6;var PB=7;var PK=8;var LN=0;var LCLR=PW;var LT=PB;var LLSF=PP;var LA=PP;var LRL0=PC;var LRL1=PG;var LRL2=LRL0;var LRM0=PR;var LRM1=PB;var LRM1_WRP=PK;var LRM2=PG;var LRR0=PG;var LRR1=PW;var LRR1U=PR;var LRR1V=PY;var LRR1X=PK;var LRR2=PY;var LMX_M0=LCLR;var LMX_M1IN=PC;var LMX_M1OUT=PY;var LMX_M2IN=PP;var LMX_M2OUT=PR;var LMX_M3IN=PB;var LMX_M3OUT=PK;var LMS_WRP=PK;var LMR0=PK;var LML1=PY;var LMR2=PR;var LML3=PC;var LMMF=PG;var LMMH=PP;var LG3=PK;var LG4=PR;var LG5=PK;var LG6=PB;var LP0=LCLR;var LPB=PC;var LPG=PY;var LPG1=PR;var LPX=PP;var FALSE_X9=[false,false,false,false,false,false,false,false,false];
var UNDEF_X9=[undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined];
var QCPERD=6;var LCL_QC_RESET=LCLR;var LCRQC=[PY,PP,PC,PR,PG,PK];var LCRQCVAL=Array.from(FALSE_X9);var LCRQC_VALUE=Array.from(UNDEF_X9);for (var i=0; i<QCPERD; i++){LCRQCVAL[LCRQC[i]]=true;LCRQC_VALUE[LCRQC[i]]=i;}var SCPERD=7;var LCL_SC_RESET=LCLR;var LCRSC=[PY,PP,PC,PR,PG,PB,PK];var LCRSCVAL=Array.from(FALSE_X9);var LCRSC_VALUE=Array.from(UNDEF_X9);for (i=0; i<SCPERD; i++){LCRSCVAL[LCRSC[i]]=true;LCRSC_VALUE[LCRSC[i]]=i;}var LCRPHR=Array.from(FALSE_X9);LCRPHR[LPG]=true;LCRPHR[LPG1]=true;var LCRPHASES=Array.from(LCRPHR);LCRPHASES[LPX]=true;var LCRGRM1=Array.from(FALSE_X9);LCRGRM1[LRM1]=true;LCRGRM1[LRM1_WRP]=true;var LCRGRM_ALL=Array.from(FALSE_X9);LCRGRM_ALL[LRM0]=true;LCRGRM_ALL[LRM1]=true;LCRGRM_ALL[LRM1_WRP]=true;LCRGRM_ALL[LRM2]=true;var LCRGRR1_OUT=Array.from(FALSE_X9);LCRGRR1_OUT[LRR1V]=true;LCRGRR1_OUT[LRR1X]=true;var LCRGRR1B=Array.from(LCRGRR1_OUT);LCRGRR1B[LRR1U]=true;var LCRGRR1=Array.from(LCRGRR1B);LCRGRR1[LRR1]=true;var LCRMX_IO=Array.from(FALSE_X9);LCRMX_IO[LMX_M1IN]=true;LCRMX_IO[LMX_M1OUT]=true;LCRMX_IO[LMX_M2IN]=true;LCRMX_IO[LMX_M2OUT]=true;LCRMX_IO[LMX_M3IN ]=true;LCRMX_IO[LMX_M3OUT]=true;var LCRMX=Array.from(LCRMX_IO);LCRMX[LMX_M0]=true;var LCRMX_IN=Array.from(FALSE_X9);LCRMX_IN[LMX_M1IN]=true;LCRMX_IN[LMX_M2IN]=true;LCRMX_IN[LMX_M3IN ]=true;var LCRMX_OUT=Array.from(FALSE_X9);LCRMX_OUT[LMX_M1OUT]=true;LCRMX_OUT[LMX_M2OUT]=true;var LCRMM_FOOD=Array.from(FALSE_X9);LCRMM_FOOD[LCLR]=true;LCRMM_FOOD[LMMF]=true;var LCRMM_HOME=Array.from(FALSE_X9);LCRMM_HOME[LCLR]=true;LCRMM_HOME[LMMH]=true;var LCRMS=Array.from(FALSE_X9);LCRMS[LCLR]=true;LCRMS[LMS_WRP]=true;var LCRFRLL0=Array.from(FALSE_X9);LCRFRLL0[LCLR]=true;LCRFRLL0[LMR0]=true;LCRFRLL0[LMR2]=true;LCRFRLL0[LRM0]=true;LCRFRLL0[LRM2]=true;var LCRFRLL1=Array.from(FALSE_X9);LCRFRLL1[LCLR]=true;LCRFRLL1[LMR0]=true;LCRFRLL1[LMR2]=true;LCRFRLL1[LRR0]=true;LCRFRLL1[LRM1]=true;LCRFRLL1[LRR2]=true;var LCRFRLL2=Array.from(FALSE_X9);LCRFRLL2[LCLR]=true;LCRFRLL2[LMR0]=true;LCRFRLL2[LMR2]=true;LCRFRLL2[LRM0]=true;LCRFRLL2[LRR1V]=true;var TN=8;var POSC=4;var NOP={cell:POSC};var AIMU=1;var AIML=3;var AIMR=5;var AIMD=7;var FWD_CELLS=[[ true,true,false,true,true,false,false,false,false ],[ true,true,true,true,true,true,false,false,false ],[ false,true,true,false,true,true,false,false,false ],[ true,true,false,true,true,false,true,true,false ],[ true,true,true,true,true,true,true,true,true ],[ false,true,true,false,true,true,false,true,true ],[ false,false,false,true,true,false,true,true,false ],[ false,false,false,true,true,true,true,true,true ],[ false,false,false,false,true,true,false,true,true ]];var PTNOM=-9;var PTHOME=[LRM0,LRL0,LRM0,LRL0,LN,LRL0,LN,LN,LRM0];var PTGARDEN=[LG6,LG5,LG4,LN,LN,LG3,LN,LRL0,LRL1];var PTFRM0=[LRL1,LRM1,LCRGRR1,LRL0,LRM0,LRR0,LRL2,LRM2,LRR2];var PTFRM1=[LRL2,LRM2,LRR2,LRL1,LCRGRM1,LCRGRR1,LRL0,LRM0,LRR0];var PTFRM2=[LRL0,LRM0,LRR0,LRL2,LRM2,LRR2,LRL1,LCRGRM1,LCRGRR1];var PTGRM0=[LRL1,LCRGRM1,LCRGRR1,LRL0,LRM0,LRR0,LRL2,LRM2,LRR2];var PTGRM1=PTFRM1;var PTGRM2=PTFRM2;var PTGRM2B=[LRL0,LRM0,LRR0,LRL2,LRM2,LRR2,LRL1,LCRGRM1,LCRGRR1B];var PTGRM1_WRP=[LRL2,LRM2,LRR2,LRL1,LRM1_WRP,LRR1X,LRL0,LRM0,LRR0];var PTFRL0=[LCRFRLL1,LRL1,LRM1,LCRFRLL0,LRL0,LRM0,LCRFRLL2,LRL2,LRM2];var PTFRL1=[LCRFRLL2,LRL2,LRM2,LCRFRLL1,LRL1,LCRGRM1,LCRFRLL0,LRL0,LRM0];var PTFRL0H=[LN,LRL1,LRM1,LN,LRL0,LRM0,LN,LN,LN];var PTFRL1G=[LCRFRLL2,LRL2,LRM2,LG3,LRL1,LCRGRM1,LCRPHASES,LRL0,LRM0];var PTFRL2=[LCRFRLL0,LRL0,LRM0,LCRFRLL2,LRL2,LRM2,LCRFRLL1,LRL1,LCRGRM1];var PTGRL0=[LCRFRLL1,LRL1,LCRGRM1,LCRFRLL0,LRL0,LRM0,LCRFRLL2,LRL2,LRM2];var PTGRL1=PTFRL1;var PTGRL2=PTFRL2;var PTGRR0=[LCRGRM1,LCRGRR1,LCLR,LRM0,LRR0,LMR0,LRM2,LRR2,LCRMX];var PTGRR2=[LRM0,LRR0,LMR0,LRM2,LRR2,LCRMX,LCRGRM1,LCRGRR1,LCLR];var PTGRR1=[LRM0,LCRGRM1,LRM2,LRR0,LCRGRR1,LRR2,LMR0,LCLR,LCRMX];var PTMS0R_IN=[LRR0,LRR1U,LRR2,LMR0,LCLR,LCRMX_IN,LCLR,LCLR,LML1];var PTMS0R_OUT=[LRR0,LRR1U,LRR2,LMR0,LCLR,LCRMX_IO,LCLR,LCLR,LML1];var PTMS0R_OUT1=[LRR0,LCRGRR1_OUT,LRR2,LMR0,LCLR,LCRMX,LCLR,LCLR,LML1];var PTMS0=[LCLR,LCRMM_HOME,LML3,LMR0,LCRMM_FOOD,LCLR,LCLR,LCLR,LML1];var PTMS1=[LMR0,LCRMM_HOME,LCLR,LCLR,LCRMM_FOOD,LML1,LMR2,LCLR,LCLR];var PTMS2=[LCLR,LCRMM_HOME,LML1,LMR2,LCRMM_FOOD,LCLR,LCLR,LCLR,LML3];var PTMS3=[LMR2,LCRMM_HOME,LCLR,LCLR,LCRMM_FOOD,LML3,LMR0,LCLR,LCLR];var PTMS1_IN=[LMR0,LCRMM_HOME,LCRMX_IN,LCLR,LCRMM_FOOD,LML1,LMR2,LCLR,LCLR];var PTMS1_IO=[LMR0,LCRMM_HOME,LCRMX_IO,LCLR,LCRMM_FOOD,LML1,LMR2,LCLR,LCLR];var PTMS0_OUT=[LCLR,LCLR,LML3,LMR0,LCLR,LCRMX_IO,LCLR,LCLR,LML1];var PTMS0_WRAPPING=[LCLR,LCRMM_HOME,LML3,LMR0,LCRMM_FOOD,LCRMS,LRL0,LRL1,LRL2];var PTGRL1_WRP=[LMR0,LCLR,LMS_WRP,LRL0,LRL1,LRL2,LRM0,LRM1_WRP,LRM2];var PTMS0FL=[LMMH,LML3,LN,LMMF,LCLR,LN,LCLR,LML1,LN];var PTMS1FL=[LMMH,LCLR,LN,LMMF,LML1,LN,LCLR,LCLR,LN];var PTMS2FL=[LMMH,LML1,LN,LMMF,LCLR,LN,LCLR,LML3,LN];var PTMS3FL=[LMMH,LCLR,LN,LMMF,LML3,LN,LCLR,LCLR,LN];var PTMS0FR=[LN,LCLR,LMMH,LN,LMR0,LMMF,LN,LCLR,LCLR];var PTMS1FR=[LN,LMR0,LMMH,LN,LCLR,LMMF,LN,LMR2,LCLR];var PTMS2FR=[LN,LCLR,LMMH,LN,LMR2,LMMF,LN,LCLR,LCLR];var PTMS3FR=[LN,LMR2,LMMH,LN,LCLR,LMMF,LN,LMR0,LCLR];var CCW=[6,7,8,5,2,1,0,3,6,7,8,5,2,1,0,3,6,7,8,5,2,1];
var xn=-1;var fwdWrong=[];var rearWrong=[];var here=view[POSC];var mC=here.color;var myself=here.ant;var mT=myself.type;var mF=myself.food;var mS=(mT==AE||(mT!=AQ&&mF>0));if (EFCO&&(mT==AQ)){if (mF<=THFCO1){QCPERD=5;} else if (mF<=THFCO2){QCPERD=4;} else if (mF<=THFCO3){QCPERD=5;}}var dOK=[true,true,true,true,true,true,true,true,true];
var uo=true;var sL=[0,0,0,0,0,0,0,0,0];var sD=[0,0,0,0,0,0,0,0,0];var sN=[0,0,0,0,0,0,0,0,0];var sT=[0,0,0,0,0,0,0,0,0];var fdL=0;var fdD=0;var fdT=0;sT[mC]++;for (i=0; i<TN; i+=2){var cell=view[CCW[i]];sD[cell.color]++;sN[cell.color]++;sT[cell.color]++;if (cell.food>0){fdD++;fdT++;if (mS){dOK[CCW[i]]=false;uo=false;}}}for (i=1; i<TN; i+=2){var cell=view[CCW[i]];sL[cell.color]++;sN[cell.color]++;sT[cell.color]++;if (cell.food>0){fdL++;fdT++;if (mS){dOK[CCW[i]]=false;uo=false;}}}var aF=[0,0,0,0,0,0];var aLF=[0,0,0,0,0,0];var aUF=[0,0,0,0,0,0];var fT=0;var mQ=0;var aE=[0,0,0,0,0,0];var aLE=[0,0,0,0,0,0];var aUE=[0,0,0,0,0,0];var eT=0;for (i=0; i<TN; i++){var cell=view[CCW[i]];if (cell.ant){if (cell.ant.friend){aF[cell.ant.type]++;fT++;if (cell.ant.type==AQ){xn=i&6;mQ=i&1;}if (cell.ant.food>0){aLF[cell.ant.type]++;} else {aUF[cell.ant.type]++;}} else {aE[cell.ant.type]++;eT++;if (cell.ant.food>0){aLE[cell.ant.type]++;} else {aUE[cell.ant.type]++;}}dOK[CCW[i]]=false;uo=false;}}switch (mT){case AQ:return (rQSs());case ASF:if (mQ==1){return (rSSs());} else if (aF[AQ]>0){return (rGSs());} else {return (rLSSy());}case AE:return (rESs());case AJM:case ASM:if (aE[AQ]>0){return (rDSs());} else if (mF>0){return (rLSs());} else {return (rUSs());}default:return NOP;}function rQSs (){switch (aF[ASF]){case 0:return (rQScrSy());case 1:for (var i=0; i<TN; i++){var cell=view[CCW[i]];if (cell.ant&&cell.ant.type==ASF){xn=i&6;if (i&1){if (mF<=THX){return (rQLsSy());} else {return (rQLvSy());}} else {return (rQSgSy());}}}break;case 2:for (i=0; i<TN; i+=2){var cell0=view[CCW[i]];var cell1=view[CCW[i+1]];if ((cell0.ant&&(cell0.ant.type==ASF))&&(cell1.ant&&(cell1.ant.type==ASF))){xn=i;return (rQOSy());}}return (rQCSy());default:return (rQCSy());}return NOP;}function rSSs (){if (view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&(view[CCW[xn+3]].ant.type==ASF)){return (rSOSy());} else if (view[CCW[xn+1]].ant.food<=THX){return (rSLSy());} else {return (rSESy());}}function rGSs (){var secCell=view[CCW[xn+7]];if (secCell.ant&&(secCell.ant.friend==1)&&(secCell.ant.type==ASF)){return (rGOSy());} else {return (rGSSy());}return NOP;}function rESs (){if (aF[AQ]>0){return (rEHyS());} else if (aF[AJM] +aF[ASM]>0){return (rEBRSy());} else {return (rEASy());}return NOP;}function rDSs(){if (aF[AQ]>0){return (rDHSy());} else {for (var i=0; i<TN; i++){if (view[CCW[i]].ant&&(view[CCW[i]].ant.type==AQ)){if (i&1){if ((view[CCW[i+1]].ant&&view[CCW[i+1]].ant.friend&&view[CCW[i+2]].ant&&view[CCW[i+2]].ant.friend)||(view[CCW[i-1]].ant&&view[CCW[i-1]].ant.friend&&view[CCW[i+6]].ant&&view[CCW[i+6]].ant.friend)||(view[CCW[i+2]].ant&&view[CCW[i+2]].ant.friend&&view[CCW[i+6]].ant&&view[CCW[i+6]].ant.friend)){if (dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else if (dOK[CCW[i+3]]){return {cell:CCW[i+3]};} else if (dOK[CCW[i+5]]){return {cell:CCW[i+5]};}}} else {if (view[CCW[i+1]].ant&&view[CCW[i+1]].ant.friend&&
view[CCW[i+7]].ant&&view[CCW[i+7]].ant.friend){if (dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else if (dOK[CCW[i+3]]){return {cell:CCW[i+3]};} else if (dOK[CCW[i+5]]){return {cell:CCW[i+5]};} else if (dOK[CCW[i+6]]){return {cell:CCW[i+6]};} else if (dOK[CCW[i+2]]){return {cell:CCW[i+2]};}}if ((i<=2)&&dOK[CCW[i+7]]){return {cell:CCW[i+7]};}if ((i>=4)&&dOK[CCW[i+1]]){return {cell:CCW[i+1]};}}if (fT==0){if (view[CCW[i]].color!=PP){return {cell:CCW[i],color:PP};} else if (mC!=LCLR){return {cell:POSC,color:LCLR};}}}}}return NOP;}function rUSs (){if ((aF[AQ]>0)&&!LCRQCVAL[view[CCW[xn+mQ]].color]){return (rUHSy());} else if ((fT+eT>=4)&&(aF[AJM]+aF[ASM] +aF[AE]>=3)){return (rUCRSy());} else if (aF[AQ]>0){return (rUHSy());} else if (aF[ASF]>0){if (aF[ASF]>1){return (rM2R1Sy());} else {return (rURHSy());}} else if (aF[AE]>0){return (rUBRSy());} else if (spcRL1()){return (rULRL1Sy());} else if (spcRR0()){return (rULRR0Sy());} else if (spcRR2()){return (rULRR2Sy());} else if (spcMS()){return (rUDSSy());} else if (spcRL02()){return (rULRL02Sy());} else if (spcRM()){return (rUTRRSy());} else if (spcRR1()){return (rUPSSy());} else if (spcMS0R()){return (rUESSy());} else if (spcMS0W()){return (rUSWSy());}return (rLostMSy(true));}function rLSs (){if ((fT>=3)&&(fT+eT>=4)){return (rLCRSy());} else if (aF[ASF]>0){if (aF[ASF]>1){return (rM2R1Sy());} else {return (rLRHSy());}} else if (spcMFL()){return (rLLLWSy());} else if (spcMFR()){return (rLLRWSy());} else if (spcRL1()){return (rLLRL1Sy());} else if (spcRR0()){return (rLLRR0Sy());} else if (spcRR2()){return (rLLRR2Sy());} else if (spcMS0R()){return (rLLSSy());} else if (spcMS0ROut()){return (rLLVSSy());} else if (spcMS()&&(aF[AE]==0)){return (rLASSy());} else if (spcRL02()){return (rLLRL02Sy());} else if (spcRM()){return (rLTRRSy());} else if (spcRR1()){return (rLDSSy());} else if (aF[AE]>0){return (rLFRSy());}return (rLostMSy(true));}function rQScrSy(){if (uo){if (fdT>0){return (rQSETc());} else if (mF>=THC){for (var i=0; i<TN; i+=2){if ((view[CCW[i]].color==LT)||(view[CCW[i+1]].color==LT)){return {cell:CCW[i+1],type:ASF};}}return {cell:1,type:ASF};} else if (mC!=LT){if ((mC==LCLR)||(sN[LCLR]>=TN-1)){return {cell:POSC,color:LT};} else {return (rQSTCTc());}} else if ((sN[LCLR]>=4)&&(sN[LT]==1)){for (var i=0; i<TN; i+=2){if ((view[CCW[i]].color==LT)||(view[CCW[i+1]].color==LT)){return {cell:CCW[i+4]};}}} else if (sN[LCLR]==TN){return {cell:0};} else {return (rQSATc());}} else {if ((fdT>0)&&(eT>0)&&(eT==aE[AQ])){return (rQSSTc());} else {return (rQSEvTc());}}return NOP;}function rQSgSy(){if (fdT>0){if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else {for (var i=2; i<TN-1; i++){if (dOK[CCW[xn+i]]&&(view[CCW[xn+i]].food>0)){return {cell:CCW[xn+i]};}}for (var i=2; i<TN-1; i++){if (dOK[CCW[xn+i]]){     return {cell:CCW[xn+i]};}}return NOP;}} else if ((mF>TH0)&&(mC==LCL_QC_RESET)){if (dOK[CCW[xn+7]]){return { cell:CCW[xn+7],type:AE};} else if (view[CCW[xn]].color==LPB){if (dOK[CCW[xn+3]]){return { cell:CCW[xn+3],type:AE};} else if (dOK[CCW[xn+5]]){return { cell:CCW[xn+5],type:AE};} else if (dOK[CCW[xn+6]]){return { cell:CCW[xn+6],type:AJM};} else if (dOK[CCW[xn+2]]){return { cell:CCW[xn+2],type:AJM};} else if (dOK[CCW[xn+4]]){return { cell:CCW[xn+4],type:AJM};} else if (dOK[CCW[xn+1]]){return { cell:CCW[xn+1],type:ASF};}}}return NOP;}function rQOSy(){if ((aE[AQ]>0)&&(mF>0)){for (var i=2; i<TN; i++){if (view[CCW[xn+i]].ant&&(view[CCW[xn+i]].ant.type==AQ)&&!view[CCW[xn+i]].ant.friend){var j=(xn&4) ? 1 : -1;if (dOK[CCW[xn+i-j]]){return {cell:CCW[xn+i-j],type:AJM};} else if (dOK[CCW[xn+i+j]]){return {cell:CCW[xn+i+j],type:AJM};} else if (i==5){var i1=5-2*j;var i2=5+2*j;if (dOK[CCW[xn+i1]]&&!(view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend&&
view[CCW[xn+6]].ant&&view[CCW[xn+6]].ant.friend&&
view[CCW[xn+i2]].ant&&view[CCW[xn+i2]].ant.friend)){return {cell:CCW[xn+i1],type:AJM};} else if (dOK[CCW[xn+i2]]&&!(view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend&&
view[CCW[xn+6]].ant&&view[CCW[xn+6]].ant.friend&&
view[CCW[xn+i1]].ant&&view[CCW[xn+i1]].ant.friend)){return {cell:CCW[xn+i2],type:AJM};}} else if ((i==3)&&dOK[CCW[xn+5]]&&!(view[CCW[xn+2]].ant&&view[CCW[xn+2]].ant.friend&&
view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend)){return {cell:CCW[xn+5],type:AJM};} else if ((i==7)&&dOK[CCW[xn+5]]&&!(view[CCW[xn+6]].ant&&view[CCW[xn+6]].ant.friend)){return {cell:CCW[xn+5],type:AJM};}}}} else if ((mF>0)&&(view[CCW[xn+7]].color==LA)&&dOK[CCW[xn+7]]){return {cell:CCW[xn+7],type:AJM};} else if (view[CCW[xn+1]].ant.food>0){if ((mF>0)&&dOK[CCW[xn+2]]){return {cell:CCW[xn+2],type:AJM};}} else if ((aLF[AJM]+aLF[ASM]>0)&&(mF>0)&&(sN[LA]>0)){for (var i=2; i<TN; i++){var c=CCW[xn+i];if (dOK[c]&&(view[c].color==LA)){return {cell:c,type:AJM};}}} else if (eT>0){var bandits=aUE[1]+aUE[2]+aUE[3]+aUE[4];if ((mF>THX)&&((bandits>=2)||((bandits>=1)&&view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend&&(view[CCW[xn+5]].color==LA)&&(view[CCW[xn+7]].ant&&view[CCW[xn+7]].ant.friend&&
(view[CCW[xn+7]].color==LA))||(view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&
(view[CCW[xn+3]].color==LA))))){if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};}}if (mF<RD1){if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};}} else if ((bandits>=1)&&(mF>0)){if ((mF>THX)&&(mF % RM2==RD2+xn/2)){if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};}}for (var i=2; i<TN; i++){var c=CCW[xn+i];var c1,c2;if ((xn==2)||isSc0(view[CCW[xn+1]].color)){c1=CCW[xn+i-1];c2=CCW[xn+i+1];} else if ((xn==6)||isSc1(view[CCW[xn+1]].color)){c1=CCW[xn+i+1];c2=CCW[xn+i-1];} else {break;}if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.food==0)){if (dOK[c1]&&!(view[c2].ant&&view[c2].ant.friend&&view[c2].ant.food>0)){return {cell:c1,type:AJM};} else if (dOK[c2]&&!(view[c1].ant&&view[c1].ant.friend&&view[c1].ant.food>0)){return {cell:c2,type:AJM};}break;}}}}if (!(LCRQCVAL[mC])){return {cell:POSC,color:LCRQC[1]};} else if ((view[CCW[xn]].color==LPX)&&isSc0(view[CCW[xn+1]].color)){if ((mF<=TH0)||(mF % RM1==RD1)){return (rQHTc());} else if (mF<=TH2){if (dOK[CCW[0]]){return {cell:CCW[0],type:AJM};} else {return (rQHTc());}} else {var destCycle=[2,4,6,4,6,2,6,2,4];var destination=destCycle[mF % 9];if (!dOK[CCW[xn+destination]]){destination=destination % 6+2;}if (!dOK[CCW[xn+destination]]){destination=destination % 6+2;}if (!dOK[CCW[xn+destination]]){return (rQHTc());}if (mF<=TH3){if (xn<=2){return {cell:CCW[xn+destination],type:AJM};} else {return (rQHTc());}} else if (mF<=TH4){if (xn<=2){return {cell:CCW[xn+destination],type:((xn>0) ? AJM : ASM)};} else {return (rQHTc());}} else if (mF<=TH5){if (xn==0){return {cell:CCW[xn+destination],type:ASM};} else {return (rQHTc());}} else {return (rQHTc());}}} else {return {cell:POSC,color:incQc(mC)};}return NOP;}function rQLsSy(){if (mF>=TH1){if (mC!=LCLR){return {cell:POSC,color:LCLR};} else {for (var i=0; i<TN; i+=2){if (view[CCW[i]].color!=LCLR){return {cell:CCW[i],color:LCLR};}}}if ((eT==0)&&(fT==1)){return {cell:CCW[xn+3]};}}if ((eT==0)&&(fT==1)){if (view[CCW[xn+2]].food>0){return {cell:CCW[xn+2]};} else if ((view[CCW[xn+3]].food +view[CCW[xn+4]].food>0)&&(view[CCW[xn+1]].color!=LLSF)){return NOP;} else {return {cell:CCW[xn+2]};}} else if (dOK[CCW[xn+2]]&&(dOK[CCW[xn+3]]||(view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend))){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn]]&&dOK[CCW[xn+7]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else {return NOP;}}function rQLvSy(){if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};}return NOP;}function rQCSy(){return NOP;}function rSOSy(){if (!(LCRSCVAL[mC])){return {cell:POSC,color:LCRSC[1]};} else if (isSc0(mC)&&isQc0(view[CCW[xn+1]].color)&&
(view[CCW[xn+3]].color==LPG)){return {cell:CCW[xn+3],color:LPX};} else {return {cell:POSC,color:incSc(mC)};}return NOP;}function rSLSy(){if ((eT==0)&&(fT==1)){if (view[CCW[xn]].food>0){return {cell:CCW[xn]};} else if (view[CCW[xn+7]].food +view[CCW[xn+6]].food>0){return {cell:POSC,color:LLSF};} else {return {cell:CCW[xn]};}} else if ((eT>0)&&view[CCW[xn+2]].ant&&!view[CCW[xn+2]].ant.friend){return {cell:POSC,color:LLSF};} else {if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};}}return NOP;}function rSESy(){if (view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend&&(view[CCW[xn+5]].ant.type==ASF)){if (dOK[CCW[xn]]){return {cell:CCW[xn]};}} else if ((mC==LRR0)&&(view[CCW[xn+5]].color==LG6)){if (dOK[CCW[xn]]){return {cell:CCW[xn]};}} else {if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn]]){return {cell:CCW[xn]};}}return NOP;}function rGSSy(){if (view[CCW[xn]].color!=LCL_QC_RESET){return {cell:CCW[xn],color:LCL_QC_RESET};}if (mC!=LPB){return {cell:POSC,color:LPB};}return (rGGTc());}function rGOSy(){if (aE[AQ]>0){var c=CCW[xn+2];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+1]].ant){return {cell:CCW[xn+1],color:LA};}c=CCW[xn+3];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+1]].ant){return {cell:CCW[xn+1],color:LA};}}if (!LCRPHR[mC]){if ((mC==LPX)&&isSc0(view[CCW[xn+7]].color)){return NOP;} else if (eT==0){return {cell:POSC,color:LPG};}} else if (isSc1(view[CCW[xn+7]].color)&&isQc2(view[CCW[xn]].color)){switch (mC){case LPG:return {cell:POSC,color:LPG1};case LPG1:return {cell:POSC,color:LPG};default:return {cell:POSC,color:LPG};}}if ((eT>0)&&!LCRPHR[mC]&&(xn&4)){return {cell:POSC,color:LPG};} else {return (rGGTc());}}function rLSSy(){if (mC!=LCLR){return {cell:POSC,color:LCLR};}return NOP;}function rEHyS(){if (mQ==1){var ptrn=PTFRL0H;var msm=patC(ptrn,AIMU,0,1);if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (LCRQCVAL[view[CCW[xn+mQ]].color]&&dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};}}return NOP;}function rEBRSy(){if (aF[ASF]>0){return (rELGTc());} else {return (rEBRTc());}}function rEASy(){return NOP;}function rUHSy(){if ((mQ==0)&&(view[CCW[xn]].ant.food<RD1)&&view[CCW[xn+1]].ant&&view[CCW[xn+1]].ant.friend&&(view[CCW[xn+1]].ant.type==ASF)){var cc=[5,6,7,4,2];for (var i=0; i<cc.length; i++){var c=CCW[xn+cc[i]];if (dOK[c]){return {cell:c};}}return NOP;}if ((eT>0)&&(aE[AQ]+aUE[1]+aUE[2] +aUE[3]+aUE[4]>0)){var common;if (mQ==0){common=[1,7];} else {common=[0,2,3,7];}for (var i=0; i<common.length; i++){var c=CCW[xn+common[i]];if (view[c].ant&&!view[c].ant.friend&&((view[c].ant.type==AQ)||(view[c].ant.food==0))){if ((aE[AQ]==0)&&(mC!=LA)){return {cell:POSC,color:LA};} else {return NOP;}}}}if (mQ==0){if (mC!=LRM0){return {cell:POSC,color:LRM0};} else if (view[CCW[xn+3]].color!=LRR0){return {cell:CCW[xn+3],color:LRR0};} else if (view[CCW[xn+7]].color!=LRL0){return {cell:CCW[xn+7],color:LRL0};} else if (view[CCW[xn+5]].color!=LRM1){return {cell:CCW[xn+5],color:LRM1};} else if (view[CCW[xn+6]].color!=LRL1){return {cell:CCW[xn+6],color:LRL1};} else if ((!LCRGRR1[view[CCW[xn+4]].color])&&!(view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend)){return {cell:CCW[xn+4],color:LRR1};}if (LCRQCVAL[view[CCW[xn]].color]||(view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend&&
(view[CCW[xn+5]].ant.food>0))||(view[CCW[xn+6]].ant&&view[CCW[xn+6]].ant.friend&&
(view[CCW[xn+6]].ant.food>0))){if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};}}} else {var ptrn=PTFRL0H;var msm=patC(ptrn,AIMU,0,1);if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (LCRQCVAL[view[CCW[xn+mQ]].color]){if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};}}}return NOP;}function rUBRSy(){if (spcRL1()){return (rULRL1Sy());} else if (spcRL02()){return (rULRL02Sy());} else if (spcRM()){return (rUFCRTc());}for (var i=TN-1; i>=0; i--){if (view[CCW[i+1]].ant&&view[CCW[i+1]].ant.friend&&
(view[CCW[i+1]].ant.type==AE)&&dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;}function rUTRRSy(){return (rUCRTc());}function rULRL1Sy(){var ptrn=PTGRL1;var msm=patC(ptrn,AIMR,0,1);if (xn>=0){if ((view[CCW[xn+6]].color==LMS_WRP)&&(view[CCW[xn+7]].color==LCLR)&&(view[CCW[xn]].color==LMR0)&&(view[CCW[xn+3]].color!=LRM1_WRP)){return {cell:CCW[xn+3],color:LRM1_WRP};} else if ((view[CCW[xn+3]].color!=LRM1_WRP)&&view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else {return NOP;}} else if (spcRM()){return (rUTRRSy());}return (rLostMSy(false));}function rULRL02Sy(){var ptrn;var msm;if (sL[LRM0]>0){ptrn=PTGRL0;msm=patC(ptrn,AIMR,1,1);}if (xn<0){ptrn=PTGRL2;msm=patC(ptrn,AIMR,1,1);}if (xn>=0){if (view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else {return NOP;}} else if (spcRM()){return (rUTRRSy());}return (rLostMSy(false));}function rULRR0Sy(){var ptrn=PTGRR0;var msm=patC(ptrn,AIML,2,1);if (xn>=0){return (runUMLeaveRRTactic());} else if (spcRM()){return (rUTRRSy());}return (rLostMSy(false));}function rULRR2Sy(){var ptrn=PTGRR2;var msm=patC(ptrn,AIML,2,1);if (xn>=0){return (runUMLeaveRRTactic());}    return (rLostMSy(false));}function rUPSSy(){var ptrn=PTGRR1;var msm=patC(ptrn,AIMD,3,2);if (xn>=0){var c=CCW[xn+1];if (view[c].ant&&view[c].ant.friend&&(view[c].ant.food>0)){if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else {return NOP;}}if ((view[CCW[xn+2]].color==LMX_M3OUT)&&(view[CCW[xn]].color==LMR0)&&(view[CCW[xn+1]].color==LCLR)&&(mC!=LRR1X)){return {cell:POSC,color:LRR1X};} else if ((mT==AJM)&&(mC==LRR1U)&&(view[CCW[xn]].color==LMR0)&&(view[CCW[xn+1]].color==LCLR)&&LCRMX_OUT[view[CCW[xn+2]].color]){return {cell:POSC,color:LRR1V};} else if ((mC==LRR1X)||((mT==AJM)&&(mC==LRR1V))||((mC==LRR1U)&&(view[CCW[xn]].color==LMR0)&&(view[CCW[xn+1]].color==LCLR)&&LCRMX_IN[view[CCW[xn+2]].color])){if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}} else if (mC!=LRR1U){return {cell:POSC,color:LRR1U};} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {if (dOK[c]){return {cell:c};} else if (view[c].ant&&view[c].ant.friend){if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}} else {return NOP;}}}return (rLostMSy(false));}function rUESSy(){var ptrn=PTMS0R_IN;var msm=patC(ptrn,AIMD,4,2);if (xn>=0){return (rUESTc(ptrn,msm));}return (rLostMSy(false));}function rUDSSy(){var ptrn;var msm;if ((sL[LML3]>=1)&&(sD[LMR2]+sD[LMR0]>=1)){ptrn=PTMS3;msm=patC(ptrn,AIMD,3,2);}if ((xn<0)&&(sL[LMR2]>=1)&&(sD[LML1]+sD[LML3]>=1)){ptrn=PTMS2;msm=patC(ptrn,AIMD,3,2);}if (xn>=0){if ((msm<0)&&(view[CCW[xn]].color==LRM0)&&(view[CCW[xn+1]].color==LRR0)&&(view[CCW[xn+2]].color==LMR0)){if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else {return NOP;}}return (rUDSTc(ptrn,msm));}if ((xn<0)&&(sL[LML1]>=1)&&(sD[LMR0]+sD[LMR2]>=1)){ptrn=PTMS1_IN;msm=patC(ptrn,AIMD,3,4);if (xn<0){ptrn=PTMS1;msm=patC(ptrn,AIMD,3,2);}}if (xn>=0){return (rUDSTc(ptrn,msm));}if ((sD[LML3]+sL[LMR0]>=2)&&(sD[LRL0] >=2)&&(sD[LML1]==0)){ptrn=PTMS0_WRAPPING;msm=patC(ptrn,AIMD,0,1);if (xn>=0){return (rUWRTc(ptrn,msm));}}if ((sL[LMR0]>=1)&&(sD[LML3]+sD[LML1]>=1)){ptrn=PTMS0;msm=patC(ptrn,AIMD,3,2);if (xn>=0){return (rUDSTc(ptrn,msm));}}if (spcRR1()){ptrn=PTGRR1;msm=patC(ptrn,AIMD,3,2);if (xn>=0){if (mC==LRR1){return {cell:POSC,color:LRR1U};}return NOP;}}if (spcMS0R()){ptrn=PTMS0R_IN;msm=patC(ptrn,AIMD,4,2);if (xn>=0){return (rUESTc(ptrn,msm));}}return (rLostMSy(false));}function rUSWSy(){var ptrn=PTMS0_WRAPPING;var msm=patC(ptrn,AIMD,0,1);if (xn>=0){return (rUWRTc(ptrn,msm));}return (rLostMSy(false));}function rURHSy(){return (rMNGTc());}function rUCRSy(){for (var i=TN; i>=1; i--){if (view[CCW[i]].ant&&dOK[CCW[i-1]]){return {cell:CCW[i-1]};}}return NOP;}function rLLLWSy(){var ptrn;var msm;if (mC==LML1){ptrn=PTMS1FL;msm=patC(ptrn,AIML,0,1);if (xn>=0){return (rLLLWTc());}} else if (mC==LML3){ptrn=PTMS3FL;msm=patC(ptrn,AIML,0,1);if (xn>=0){return (rLLLWTc());}} else if (sL[LML1]+sL[LML3]>=2){ptrn=PTMS0FL;msm=patC(ptrn,AIML,0,1);if (xn<0){ptrn=PTMS2FL;msm=patC(ptrn,AIML,0,1);}if (xn>=0){return (rLLLWTc());}} else if (spcMFR()){return (rLLRWSy());} else if (spcMS()){return (rLASSy());}return (rLostMSy(false));}function rLLRWSy(){var ptrn;var msm;if (mC==LMR0){ptrn=PTMS0FR;msm=patC(ptrn,AIMR,0,1);if (xn>=0){return (rLLRWTc());}} else if (mC==LMR2){ptrn=PTMS2FR;msm=patC(ptrn,AIMR,0,1);if (xn>=0){return (rLLRWTc());}} else if (sL[LMR0]+sL[LMR2]>=2){ptrn=PTMS1FR;msm=patC(ptrn,AIMR,0,1);if (xn<0){ptrn=PTMS3FR;msm=patC(ptrn,AIMR,0,1);}if (xn>=0){return (rLLRWTc());}} else if (spcMS()){return (rLASSy());}return (rLostMSy(false));}function rLASSy(){var ptrn;var msm;if ((sL[LML3]>=1)&&(sD[LMR2]+sD[LMR0]>=1)){ptrn=PTMS3;msm=patC(ptrn,AIMU,3,2);}if ((xn<0)&&(sL[LMR2]>=1)&&(sD[LML1]+sD[LML3]>=1)){ptrn=PTMS2;msm=patC(ptrn,AIMU,3,2);}if ((xn<0)&&(sL[LML1]>=1)&&(sD[LMR0]+sD[LMR2]>=1)){ptrn=PTMS1_IO;msm=patC(ptrn,AIMU,0,1);if (xn<0){ptrn=PTMS1;msm=patC(ptrn,AIMU,3,2);}}if (xn>=0){return (rLASTc(ptrn,msm));}if ((sD[LML3]+sL[LMR0]>=2)&&(sD[LRL0] >=2)&&(sD[LML1]==0)){ptrn=PTMS0_OUT;msm=patC(ptrn,AIMU,0,1);if (xn>=0){return {cell:CCW[xn+3],color:LCLR};}ptrn=PTMS0_WRAPPING;msm=patC(ptrn,AIMD,0,1);if (xn>=0){return (rLWRTc(ptrn,msm));}}if ((sL[LMR0]>=1)&&(sD[LML3]+sD[LML1]>=1)){ptrn=PTMS0;msm=patC(ptrn,AIMU,3,2);if (xn>=0){return (rLASTc(ptrn,msm));}}if (spcRM()){return (rLTRRSy());}return (rLostMSy(false));}function rLLSSy(){var ptrn=PTMS0R_OUT;var msm=patC(ptrn,AIMU,0,1);if (xn>=0){} else {ptrn=PTMS0R_IN;msm=patC(ptrn,AIMU,4,2);if (xn>=0){ptrn=PTMS0R_OUT;msm=patC(ptrn,AIMU,0,1);}}if (xn>=0){return (rLLSTc(ptrn,msm));} else if (spcMS()){return (rLASSy());} else {return (rLostMSy(false));}return NOP;}function rLLVSSy(){var ptrn=PTMS0R_OUT1;var msm=patC(ptrn,AIMU,0,1);if (xn>=0){if (view[CCW[xn+3]].color==LCLR){return {cell:CCW[xn+3],color:((mT==ASM) ? LMX_M2OUT : LMX_M1OUT)};
} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend&&
dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}} else if (spcMS()){return (rLASSy());}return (rLostMSy(false));}function rLDSSy(){var ptrn=PTGRR1;var msm=patC(ptrn,AIMU,1,1);if (xn>=0){if ((view[CCW[xn]].color==LMR0)&&(view[CCW[xn+1]].color==LCLR)){if ((mC==LRR1X)&&(view[CCW[xn+2]].color!=LMX_M3OUT)){return {cell:CCW[xn+2],color:LMX_M3OUT};}if ((view[CCW[xn+2]].color==LMX_M3OUT)&&(mC!=LRR1X)){return {cell:POSC,color:LRR1X};} else if ((LCRMX_OUT[view[CCW[xn+2]].color])&&
(mC!=LRR1V)){return {cell:POSC,color:LRR1V};} else if ((LCRMX_IN[view[CCW[xn+2]].color])&&(mC!=LRR1U)){return {cell:POSC,color:LRR1U};} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}}if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}}return (rLostMSy(false));}function rLTRRSy(){return (rLCRTc());}function rLFRSy(){for (var i=1; i<TN; i+=2){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==AE)&&dOK[CCW[i+2]]){return {cell:CCW[i+2]};}}return NOP;}function rLRHSy(){var ptrn=PTFRL1G;var msm=patC(ptrn,AIMR,0,1);if (xn>=0){return (rLRLTc());}return (rMNGTc());}function rLLRL1Sy(){var ptrn=PTGRL1;var msm=patC(ptrn,AIMR,0,1);if (xn>=0){return (rLRLTc());} else if (spcRM()){return (rLTRRSy());}return (rLostMSy(false));}function rLLRL02Sy(){var ptrn;var msm;if (sL[LRM0]>0){ptrn=PTGRL0;msm=patC(ptrn,AIMR,1,1);}if (xn<0){ptrn=PTGRL2;msm=patC(ptrn,AIMR,1,1);}if (xn>=0){return (rLRLTc());}return (rLostMSy(false));}function rLLRR0Sy(){var ptrn=PTGRR0;var msm=patC(ptrn,AIML,2,1);if (xn>=0){return (rLRRTc());} else if (spcRM()){return (rLTRRSy());}return (rLostMSy(false));}function rLLRR2Sy(){var ptrn=PTGRR2;var msm=patC(ptrn,AIML,2,1);if (xn>=0){return (rLRRTc());} else if (spcRM()){return (rLTRRSy());}return (rLostMSy(false));}function rLCRSy(){for (var i=TN; i>=1; i--){if (!dOK[CCW[i]]&&dOK[CCW[i-1]]){return {cell:CCW[i-1]};}}return NOP;}function rM2R1Sy(){for (var i=0; i<TN; i++){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==ASF)&&view[CCW[i+1]].ant&&view[CCW[i+1]].ant.friend&&
(view[CCW[i+1]].ant.type==ASF)){if (i&1){} else {}if (dOK[CCW[i+7]]){return {cell:CCW[i+7]};} else {return NOP;}}}return (rLostMSy(true));}function rLostMSy(totally){if ((fdT>0)&&(mF==0)){for (var i=0; i<TN; i++){if ((view[CCW[i]].food>0)&&dOK[CCW[i]]){return {cell:CCW[i]};}}}if (totally&(fT==0)){if (((mC==PY)&&(sN[PY]==0))||((mC==PR)&&(sN[PR]==0))){return {cell:POSC,color:PP};}if (((mC==PG)&&(sN[PG]==0))||((mC==PC)&&(sN[PC]==0))||((mC==PB)&&(sN[PB]==0))||((mC==PP)&&(sN[PP]==0))){return {cell:POSC,color:PW};} else if ((sT[PG]==0)&&(((mC==PK)&&(sN[PK]==0))||((mC==PY)&&(sN[PY]==0))||((mC==PR)&&(sN[PR]==0)))){return {cell:POSC,color:PW};} else if ((mC!=PW)&&(sN[mC]>=4)){return {cell:POSC,color:PW};}}if ((mC==PG)&&(sL[PG]>=2)){return {cell:POSC,color:PW};} else if (((mC==PK)||(mC==PR))&&(sN[PK]+sN[PR]>=3)){return {cell:POSC,color:PW};}if (sN[PW]<=4){var preferredColors =[PG,PB,PC,PP,PY,PR,PK];for (var ci=0; ci<preferredColors.length; ci++){var c=preferredColors[ci];if (mC==c){break;}if (sN[c]>0){for (var i=1; i<TN; i++){if ((view[CCW[i]].color==c)&&dOK[CCW[i]]){return {cell:CCW[i]};}}}}}if (RW){for (var i=1; i<TN; i+=2){if (dOK[CCW[i]]){return {cell:CCW[i]};}}for (i=0; i<TN; i+=2){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;} else {return NOP;}}function rDHSy(){if (mQ==0){var c=CCW[xn+2];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+1]].ant){return {cell:CCW[xn+1],color:LA};}c=CCW[xn+6];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+7]].ant){return {cell:CCW[xn+7],color:LA};}} else {var c=CCW[xn+4];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+3]].ant){return {cell:CCW[xn+3],color:LA};}c=CCW[xn+6];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+7]].ant){return {cell:CCW[xn+7],color:LA};}c=CCW[xn+5];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)){if (!view[CCW[xn+3]].ant){return {cell:CCW[xn+3],color:LA};} else if (!view[CCW[xn+7]].ant){return {cell:CCW[xn+7],color:LA};}}}return NOP;}function rQSETc(){if (mC!=LT){return {cell:POSC,color:LT};}for (var i=0; i<TN; i++){if (view[CCW[i]].food>0){return {cell:CCW[i]};}}return NOP;}function rQSSTc(){for (var i=0; i<TN; i++){if ((view[CCW[i]].food>0)&&(dOK[CCW[i]])){return {cell:CCW[i]};}}return NOP;}function rQSTCTc(){if ((mC!=LCLR)&&(sN[mC]>=4)){if (sN[LT]==0){return {cell:POSC,color:LT};} else if (sN[LT]>=3){return {cell:POSC,color:LT};} else {for (var i=0; i<TN; i++){if ((view[CCW[i]].color==LT)&&(view[CCW[i+2]].color!=LT)){return {cell:CCW[i+2],color:LT};}}return NOP;}} else if (sN[LT]==1){for (var i=0; i<TN; i++){if ((view[CCW[i]].color==LT)&&(view[CCW[i+4]].color!=LCLR)){if (view[CCW[i+1]].color==LCLR){return { cell:CCW[i+1]};} else if (view[CCW[i+7]].color==LCLR){return { cell:CCW[i+7]};} else {return {cell:POSC,color:LT};}}}return {cell:POSC,color:LT};} else {return {cell:POSC,color:LT};}return NOP;}function rQSATc(){for (var i=0; i<TN; i++){if ((view[CCW[i]].color==LCLR)&&(view[CCW[i+1]].color==LCLR)&&(view[CCW[i+2]].color==LCLR)){if ((view[CCW[i+3]].color==LCLR)&&(view[CCW[i+4]].color==LCLR)){return {cell:CCW[i+2]};}return {cell:CCW[i+1]};}}for (i=TN-1; i>=0; i--){if (view[CCW[i]].color!=LT){return {cell:CCW[i]};}}for (i=0; i<TN; i++){if (view[CCW[i]].color!=LT){return {cell:CCW[i],color:LCLR};}}return {cell:0,color:LCLR};}function rQSEvTc(){if (sN[LT]>0){for (var i=0; i<TN; i++){if (view[CCW[i]].color==LT){xn=i&6;}}if ( dOK[CCW[xn+7]]&&dOK[CCW[xn]]&&dOK[CCW[xn+1]]&&dOK[CCW[xn+2]]&&dOK[CCW[xn+3]] ){return {cell:CCW[xn+1]};} else if (dOK[CCW[xn+5]]&&dOK[CCW[xn+6]]&&dOK[CCW[xn+7]]&&dOK[CCW[xn]]&&dOK[CCW[xn+1]]){return {cell:CCW[xn+7]};} else if (dOK[CCW[xn+3]]&&dOK[CCW[xn+4]]&&dOK[CCW[xn+5]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+5]]&&dOK[CCW[xn+6]]&&dOK[CCW[xn+7]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+1]]&&dOK[CCW[xn+2]]&&dOK[CCW[xn+3]]){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn+7]]&&dOK[CCW[xn]]&&dOK[CCW[xn+1]]){return {cell:CCW[xn]};} else {for (i=0; i<TN; i++){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;}} else {for (var i=0; i<TN; i++){if (dOK[CCW[i]]&&dOK[CCW[i+1]]&&dOK[CCW[i+2]]&&dOK[CCW[i+3]]&&dOK[CCW[i+4]]){return {cell:CCW[i+2]};}}for (i=0; i<TN; i++){if (dOK[CCW[i]]&&dOK[CCW[i+1]]&&dOK[CCW[i+2]]){return {cell:CCW[i+1]};}}for (i=0; i<TN; i++){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;}return NOP;}function rQHTc(){var ptrn=PTHOME;var msm=patC(ptrn,POSC,0,1);if (msm!=0){var cc=fwdWrong[0];return {cell:cc.v,color:ptrn[cc.p]};} else {return NOP;}}function rGGTc(){var ptrn=PTGARDEN;var msm=patC(ptrn,POSC,0,1);if (msm!=0){var cc=fwdWrong[0];return {cell:cc.v,color:ptrn[cc.p]};} else {return NOP;}}function rUFCRTc(){var ptrn;var msm;if (mC==LRM0){ptrn=PTFRM0;msm=patC(ptrn,AIMU,4,1);if ((xn<0)&&(eT>0)){ptrn=PTFRM1;msm=patC(ptrn,AIMU,4,1);}if ((xn<0)&&(eT>0)){ptrn=PTFRM2;msm=patC(ptrn,AIMU,4,1);}} else if (mC==LRM2){ptrn=PTFRM2;msm=patC(ptrn,AIMU,4,1);if ((xn<0)&&(eT>0)){ptrn=PTFRM0;msm=patC(ptrn,AIMU,4,1);}if ((xn<0)&&(eT>0)){ptrn=PTFRM1;msm=patC(ptrn,AIMU,4,1);}} else if (mC==LRM1){ptrn=PTFRM1;msm=patC(ptrn,AIMU,4,1);if ((xn<0)&&(eT>0)){ptrn=PTFRM2;msm=patC(ptrn,AIMU,4,1);}if ((xn<0)&&(eT>0)){ptrn=PTFRM0;msm=patC(ptrn,AIMU,4,1);}} else if (mC==LRM1_WRP){ptrn=PTGRM1_WRP;msm=patC(ptrn,AIMR,1,1);}if ((xn<0)&&spcRR1()){return (rUPSSy());}if (xn<0){return (rLostMSy(false));}if (msm==0){if (fdL>0){if ((view[CCW[xn+7]].food>0)&&dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else if ((view[CCW[xn+3]].food>0)&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};}}if ((mC==LRM1)&&(view[CCW[xn+3]].color!=LRR1X)&&(view[CCW[xn+3]].color!=LRR1U)&&!((mT==AJM)&&(view[CCW[xn+3]].color==LRR1V))&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};}if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};}return NOP;}for (var i=0; i<TN; i++){var ce=CCW[xn+i];if (view[ce].ant&&view[ce].ant.friend&&(view[ce].ant.type==AE)){if ((2<=i)&&(i<=4)){var msmSaved=msm;var fwdWrongSaved=Array.from(fwdWrong);var rearWrongSaved=Array.from(rearWrong);xn=xn % 4+4;msm=patC(ptrn,POSC,0,1);if (msm<msmSaved){xn=xn % 4+4;msm=msmSaved;fwdWrong=fwdWrongSaved;rearWrong=rearWrongSaved;} else {}break;}}}if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}return NOP;}function rUCRTc(){var ptrn;var msm;if (mC==LRM0){ptrn=PTGRM0;msm=patC(ptrn,AIMU,4,2);if ((xn<0)&&spcRR1()){return (rUPSSy());}if ((xn<0)&&(eT>0)){ptrn=PTGRM1;msm=patC(ptrn,AIMU,4,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM2;msm=patC(ptrn,AIMU,4,2);}} else if (mC==LRM2){ptrn=PTGRM2;msm=patC(ptrn,AIMU,4,2);if ((xn<0)&&(eT>0)){ptrn=PTGRM0;msm=patC(ptrn,AIMU,4,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM1;msm=patC(ptrn,AIMU,4,2);}} else if (mC==LRM1){ptrn=PTGRM1;msm=patC(ptrn,AIMU,4,2);if ((xn<0)&&(eT>0)){ptrn=PTGRM2;msm=patC(ptrn,AIMU,4,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM0;msm=patC(ptrn,AIMU,4,2);}} else if (mC==LRM1_WRP){ptrn=PTGRM1_WRP;msm=patC(ptrn,AIMR,1,1);}if ((xn<0)&&spcRR1()){return (rUPSSy());}if (xn<0){return (rLostMSy(true));}if (msm==0){if (fdL>0){if ((view[CCW[xn+7]].food>0)&&dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else if ((view[CCW[xn+3]].food>0)&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};}}if ((mC==LRM1)&&(view[CCW[xn+3]].color!=LRR1X)&&(view[CCW[xn+3]].color!=LRR1U)){if ((((mT==AJM)&&(view[CCW[xn+3]].color==LRR1))||((mT==ASM)&&(view[CCW[xn+3]].color==LRR1V)))&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};}}if (mC==LRM0&&(view[CCW[xn+5]].color==LRM1_WRP)&&!(view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend)){return {cell:CCW[xn+5],color:LRM1};}var c=CCW[xn+5];if (dOK[c]){return {cell:c};} else if (view[c].ant&&view[c].ant.friend){var evade=false;if (view[c].ant.food>0){evade=true;} else if (view[CCW[xn+1]].ant&&view[CCW[xn+1]].ant.friend&&(view[CCW[xn+1]].ant.food==0)){evade=true;}if (evade){if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else {return NOP;}} else {return NOP;}}} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}return NOP;}function rUESTc(ptrn,msm){switch (view[CCW[xn+3]].color){case LMX_M0:return {cell:CCW[xn+3],color:LMX_M1IN};case LMX_M1OUT:if (mT==ASM){return {cell:CCW[xn+3],color:LMX_M2IN};}break;case LMX_M2OUT:if (mT==ASM){return {cell:CCW[xn+3],color:LMX_M3IN};}break;case LMX_M3OUT:break;case LMX_M1IN:case LMX_M2IN:case LMX_M3IN:if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if ((msm==0)&&dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else {break;}default:break;}if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};}return NOP;}function runUMLeaveRRTactic(){if (view[CCW[xn+7]].ant&&view[CCW[xn+7]].ant.friend&&dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else {return NOP;}}function rUDSTc(ptrn,msm){var c=CCW[xn+1];if ((msm==0)&&(fdL>0)&&(view[CCW[xn+3]].food+view[CCW[xn+7]].food>0)){if (mC!=LMMF){return {cell:POSC,color:LMMF};} else if (view[CCW[xn+5]].color!=LMMH){return {cell:CCW[xn+5],color:LMMH};} else if ((view[CCW[xn+3]].food>0)&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if ((view[CCW[xn+7]].food>0)&&dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};}} else if ((msm<0)&&!(view[c].ant&&view[c].ant.friend)){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (msm>=0){if (dOK[c]){return {cell:c};} else {if (view[c].ant&&view[c].ant.friend){if (view[c].ant.food>0){if ((view[CCW[xn]].color==LCLR)&&dOK[CCW[xn]]){return {cell:CCW[xn]};} else if ((view[CCW[xn+2]].color==LCLR)&&dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else {return NOP;}} else {var c=CCW[xn+5];if (view[c].ant&&view[c].ant.friend&&(view[c].ant.food==0)){if (view[c].color==LMMH){if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else {return NOP;}} else if (mC!=LMMH){return {cell:POSC,color:LMMH};} else {return NOP;}} else {return NOP;}}} else {return NOP;}}}return NOP;}function rUWRTc(ptrn,msm){if (view[CCW[xn+3]].color!=LMS_WRP){return {cell:CCW[xn+3],color:LMS_WRP};} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};}return NOP;}function rLLLWTc(){if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else {return NOP;}}function rLLRWTc(){if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else {return NOP;}}function rLWRTc(ptrn,msm){if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else {return NOP;}}function rLASTc(ptrn,msm){var c=CCW[xn+5];if ((msm<0)&&!(view[c].ant&&view[c].ant.friend)){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (mC==LMMF){return {cell:POSC,color:LCLR};} else if (view[CCW[xn+5]].color==LMMH){return {cell:CCW[xn+5],color:LCLR};} else if (msm>=0){if (dOK[c]){return {cell:c};} else if ((view[c].food>0)&&(eT==0)){if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};}}}return NOP;}function rLLSTc(ptrn,msm){if (msm<0){if (view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend){return NOP;}var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}switch (view[CCW[xn+3]].color){case LMX_M1IN:return {cell:CCW[xn+3],color:LMX_M1OUT};case LMX_M2IN:return {cell:CCW[xn+3],color:LMX_M2OUT};case LMX_M3IN:default:return {cell:CCW[xn+3],color:LMX_M3OUT};case LMX_M1OUT:case LMX_M2OUT:case LMX_M3OUT:if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};}break;}return NOP;}function rLCRTc(){var ptrn;var msm;var trust=(aF[AE]>0 ? 1 : 0);if (mC==LRM0){ptrn=PTGRM0;msm=patC(ptrn,AIMD,3,2-trust);if ((xn<0)&&(eT>0)){ptrn=PTGRM1;msm=patC(ptrn,AIMD,3,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM2B;msm=patC(ptrn,AIMD,3,2);}} else if (mC==LRM2){ptrn=PTGRM2B;msm=patC(ptrn,AIMD,3,2-trust);if ((xn<0)&&(eT>0)){ptrn=PTGRM0;msm=patC(ptrn,AIMD,3,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM1;msm=patC(ptrn,AIMD,3,2);}} else if (mC==LRM1){ptrn=PTGRM1;msm=patC(ptrn,AIMD,3,2-trust);if ((xn<0)&&(eT>0)){ptrn=PTGRM2B;msm=patC(ptrn,AIMD,3,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM0;msm=patC(ptrn,AIMD,3,2);}} else if (mC==LRM1_WRP){ptrn=PTGRM1;msm=patC(ptrn,AIMD,3,2);if (xn>=0){if (view[CCW[xn+3]].color!=LRR1X){return {cell:CCW[xn+3],color:LRR1X};} else if (!(view[CCW[xn+7]].ant&&view[CCW[xn+7]].ant.friend)){return {cell:POSC,color:LRM1};}}}if (xn<0){if (spcRR1()){return (rLDSSy());}return (rLostMSy(true));}if (msm==0){var c=CCW[xn+1];if (dOK[c]){return {cell:c};} else if (view[c].ant&&view[c].ant.friend){var evade=false;if (view[c].ant.food==0){evade=true;} else if (view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend&&(view[CCW[xn+5]].ant.food>0)){evade=true;}if (evade){if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else {return NOP;}} else {return NOP;}}} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}return NOP;}function rLRLTc(){if (view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&
dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else {return NOP;}}function rLRRTc(){if (view[CCW[xn+7]].ant&&view[CCW[xn+7]].ant.friend&&dOK[CCW[xn]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else {return NOP;}}function rMNGTc(){for (var i=0; i<TN; i++){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==ASF)){if (view[CCW[i]].color==LCLR){if (i&1){if ((view[CCW[i+3]].color==LG5)&&dOK[CCW[i+3]]){return {cell:CCW[i+3]};}} else {if ((view[CCW[i+4]].color==LG6)&&dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else if ((view[CCW[i+3]].color==LG5)&&dOK[CCW[i+3]]){return {cell:CCW[i+3]};}}return (rLostMSy(true));} else if (dOK[CCW[i+1]]){return {cell:CCW[i+1]};}}}return NOP;}function rELGTc(){var ptrn=PTFRL1G;var msm;for (var i=0; i<TN; i+=2){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==ASF)){xn=i;break;}}msm=patC(ptrn,AIMU,0,1);if (xn<0){return NOP;} else if ((msm==0)&&dOK[CCW[xn+5]]&&((view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend)||
(view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend))){return {cell:CCW[xn+5]};} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (msm>0){var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {return NOP;}return NOP;}function rEBRTc(){var ptrn;var msm;if (mC==LRL0){for (var i=0; i<TN; i+=2){var c=CCW[i];if ((view[c].color==LRM0)&&view[c].ant&&view[c].ant.friend&&((view[CCW[i]].ant.type==AJM)||(view[CCW[i]].ant.type==ASM))){ptrn=PTFRL2;msm=patC(ptrn,AIMU,1,1);if (xn>=0){break;}}}if (xn<0){ptrn=PTFRL0;msm=patC(ptrn,AIMU,1,1);}if (xn<0){ptrn=PTFRL2;msm=patC(ptrn,AIMU,1,1);}if ((xn<0)&&(eT>0)){ptrn=PTFRL1;msm=patC(ptrn,AIMU,1,1);}if (xn<0){return rECLRETc();}} else if (mC==LRL1){ptrn=PTFRL1;msm=patC(ptrn,AIMU,1,1);if ((xn<0)&&(eT>0)){ptrn=PTFRL2;msm=patC(ptrn,AIMU,1,1);}if ((xn<0)&&(eT>0)){ptrn=PTFRL0;msm=patC(ptrn,AIMU,1,1);}if (xn<0){return rECLRETc();}} else if ((mC==LRR2)&&(sL[LRL1]>=1)&&(sL[LRL0]==0)){return {cell:POSC,color:LRL0};}if ((msm==0)&&dOK[CCW[xn+5]]&&((view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend)||
(view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend))){return {cell:CCW[xn+5]};} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (msm>0){var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {return NOP;}return NOP;}function rECLRETc(){var ptrn;var msm;for (var i=3; i<TN+2; i+=2){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
((view[CCW[i]].ant.type==AJM)||(view[CCW[i]].ant.type==ASM))){xn=i-3;if (mC==LRL0){ptrn=PTFRL0;msm=patC(ptrn,AIMR,1,0.3);if (msm==PTNOM){ptrn=PTFRL2;msm=patC(ptrn,AIMR,1,0.3);}} else if (mC==LRL1){ptrn=PTFRL1;msm=patC(ptrn,AIMR,1,0.3);}if (msm>0){var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}return NOP;}}return NOP;}function patC(ptrn,targetCell,qG,wt){if (xn>=0){return (patCO(ptrn,targetCell,qG,wt,xn));} else {var msm;for (var o=0; o<TN; o+=2){msm=patCO(ptrn,targetCell,qG,wt,o);if (xn>=0){return msm;}}return PTNOM;}}function patCO(ptrn,targetCell,qG,wt,ortn){var fwdFCs=FWD_CELLS[targetCell];var totDscs=0;fwdWrong=[];rearWrong=[];if ((Array.isArray(ptrn[POSC])&&!ptrn[POSC][mC])||
((ptrn[POSC]>0)&&(mC!=ptrn[POSC]))){if (fwdFCs[POSC]){fwdWrong.push({p:POSC,v:POSC});totDscs+=1;} else {rearWrong.push({p:POSC,v:POSC});totDscs+=wt;}}if ((xn<0)&&(totDscs>qG)){return PTNOM;}var jFrom=0;switch (targetCell){case AIMU:jFrom=4;break;case AIML:jFrom=6;break;case AIMR:jFrom=2;break;case AIMD:case POSC:default:break;}for (var j=jFrom; j<TN+jFrom; j++){var posP=CCW[j];var posV=CCW[ortn+j];var c=view[posV].color;if ((Array.isArray(ptrn[posP])&&!ptrn[posP][c])||
((ptrn[posP]>0)&&(c!=ptrn[posP]))){if (fwdFCs[posP]){fwdWrong.push({p:posP,v:posV});totDscs+=1;} else {rearWrong.push({p:posP,v:posV});totDscs+=wt;}}if ((xn<0)&&(totDscs>qG)){return PTNOM;}}if ((xn<0)){xn=ortn;}if (fwdWrong.length==0){return (totDscs);} else {return (-totDscs);}}function isQc0(color){return (LCRQCVAL[color]&&(LCRQC_VALUE[color]==0));
}function isQc2(color){return (LCRQCVAL[color]&&(LCRQC_VALUE[color]==2));
}function incQc(color){if (LCRQCVAL[color]){if (LCRQC_VALUE[color]>=QCPERD){return LCRQC[0];} else {return (LCRQC[(LCRQC_VALUE[color]+1) % QCPERD]);
}} else {return undefined;}}function isSc0(color){return (LCRSCVAL[color]&&(LCRSC_VALUE[color]==0));
}function isSc1(color){return (LCRSCVAL[color]&&(LCRSC_VALUE[color]==1));
}function incSc(color){if (LCRSCVAL[color]){return (LCRSC[(LCRSC_VALUE[color]+1) % SCPERD]);
} else {return undefined;}}function spcMS(){return (((mC==LCLR)||((mF+fdL>0)&&(mC==LMMF))||((mF>0)&&(mC==LMMH)))&&(sN[LMR0]+sN[LML1] +sN[LMR2]+sN[LML3]>=2)&&(sN[LCLR]>=3)&&(sN[LMMF] +sN[LMMH] +sN[PB]<=3)); }function spcRM(){return (LCRGRM_ALL[mC]&&(sT[LRL0]+sT[LRL1]>=3)&&(sN[LRL0]>=1)&&(sN[LRR0]+sN[LRR2]>=2)&&(sT[LRM0]+sT[LRM1_WRP] +sT[LRM1]+sT[LRM2]>=2)&&(sT[LCLR]<=4));}function spcRL1(){return ((mC==LRL1)&&(sL[LRL0]>=2)&&(sD[LRM0]>=1)&&(sL[LRM1_WRP]+sL[LRM1]>=1)&&(sD[LRM2]>=1));}function spcRL02(){return ((mC==LRL0)&&(sL[LRL1]+sL[LRL2]>=2)&&(sN[LRM0]>=1)&&(sD[LRM1_WRP]+sD[LRM1]>=1));}function spcRR0(){return ((mC==LRR0)&&(sL[LRM1]==0)&&(sD[LRM1]+sD[LRM1_WRP]>=1)&&(sL[LMR0]>=1)&&(sL[LRR2]>=1));}function spcRR1(){return (LCRGRR1[mC]&&(sN[LRR0]>=2)&&(sL[LRR2]>=1)&&(sD[LRM0]>=1)&&(sN[LCLR]<=3)&&(sL[LRM1] +sL[LRM1_WRP]>=1));}function spcRR2(){return ((mC==LRR2)&&(sD[LCLR]>=1)&&(sD[LRM0]>=1)&&(sD[LRM1]+sD[LMR0]>=2)&&(sL[LRR0]>=2));}function spcMS0R(){return((mC==LCLR)&&(sL[LMR0]>=1)&&(sL[LRR1U]>=1)&&(sD[LRR2]>=1)&&(sD[LRR0]>=1));}function spcMS0ROut(){return ((mC==LCLR)&&(sL[LMR0]+sL[LRR1V]>=2)&&(sD[LRR2]>=2)&&(sD[LRR0]>=1));}function spcMS0W(){return ((mC==LCLR)&&(sD[LRL0]>=3)&&(sL[LRL1]>=1)&&(sL[LMS_WRP]>=2)&&(sN[LCLR]>=2));}function spcMFL(){return ((sL[LMMF]>=1)&&(sD[LMMH]>=1)&&(sT[LCLR]>=2)&&(sT[LML1]+sT[LML3]>=1));}function spcMFR(){return ((sL[LMMF]>=1)&&(sD[LMMH]>=1)&&(sT[LCLR]>=2)&&(sT[LMR0]+sT[LMR2]>=1));}function fixup(ptrnCell){if (Array.isArray(ptrnCell)){for (var i=1; i<=9; i++){if (ptrnCell[i]){return i;}}return LCLR;} else {return ptrnCell;}}

(На пораду трихоплакса та dzaima - за що велике спасибі! - це було невірно вписано в рамки розмірів PPCG за рахунок читабельності. Редагувати: Недоступний оригінал, достатньо прокоментований та зі значущими назвами змінних та функцій, тепер доступний на GitHub .)

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

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

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

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

Цей головний годинник разом із кількістю прихованої їжі та випадковими орієнтаціями 1-з-4, які ми отримуємо, відриває створення шахтарів від надходження їжі та регулює швидкість, з якою будуть породжуватися подальші шахтарі. Коли королева тільки оселилася, вся їжа, що надходить, швидко перетворюється на більше шахтарів, поки швидкість надходження їжі не досягне приблизно 9 на 1000 ходів (що вимагає близько десятка продуктивних шахтарів). Потім цикл годин стає обмежуючим фактором, і ми починаємо прибирати їжу. Пізніше, коли кількість їжі збільшується, ми зменшуємо швидкість нересту, щонайменше, до 6 нових шахтарів на 1000 рухів, а пізніше - максимум 3, і, нарешті, взагалі припиняємо розростатися. Механізм храповиків заважає королеві не витрачати занадто багато їжі на нерест, коли всі рейки заблоковані або пошкоджені.

Навіть не маючи серйозних збитків, ефективність видобутку значно знизиться в міру розгортання кожної гри. Спочатку ми очікуємо, що шахтар бурить з половиною швидкості світла і повертається зі швидкістю світла, щоб доставляти 1 їжу кожні 1000 ходів в середньому. На кінець це більше схоже на 0,12-0,14 їжі на шахтера на 1000 рухів. Подорож довгими рейками, малювання переважно білого шаблону шахти на вже не переважно білому полотні, а ремонт валів та рейок потребує часу; шахтарі застрягають, або губляться, або зав'язуються у сутичках пейнтболу з супротивниками.

Наші шахтарі намагаються позбутися себе від будь-яких великих пробок.

І є рудиментарна імунна система для боротьби з недоброзичливими зловмисниками.

Намагання парити наш задній двір не рекомендується. Наш персонал не буде розважати.

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

Реалізація розглядає сусідні комірки як пронумеровані проти годинникової стрілки, починаючи з кута, масивом ( CCW) для перекладу цих чисел у viewпідписки контролера . Коли ми маємо потребу (і зможемо) закріпити своє відчуття Півночі, ми встановимо наш компас, базовий підписCCW. Мурашина функція завжди починається з підрахунку її оточення, зокрема, запису спектра (як часто зустрічається кожен колір), а потім розгалужується за типом і ситуацією мурашок уздовж багаторівневого дерева рішень стратегій та тактик. Це робить можливим обробляти безліч дивних спеціальних випадків, в той час як вирішувати найбільш поширені ситуації дуже швидко. Дерево має майже 200 листків, які фарбують клітинку або роблять крок або створюють мурашник, і понад 70, які нічого не роблять - переганяють із 2 ^ 27 можливих кольорових візерунків перегляду, помножених на можливих мурах, з огляду на їжу та їжу несли.

Ось перегляд ASCII геометрії концентратора:

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   | ^ |MR2|   | v |MR2|   | ^ |   |   |   |   |   |   |   |   |
|   |   |   |   | i |   |   | a | r |  rail 2   |   |   |   |   |   |   |   |
+---+---+---+---+-n-+---+---+-c-+---+---+---+---+---+---+---+---+---+---+---+
|   |   |   |ML1|   |   |ML1| a |   |RL0|RM0|RR0|   |   |   |   |   |   |   |
|   |   |   | y | u |   | y | t |   | c | r | g |   |   |   |   |   |   |   |
+---+---+---+---+-s-+---+---+-e-+---+---+---+---+---+---+---+---+---+---+---+
|   |   |   |MX | e |MR0|MX | d |MR0|RL2|RM2|RR2|MX |ML1|   |ML3|   |ML1|   |
|   |   |   | c |   | k | y |   | k | c | g | y | c | y |   | c |   | y |   |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|   |RR1|RR0|RR2|RR1|RR0|RR2|RR1|RR0|RL1|RM1|RR1|   | shaft in use  |  >|   |
|  r|   | g | y | r | g | y | y | g | g | b | r |   |   |   |   |   |   |   |
+--a+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| <i|RM1|RM0|RM2|RM1|RM0|RM2|RM1|RM0|RL0|RM0|RR0|MR0|   |MR2|   |MR0|   |   |
|  l| b | r | g | b | r | g | b | r | c | r | g | k |   | r |   | k |   |   |
+-- +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|  3|RL1|RL0|RL2|RL1|RL0|RL2|RL1|RL0|*Q*|RL0|RL1|RL2|RL0|RL1|RL2|RL0|RL1|   |
|   | g | c | c | g | c | c | g | c |clk| c | g | c | c | g | c | c | g |r  |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+a--+
|   |   |   |   |   |   |   |G3 |Grd|Sec|RM0|RM1|RM2|RM0|RM1|RM2|RM0|RM1|i >|
|   |   |   |   |   |   |   | k |r/y|clk| r | b | g | r | b | g | r | b |l  |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ --+
|   |   |   |   |   |   |   |G4 |G5 |G6 |RR0|RR1|RR2|RR0|RR1|RR2|RR0|RR1|1  |
|   |   |   |   |   |   |   | r | k | b | g | y | y | g | r | y | g |   |   |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |MR0|   |MX |MR0|   |MX |   |   |   |
|   |   |   |   |   |   |   |   |   |   | k |   | y | k |   | c |   |   |   |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

(відображення кольорів MX та RR1 під час та після першого спуску молодшим шахтарем).

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

v1.1 виправляє причину дискваліфікації та додає кілька незначних удосконалень.

v1.2 додає ще кілька виправлень та удосконалень.

v1.3 додає раніше змішані varдекларації, щоб зробити цю роботу на контролерах суворого режиму, таких як Дейв ; відсутність функціональних змін.

v1.4 виправляє непорозуміння між королевою та садівником щодо вживання часнику (як дізнаються вампіри, що приїжджають по залізниці3), виліковує дурну помилку з визначенням шаблону та покращує декілька випадків.

v1.5 вчить Вітрянку новій хитрість - хочете побачити мурашника?

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

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

v1.8 виправляє тупик в логіці фази Lightspeed і, що ще важливіше, виліковує помилку нересту, введеного в v1.6, що призвело до дискваліфікації.

v1.9 виправляє чергову екзотичну помилку, що дискваліфікує, орієнтовно розглядає деякі випадки заторів у валах та намагається боротися з останніми вампірськими винаходами.

v2.0 дозволяє королеві взагалі відмовитися від існуючого хабу при гострій потребі, вдавшись до скремблінгу в стилі Lightspeed, і, незважаючи на удачу, переселиться пізніше, щоб знайти новий млин в іншому місці, далеко від початкового сайту. Експерименти з осцилятором з керованою їжею зі змінною швидкістю, з іншого боку, не призвели до переконливих поліпшень. Раніше надсилання більшої кількості гірників дещо збільшить рівень аварій у деяких конкурентів, але також і наш власний. Код FCO залишається в силі, але наразі був відключений.

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


Причина, чому шахти шахтарів такі, якими вони є, - це внаслідок еластичної гумки: за моїм дизайном дуже малоймовірно, що кожух, який знайде вал, призведе до того, що він повернеться до рейки, тоді як ваш має принаймні 50% шанс провідної гумки до рейки. Я шукав кращого шаблону шахти принаймні півгодини, не знайшовши його ..
dzaima

Так, це вже справа. З іншого боку, Trail-гумки можуть набриднути з рейкою і йти геть уздовж зигзага спину до спини стіни вала ... Мінлива модель більш стійка при бурінні в важкодоступну місцевість (хоча і НЕ непогрішні).
GNiklasch

Гірники на залізниці іноді фактично виграють від гусениць Trail: Це дає можливість шахтарям залізничників повторно досліджувати вали без дійсного наміру. - А рейки та вали можна відремонтувати. Аварії можуть і відбудуться. Важливі варіанти відновлення. MoaR вже вражають!
GNiklasch

1
Я люблю пояснення. Особливо використання одного типу працівників для створення двох різних форм поведінки залежно від місцевого контексту.
трихоплакс

1
@trichoplax Whoops - спасибі за голову! Причина відома (підписка вимкнена двома), див. Випуск №6 . Я використаю привід, щоб перевірити ще пару маленьких виправлень. Оновлять мою відповідь після деякого регресійного тестування та повторного змінення коду.
GNiklasch

12

Чорна діра

var COLOR=8
var COLOR2=7
var COLOR3=2
var LOCKDOWN=8
var orthogonals = [1, 3, 7, 5]
var isQueen = view[4].ant.type==5
var rotationsCW = [1,2,5,8,7,6,3,0]
var rotationsCCW = [3,6,7,8,5,2,1,0]
var matchStates = [
    {state:[0,0,0,
            0,1,0,
            0,0,0],move:0,back:0,fill:true},
    {state:[1,0,0,
            0,1,0,
            0,0,0],move:1,back:3,fill:true},
    {state:[0,1,0,
            1,0,0,
            0,0,0],move:0,back:0,fill:false},
    {state:[0,1,0,
            0,0,1,
            0,0,0],move:2,back:2,fill:false},
    {state:[0,0,1,
            0,0,1,
            0,1,0],move:8,back:2,fill:false},
    //5:
    {state:[1,0,0,
            1,0,0,
            0,1,0],move:2,back:6,fill:false},
    {state:[0,1,0,
            0,1,0,
            0,0,0],move:2,back:0,fill:false},
    {state:[1,0,0,
            1,1,0,
            0,0,0],move:1,back:6,fill:false},
    {state:[0,1,1,
            0,1,0,
            0,0,0],move:5,back:0,fill:false},
    {state:[0,1,0,
            1,1,0,
            0,0,0],move:2,back:6,fill:false},
    //10:
    {state:[1,1,0,
            0,1,0,
            0,0,0],move:2,back:3,fill:false},
    {state:[1,1,0,
            1,1,0,
            0,0,0],move:2,back:6,fill:false},
    {state:[0,1,1,
            0,1,1,
            0,0,0],move:8,back:0,fill:false},
    {state:[1,1,1,
            0,1,0,
            0,0,0],move:5,back:3,fill:false},
    {state:[1,0,0,
            1,1,0,
            1,0,0],move:1,back:7,fill:false},
    //15:
    {state:[0,1,0,
            1,1,1,
            0,0,0],move:8,back:6,fill:false},
    {state:[1,1,1,
            1,1,0,
            0,0,0],move:5,back:6,fill:false},
    {state:[1,0,0,
            1,1,0,
            1,1,0],move:1,back:8,fill:false},
    {state:[1,1,0,
            1,1,0,
            1,0,0],move:2,back:7,fill:false},
    {state:[1,1,1,
            0,1,1,
            0,0,0],move:8,back:3,fill:false},
    //20:
    {state:[1,1,1,
            0,0,1,
            0,0,1],move:7,back:3,fill:true},
    {state:[1,1,1,
            1,0,0,
            1,0,0],move:5,back:7,fill:true},
    {state:[1,0,0,
            1,0,0,
            1,1,1],move:1,back:5,fill:true},
    {state:[0,0,1,
            0,0,1,
            1,1,1],move:3,back:1,fill:true},
    {state:[1,1,1,
            0,1,1,
            0,0,1],move:7,back:3,fill:true},
    //25:
    {state:[1,1,1,
            1,1,0,
            1,0,0],move:5,back:7,fill:true},
    {state:[1,0,0,
            1,1,0,
            1,1,1],move:1,back:5,fill:true},
    {state:[1,1,1,
            1,1,1,
            0,0,0],move:8,back:6,fill:true},
    {state:[1,1,1,
            1,1,1,
            1,0,0],move:8,back:7,fill:true},
    {state:[1,1,1,
            1,1,0,
            1,1,0],move:5,back:8,fill:true},
    {state:[1,1,1,
            1,1,1,
            1,1,0],move:8,back:8,fill:true},
    //30:
    {state:[1,1,1,
            1,1,1,
            1,0,1],move:7,back:7,fill:true},
    {state:[1,1,1,
            1,1,0,
            1,1,1],move:5,back:5,fill:true},
    {state:[1,0,1,
            1,1,1,
            1,1,1],move:1,back:1,fill:true},
    {state:[1,1,1,
            0,1,1,
            1,1,1],move:3,back:3,fill:true},
    {state:[1,1,1,
            1,1,1,
            1,1,1],move:9,back:9,fill:false},
    //35:
]
function matchesColor(c) {
    return c==COLOR || c==COLOR2 || c==COLOR3 || (view[4] == COLOR3 && c == LOCKDOWN)
}
function matchesNonLineColor(c) {
    return c==COLOR || c==COLOR2
}
function isAnyColor(c) {
    var r=0
    for(var i=0;i<9;i++) {
        if(view[i].color == c) r++
    }
    return r
}
function howManyAnts() {
    var r=0;
    for(var i=0;i<9;i++) {
        if(view[i].ant != null) r++
    }
    return r
}
function deRotate(m, amt) {
    if(m == 4 || m < 0 || m > 8 || amt == 0) return m
    if(amt > 0)
        return rotationsCW[(rotationsCW.indexOf(m)+amt)%8]
    amt = -amt
    return rotationsCCW[(rotationsCCW.indexOf(m)+amt)%8]
}
function deRotateSide(m, amt) {
    return deRotate(m,amt*2)
}
function matchWhileLost(sides) {
    var c=0;
    for(var i=0;i<9;i++) {
        if(view[i].color == COLOR3) c++
        if(view[i].color == COLOR3 && i%2 == 0) c+=10
    }
    if(c == 2) {
        if(view[0].color == COLOR3 || view[2].color == COLOR3 || view[6].color == COLOR3 || view[8].color == COLOR3) {
            return {cell:4,color:COLOR3}
        }
        if(view[0].ant == null)
            return {cell:0}
        if(view[2].ant == null)
            return {cell:2}
        if(view[6].ant == null)
            return {cell:6}
        if(view[8].ant == null)
            return {cell:8}
    }
    c = 0
    sides[4] = 0
    var toMatch =[{state:[1,1,1,
                         2,0,2,
                         0,1,0]},
                 {state:[0,2,1,
                         1,0,1,
                         0,2,1]},
                 {state:[0,1,0,
                         2,0,2,
                         1,1,1]},
                 {state:[1,2,0,
                         1,0,1,
                         1,2,0]}]
    for(var m=0;m<4;m++) {
        var score=0
        for(var j=0;j<9;j++) {
            if(j!=4) {
                if(sides[j] == COLOR3 && toMatch[m].state[j] == 1) {
                    score++
                }
                if(sides[j] != COLOR3 && (toMatch[m].state[j] == 0 || toMatch[m].state[j] == 2)) {
                    score++
                }
                if(sides[j] == COLOR3 && toMatch[m].state[j] == 2) {
                    score--
                }
            }
        }
        if(score >= 6) {
            var clearOrder=[1,0,2]
            for(var r=0;r<clearOrder.length;r++) {
                var s = deRotateSide(clearOrder[r],m)
                if(view[s].color == COLOR3) {
                    if(view[s].ant == null)
                        return {cell:s,color:COLOR}
                    else
                        return {cell:4}
                }
            }
        }
    }
    return null
}
function matchBlueStyle(sides) {
    return null
}
function bestMatch(sides) {
    var c=0;
    for(var i=0;i<9;i++) {
        if(sides[i] > 1) c++
    }
    if(!isQueen && view[4].ant.food > 0) {
        c++
        sides[4] = 8
    }
    if(c <= 1) {
        return {state:matchStates[0],rot:0,fill:matchStates[0].fill}
    }
    c = 0
    while(!matchesColor(sides[0]) && !matchesColor(sides[1]) && c < 4) {
        var s2 = [0,0,0,0,0,0,0,0,0]
        s2[0] = sides[2]
        s2[1] = sides[5]
        s2[2] = sides[8]
        s2[3] = sides[1]
        s2[5] = sides[7]
        s2[6] = sides[0]
        s2[7] = sides[3]
        s2[8] = sides[6]
        sides = s2
        c++
    }
    while(c < 8 && (matchesColor(sides[0]) || matchesColor(sides[1])) && matchesColor(sides[8])) {
        var s2 = [0,0,0,0,0,0,0,0,0]
        s2[0] = sides[2]
        s2[1] = sides[5]
        s2[2] = sides[8]
        s2[3] = sides[1]
        s2[5] = sides[7]
        s2[6] = sides[0]
        s2[7] = sides[3]
        s2[8] = sides[6]
        sides = s2
        c++
    }
    var bestState = null
    var bestMatchScore = -1
    for(var i = 0; i < matchStates.length; i++) {
        var score=0
        for(var j=0;j<9;j++) {
            if(j!=4) {
                if(matchesColor(sides[j]) && matchStates[i].state[j] == 1) {
                    score++
                }
                if(!matchesColor(sides[j]) && matchStates[i].state[j] == 0) {
                    score++
                }
            }
        }
        if(score >= bestMatchScore) {
            //console.log("state " + i + ": " + score);
            bestMatchScore = score
            bestState = matchStates[i]
        }
    }
    return {state:bestState,rot:c,fill:bestState.fill,score:bestMatchScore}
}
function getHighestWorker() {
    var r=0;
    for(var i=0;i<9;i++) {
        if(i != 4 && view[i].ant != null) {
            if(view[i].ant.friend && view[i].ant.type > r) r = view[i].ant.type
        }
    }
    return r
}
function pathLost() {
    var i, j
    var safe = []
    for(var q=0;q<9;q++) {
        if(q != 4 && view[q].ant != null && view[q].ant.friend && (view[q].ant.type > view[4].ant.type && view[4].ant.food == 0 && view[q].ant.type < 5)) {
            if(!matchesColor(view[4].color)) return {cell:4,color:COLOR}
            return {cell:4}
        }
    }
    if (matchesNonLineColor(view[4].color)) {
        var myView = [0,0,0,0,0,0,0,0,0]
        for(var i=0; i < 9; i++) {
            myView[i] = view[i].color
            if(!isQueen && view[4].ant.food > 0 && view[i].food > 0) {
                myView[i] = COLOR;
            }
        }
        var ret = matchWhileLost(myView)
        if(ret == null)
            return {cell:4, color:COLOR3}
        else {
            if(!(view[ret.cell].ant != null && view[ret.cell].ant.friend == false) && (view[4].ant.food == 0 || view[ret.cell].food == 0 || isQueen))
                return ret
        }
    }
    for (i=0; i<view.length; i++) {
        if (view[i].ant === null && (view[4].ant.food == 0 || view[i].food == 0 || isQueen)) {
            safe.push(i);
        }
    }
    for (i=0; i<4; i++) {
        j = (i+2) % 4
        if (matchesNonLineColor(view[orthogonals[i]].color) && view[orthogonals[j]].color == COLOR3) {
            if (view[orthogonals[i]].ant == null) {
                return {cell:orthogonals[i]}
            } else if (safe.length > 0) {
                return {cell:safe[0]}
            } else if (view[0].ant === null && (view[4].ant.food == 0 || view[0].food == 0 || isQueen)) {
                return {cell:0}
            }
        }
    }
    if (view[1].ant === null && (view[4].ant.food == 0 || view[1].food == 0 || isQueen)) {
        return {cell:1}
    } else {
        if(!matchesColor(view[4].color)) return {cell:4,color:COLOR}
        return {cell:4}
    }
}
function isAllyAdjacentTo(view, place) {
    var i = deRotate(place, 1)
    var j = deRotate(place, -1)
    if(view[i].ant != null && view[i].ant.friend && view[i].ant.type < 5) return 1
    if(view[j].ant != null && view[j].ant.friend && view[j].ant.type < 5) return 1
    if(orthogonals.indexOf(place) >= 0) {
        i = deRotate(place, 2)
        j = deRotate(place, -2)
        if(view[i].ant != null && view[i].ant.friend && view[i].ant.type < 5) return 2
        if(view[j].ant != null && view[j].ant.friend && view[j].ant.type < 5) return 2
    }
    return 0
}
function findOpenSpace(pos, dir) {
    if(pos > 8 || pos < 0) return pos
    if(view[pos].ant != null && view[pos].ant.friend && view[4].ant.food == 0) {
        pos=deRotate(pos,4)
    }
    //var inc = dir>0?1:-1
    var b = 0
    while(view[pos].ant != null && b < 8) {
        pos=deRotate(pos,dir)
        b++
    }
    return pos
}
//end functions
function getReturn() {
    var colToPlace=COLOR
    var blueAmt = isAnyColor(COLOR2)
    var myView = [0,0,0,0,0,0,0,0,0]
    for(var i=0; i < 9; i++) {
        myView[i] = view[i].color
        if(!isQueen && view[4].ant.food > 0 && view[i].food > 0) {
            myView[i] = COLOR;
        }
        if(!isQueen && view[4].ant.food == 0 && view[i].ant != null && view[i].ant.food > 0) {
            if(!matchesColor(view[4].color)) return {cell:4,color:COLOR}
            return {cell:4};
            //myView[i] = COLOR;
        }
        if(view[i].ant != null && !view[i].ant.friend) {
            myView[i] = COLOR;
        }
    }
    if(isQueen) {
        for(var i=0; i < 9; i++) {
            if(i != 4 && !matchesColor(view[i].color) && view[i].ant != null) {
                myView[i] = COLOR
            }
        }
    }
    //console.log("view:")
    //console.log(myView)
    //console.log("1")
    var match = bestMatch(myView)
    if(match.state.move != 9) {
        var ctY = 0
        var lastY = -1
        var ctW = 0
        var lastW = -1
        for(var i=0; i < 9; i++) {
            if(view[i].color == COLOR3) {
                myView[i] = 8
                ctY++
                lastY = i
            }
            else if(!matchesColor(view[i].color)) {
                ctW++
                lastW = i
            }
        }
        if(ctY > 0 && isQueen && view[4].ant.food > 0 && ctW >= 1) {
            if(view[4].color != COLOR3 && matchesColor(view[4].color))
                return {cell:4,color:COLOR3}
            var tt = deRotate(lastW,-1)
            if(view[tt].color != COLOR2)
                return {cell:tt,color:COLOR2}
            lastW = findOpenSpace(lastW,1)
            return {cell:lastW}
        }
        else if(ctY >= 2 && ctW >= 3)
            match = bestMatch(myView)
        else if(ctY > 0 && view[lastY].ant == null && ctW >= 3) {
            return {cell:lastY,color:1}
        }
    }
    //console.log("2")
    if(!isQueen) {
        for(var i=0; i < 9; i++) {
            if(view[i].ant != null && view[i].ant.type == 5 && view[i].ant.food > 0 && view[i].ant.food <= 2) {
                if(view[4].ant.type == 4)
                    return {cell:4,color:COLOR2}
                return {cell:4}
            }
        }
    }
    //console.log("3")
    if(blueAmt > 0 && view[4].color != COLOR3 && match.state.move != 9) {
        //console.log("Some blue")
        var mb = match.state.back
        mb = deRotateSide(mb,match.rot)
        if(!isQueen || view[4].ant.food <= 2) {
            var a = deRotate(mb,1)
            var b = deRotate(mb,-1)//TODO should be -1
            //console.log("mb: " + mb + "," + a + "," + b)
            if(mb != 9 && (view[mb].color == COLOR2 || view[4].color == COLOR2 || view[a].color == COLOR2 || view[b].color == COLOR2)) {
                //blue behind
                //console.log("Blue behind")
                colToPlace = COLOR2
            }
            else {
                //console.log("No blue behind")
                //console.log(match)
                var myView2 = [0,0,0,0,0,0,0,0,0]
                //construct a view without blue in it
                for(var i=0; i < 9; i++) {
                    myView2[i] = view[i].color == COLOR2?1:view[i].color
                }
                var match2 = bestMatch(myView2)
                if(match2.state.move == 9 || match2.state == matchStates[0]) {
                    //zero or one black
                    //console.log("<= 1 Black")
                    //console.log(myView2)
                    //console.log(match2.state)
                    colToPlace = COLOR2
                }
                else if(view[4].ant.type != 4) {
                    var mf = match2.state.move
                    mf = deRotateSide(mf,match2.rot)
                    //console.log("mf: " + mf)
                    if(mf != 9 && view[mf].color == COLOR2 && view[mf].ant == null) {
                        //about to move onto blue
                        //console.log("Moving onto blue")
                        //console.log(view)
                        //console.log(myView2)
                        return {cell:mf,color:1}
                    }
                    var clearOrder=[1,3,5,7,0,2,6,8]
                    for(var r=0;r<clearOrder.length;r++) {
                        var s = deRotateSide(clearOrder[r],0)
                        if(view[s].color == COLOR2 && (view[s].ant == null || !view[s].ant.friend || (isQueen && view[4].ant.food == 0))) {
                            //console.log("DIE BLUE SCUM")
                            //console.log(view)
                            //console.log(myView2)
                            return {cell:s,color:1}
                        }
                        else if(isQueen && view[s].ant != null && view[s].ant.friend) {
                            //console.log("Blue Queen")
                            //console.log(view)
                            //console.log(myView2)
                            return {cell:4,color:COLOR2}
                        }
                    }
                }
            }
        }
        //console.log("Nothing happened")
    }
    //console.log("4")
    if(view[4].ant.type <= 2) {
      for(var i=0; i < 9; i++) {
        if(view[i].ant != null && !view[i].ant.friend) {
          var canSeeAlly = isAllyAdjacentTo(view,i)
          if(canSeeAlly == 0) {
            if(view[i].color == LOCKDOWN) {
              var a = deRotate(i, 1)
              var b = deRotate(i, -1)
              if(view[a].color != LOCKDOWN) return {cell:a,color:LOCKDOWN}
              if(view[b].color != LOCKDOWN) return {cell:b,color:LOCKDOWN}
              if(orthogonals.indexOf(i) >= 0) {
                a = deRotate(i, 2)
                b = deRotate(i, -2)
                if(view[a].color != LOCKDOWN) return {cell:a,color:LOCKDOWN}
                if(view[b].color != LOCKDOWN) return {cell:b,color:LOCKDOWN}
              }
            }
            else {
              return {cell:i,color:LOCKDOWN}
            }
            if(view[4].color == LOCKDOWN || view[4].color == COLOR) {
              var ii = deRotate(i,4)
              ii = findOpenSpace(ii,1)
              return {cell:ii}
            }
            return {cell:4,color:COLOR}
          }
          else if(canSeeAlly == 2) {
            var m = deRotate(i, 2)
            var j = deRotate(i, -2)
            if(view[m].ant != null && view[m].ant.friend && view[m].ant.type < 5) return {cell:m,color:LOCKDOWN}
            if(view[j].ant != null && view[j].ant.friend && view[j].ant.type < 5) return {cell:j,color:LOCKDOWN}
          }
          else if(view[4].color == LOCKDOWN || view[4].color == 2) {

          }
          else {
            return {cell:4,color:2}
          }
        }
      }
      for(var i=0; i < 9; i++) {
        if(view[i].ant != null && view[i].ant.friend && (view[i].ant.type > view[4].ant.type && view[4].ant.food == 0)) {
          if(match.state.move == 9)
            return {cell:4}
          if(view[i].ant.type == 5)
            return {cell:4}
          var m = findOpenSpace(i,1)
          if(view[m].ant == null)
            return {cell:m}
          return {cell:4,color:2}
        }
      }
    }
    else if(view[4].ant.type <= 4) {
      for(var i=0; i < 9; i++) {
        if(view[i].ant != null && !view[i].ant.friend) {
          var canSeeAlly = isAllyAdjacentTo(view,i)
          if(canSeeAlly == 0) {
            if(view[i].color == LOCKDOWN) {
              var a = deRotate(i, 1)
              var b = deRotate(i, -1)
              if(view[a].color != LOCKDOWN) return {cell:a,color:LOCKDOWN}
              if(view[b].color != LOCKDOWN) return {cell:b,color:LOCKDOWN}
              if(orthogonals.indexOf(i) >= 0) {
                a = deRotate(i, 2)
                b = deRotate(i, -2)
                if(view[a].color != LOCKDOWN) return {cell:a,color:LOCKDOWN}
                if(view[b].color != LOCKDOWN) return {cell:b,color:LOCKDOWN}
              }
            }
            else {
              return {cell:i,color:LOCKDOWN}
            }
            if(view[4].color == LOCKDOWN || view[4].color == COLOR) {
              var ii = deRotate(i,4)
              ii = findOpenSpace(ii,1)
              return {cell:ii}
            }
            return {cell:4,color:COLOR}
          }
          else if(canSeeAlly == 2) {
                var m = deRotate(i, 2)
                j = deRotate(i, -2)
                if(view[m].ant != null && view[i].ant.friend && view[m].ant.type < 5) return {cell:m,color:LOCKDOWN}
                if(view[j].ant != null && view[j].ant.friend && view[j].ant.type < 5) return {cell:j,color:LOCKDOWN}
          }
          else if(view[4].color == LOCKDOWN || view[4].color == 2) {

          }
          else {
            return {cell:4,color:2}
          }
        }
      }
    }
    else if(view[4].ant.food > 4) {
        for(var i=0; i < 9; i++) {
            if(view[i].ant != null && !view[i].ant.friend) {
                var canSeeAlly = isAllyAdjacentTo(view,i)
                if(canSeeAlly == 0) {
                    var m = findOpenSpace(i,1)
                    if(view[m].ant == null)
                        return {cell:m,type:1}
                    return {cell:4,color:3}
                }
            }
        }
        var high = getHighestWorker()
        if(high >= 3 && view[4].ant.food % 2 == 1 && view[4].ant.food < 40) {
            var typeToSpawn = 1
            if(view[4].ant.food < 10 && high == 4 && view[4].ant.food % 4 == 1) {
                typeToSpawn = 3
            }
            var m = findOpenSpace(0,1)
            var canSeeAlly = isAllyAdjacentTo(view,m)
            if(canSeeAlly == 0 && view[m].ant == null)
                return {cell:m,type:typeToSpawn}
        }
    }
    //console.log("5")
    var m = match.state.move
    if(isQueen && view[4].ant.food > 0 && view[4].ant.food <= 2 && isAnyColor(COLOR2) == 0 && isAnyColor(COLOR3) == 0 && m < 9) {
        var high = getHighestWorker()+1
        var num = howManyAnts();
        //high += Math.max(num-2,0)
        if(high < 5) {
            m = deRotate(m,match.rot+4) //get space behind
            m = findOpenSpace(m,1) //make sure its open
            if(view[m].ant == null && view[m].food == 0)
                return {cell:m,type:high}
            return {cell:4}
        }
        else {
            //return {cell:9}
            colToPlace = COLOR2
        }
    }
    if(!isQueen && view[4].ant.food > 0 /*&& view[4].ant.type >= 3*/) {
        //console.log("type 3")
        m = match.state.back
        //console.log(m)
        colToPlace = COLOR
    }
    if(view[4].ant.type == 3) {
        colToPlace = COLOR
    }
    //console.log("6")
    if(!matchesColor(view[4].color) && !(!isQueen && view[4].ant.food)) {
        //console.log("6a")
        /*for(var j=0; j < 9; j++) {
            if(j != 4 && view[j].ant != null && view[j].ant.friend && view[j].ant.food > 0 && j != match.back) {
                m = match.state.move
                if(m < 9) {
                    m = findOpenSpace(m,1)
                    if(view[m].ant == null)
                        return {cell:m}
                    return {cell:4}
                }
                return {cell:4,color:colToPlace}
            }
        }*/
        if(isQueen && view[4].color == LOCKDOWN) {
          m = deRotateSide(m,match.rot)
          m = findOpenSpace(m,1)
          return {cell:m}
        }
        return {cell:4,color:colToPlace}
    }
    if(match.fill && !matchesColor(view[4].color)) {
        return {cell:4,color:colToPlace}
    }
    if(m >= 9) {
        if(!matchesColor(view[4].color)) {
            return {cell:4,color:COLOR}
        }
        //console.log("lost! " + view[4].ant.food);
        //console.log(pathLost());
        return pathLost()
    }
    //console.log("7")
    //console.log("m0: " + m + "+" + match.rot)
    m = deRotateSide(m,match.rot)
    //console.log("m1: " + m)
    m = findOpenSpace(m,1)
    //console.log("m2: " + m)
    if(view[4].ant.food > 0 && !matchesColor(view[4].color) /*&& (view[4].ant.type >= 3)*/) {
        var anyFood = false
        for(var x=0;x<9;x++) {
            if(view[x].food > 0) anyFood = true;
        }
        if(!anyFood)
            return {cell:4,color:colToPlace}
    }
    //console.log("m3: " + m)
    m = findOpenSpace(m,1)
    //console.log("m4: " + m)
    if((!isQueen && view[4].ant.food > 0 && view[m].food > 0) && view[m].ant == null) return {cell:4}
    return {cell:m}
}
var ret = getReturn()
ret = sanityCheck(ret)
return ret
function sanityCheck(ret) {
    if(!ret || ret.cell < 0 || ret.cell > 8 || (ret.cell != 4 && (ret.color == null || ret.color == 0) && view[ret.cell].ant != null) || (view[ret.cell].food > 0 && (view[4].ant.food > 0 && view[4].ant.type < 5))) {
        return {cell:4}
    }
    if(ret.type && (view[ret.cell].ant != null || view[ret.cell].food > 0)) {
        return {cell:4}
    }
    return ret;
}

Це масивна, масивна мурашина функція. Це робиться так:

Чорна діра

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

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

Queeny робить одне, коли вона рухається, кладучи чорне:

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

Робітники типів 1 і 2 діють як королева, поки не знайдуть їжу, потім відмовляються від розміщення кольорів, поки не пройдуться по колу і не дадуть їжі королеві.

Працівники типу 3 і 4 діють як королеви, поки не знайдуть їжу, потім вони працюють по колу (все ще розміщуючи кольори), поки не передадуть свою їжу королеві.

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

Ці налаштування:

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

Приклад кросовер

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

if(view[4].ant.food > 0 && !matchesColor(view[4].color) && (view[4].ant.type >= 3) || surroundingColor > 6) {
  var anyFood = false
  for(var x=0;x<9;x++) {
    if(view[x].food > 0) anyFood = true;
  }
  if(!anyFood)
    return {cell:4,color:colToPlace}
}

Мурахи типу 3 і 4, що йдуть назад, не будуть розміщувати кольори навколо їжі, яка все ще знаходиться на землі (до їжі в іншому випадку поводяться як кольорові плитки для цілей орієнтації шляху). Крім того, мурашки типу 1 або 2, які вважають, що їх затоплено (<= 2 незабарвлених пробілів), додадуть колір. Для маленьких «островів» вони врешті-решт втрачають себе, а не потрапляють у постійну пастку.

Максимальна їжа, яку може отримати ця фракція, обмежується лише тим, наскільки швидко вона перемикає кольори, а також максимальною тривалістю гри (мінімум 10 к). Більше робітників не обов'язково вигідно, але важливо отримати декілька завчасно. Працівники типу 3 і 4 є найбільш ефективними (робити 6 кроків ближче до королеви кожні 6 ігрових кроків), але їх створення занадто рано призводить до зменшення кількості загальної кількості робітників. Тож початкове розміщення має великий вплив, але оскільки область, відображена роєм, постійно зростає з кількома невидимими місцями, вона захопить кожен останній шматок, хоча жоден мурашник не ризикує загубитися, щоб забрати шматок.

Оновлення 7/23

Помітили деякі проблеми у конкретних кращих випадках, як ця:

Королева хоче перейти на ворога

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

Оновлення 7/26

Фокс, я навіть більше не знаю.

  • Налаштоване поводження з помилками-жовтою трасою, щоб бути більш надійними
  • Підвищена керованість зіткненням на шляху друзів-шляхів, щоб бути більш надійною
  • Додано код нейтралізатора Trail Eraser
  • Додано код виявлення та оброблення "блакитних стежок" [BETA]
  • Зроблена королева продовжує виробляти робітників після переходу в режим зберігання (іноді, принаймні)
  • Неправильна санітарія на ході
  • Різний код спагетті
  • Видалено console.log
  • Вилучено геробрін
  • Додано накопичувальний диск

Новий накопичувальний диск

Без жовтих стежок:

Мінус жовтий

Оновлення 7/26 ЧАСТИНА 2

  • Повністю оновив "що я роблю з синім"? код
    • Видалений накопичувальний диск
  • Додано сіль і перець
  • Виправлені проблеми із тим, "з чим я стикаюся?" виявлення
    • Вилучена кругла форма
    • Додана квадратна форма
      • Зараз виглядає нудно
  • Знятий імунітет до Trail-Eraser
  • Додані смарт-коди в код "я загублений", зменшуються застряглі мурахи

Оновлення 7/31

  • Повторно доданий анти-Trail-Eraser код (він загубився в оновлення "видалити синій")
  • Функція перевірки здоровості запобігала фарбуванню клітин під інших мурах
  • Краще виправлення проти гумок: більше не потрібно 3 працівників, щоб протистояти одній гумці

Оновлення 8/4

Незначні налаштування.

  • Колір LOCKDOWN тепер чорний
  • Всі мурахи йдуть «назад», щоб доставити їжу. Ці механіки призведуть до того, що менше мурашок потрапить у пастку в «бульбашки», залишені слідом Еразер
  • Краще поводження з втраченими мурашками, не застрягаючи один одного
  • Знижений поріг "лише приховання" до 40

Слабкі сторони

  • Стирання.
  • Колірне підроблення.

Коментарі не для розширеного обговорення; ця розмова переміщена до чату .
Мартін Ендер

12

Вампір Мк.8 (Re-Vamped)

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


Усі мої відповіді поділяють один і той же набір допоміжних функцій низького рівня. Шукайте "Логіка високого рівня починається тут", щоб побачити код, специфічний для цієї відповіді.

// == Shared low-level helpers for all solutions ==
var QUEEN = 5

var WHITE = 1
var COL_MIN = WHITE
var COL_LIM = 9

var CENTRE = 4

var NOP = {cell: CENTRE}

var DIR_FORWARDS = false
var DIR_REVERSE = true
var SIDE_RIGHT = true
var SIDE_LEFT = false

function sanity_check(movement) {
    var me = view[CENTRE].ant
    if(!movement || (movement.cell|0) !== movement.cell || movement.cell < 0 || movement.cell > 8) {
        return false
    }
    if(movement.type) {
        if(movement.color) {
            return false
        }
        if((movement.type|0) !== movement.type || movement.type < 1 || movement.type > 4) {
            return false
        }
        if(view[movement.cell].ant || view[movement.cell].food) {
            return false
        }
        if(me.type !== QUEEN || me.food < 1) {
            return false
        }
        return true
    }
    if(movement.color) {
        if((movement.color|0) !== movement.color || movement.color < COL_MIN || movement.color >= COL_LIM) {
            return false
        }
        if(view[movement.cell].color === movement.color) {
            return false
        }
        return true
    }
    if(view[movement.cell].ant && movement.cell != 4) {
        return false
    }
    if(view[movement.cell].food + me.food > 1 && me.type !== QUEEN) {
        return false
    }
    return true
}

function as_array(o) {
    if(Array.isArray(o)) {
        return o
    }
    return [o]
}

function best_of(movements) {
    var m
    for(var i = 0; i < movements.length; ++ i) {
        if(typeof(movements[i]) === 'function') {
            m = movements[i]()
        } else {
            m = movements[i]
        }
        if(sanity_check(m)) {
            return m
        }
    }
    return null
}

function play_safe(movement) {
    // Avoid disqualification: no-op if moves are invalid
    return best_of(as_array(movement)) || NOP
}

var RAND_SEED = (() => {
    var s = 0
    for(var i = 0; i < 9; ++ i) {
        s += view[i].color * (i + 1)
        s += view[i].ant ? i * i : 0
        s += view[i].food ? i * i * i : 0
    }
    return s % 29
})()

var ROTATIONS = [
    [0, 1, 2, 3, 4, 5, 6, 7, 8],
    [6, 3, 0, 7, 4, 1, 8, 5, 2],
    [8, 7, 6, 5, 4, 3, 2, 1, 0],
    [2, 5, 8, 1, 4, 7, 0, 3, 6],
]

function areAdjacent(A, B) {
    if(A == 4 || B == 4 || A == B) return true
    if(A % 2 == 0 && B % 2 == 0) return false
    if(A % 2 == 1 && B % 2 == 0) return areAdjacent(B,A)
    if(A % 2 == 1 && B % 2 == 1) return !(8-A == B || 8-B == A)
    if(A == 0 && (B == 1 || B == 3)) return true
    if(A == 2 && (B == 1 || B == 5)) return true
    if(A == 6 && (B == 3 || B == 7)) return true
    if(A == 8 && (B == 5 || B == 7)) return true
    return false
}

function try_all(fns, limit, wrapperFn, checkFn) {
    var m
    fns = as_array(fns)
    for(var i = 0; i < fns.length; ++ i) {
        if(typeof(fns[i]) !== 'function') {
            if(checkFn(m = fns[i])) {
                return m
            }
            continue
        }
        for(var j = 0; j < limit; ++ j) {
            if(checkFn(m = wrapperFn(fns[i], j))) {
                return m
            }
        }
    }
    return null
}

function identify_rotation(testFns) {
    // testFns MUST be functions, not constants
    return try_all(
        testFns,
        4,
        (fn, r) => fn(ROTATIONS[r]) ? ROTATIONS[r] : null,
        (r) => r
    )
}

function near(a, b) {
    return (
        Math.abs(a % 3 - b % 3) < 2 &&
        Math.abs(Math.floor(a / 3) - Math.floor(b / 3)) < 2
    )
}

function try_all_angles(solverFns) {
    return try_all(
        solverFns,
        4,
        (fn, r) => fn(ROTATIONS[r]),
        sanity_check
    )
}

function try_all_cells(solverFns, skipCentre) {
    return try_all(
        solverFns,
        9,
        (fn, i) => ((i === CENTRE && skipCentre) ? null : fn(i)),
        sanity_check
    )
}

function try_all_cells_near(p, solverFns) {
    return try_all(
        solverFns,
        9,
        (fn, i) => ((i !== p && near(p, i)) ? fn(i) : null),
        sanity_check
    )
}

function ant_type_at(i, friend) {
    return (view[i].ant && view[i].ant.friend === friend) ? view[i].ant.type : 0
}

function friend_at(i) {
    return ant_type_at(i, true)
}

function foe_at(i) {
    return ant_type_at(i, false)
}

function foe_near() {
    for(var i = 0; i < 9; ++ i) {
        if(i !== 4 && view[i].ant && !view[i].ant.friend) {
            return true
        }
    }
    return false
}

function ant_type_near(p, friend) {
    for(var i = 0; i < 9; ++ i) {
        if(i !== 4 && ant_type_at(i, friend) && near(i, p)) {
            return true
        }
    }
    return false
}

function move_agent(agents) {
    var me = view[CENTRE].ant
    var buddies = [0, 0, 0, 0, 0, 0]
    for(var i = 0; i < 9; ++ i) {
        ++ buddies[friend_at(i)]
    }

    for(var i = 0; i < agents.length; i += 2) {
        if(agents[i] === me.type) {
            return agents[i+1](me, buddies)
        }
    }
    return null
}

function grab_nearby_food() {
    return try_all_cells((i) => (view[i].food ? {cell: i} : null), true)
}

function go_anywhere() {
    return try_all_cells((i) => ({cell: i}), true)
}

function colours_excluding(cols) {
    var r = []
    for(var i = COL_MIN; i < COL_LIM; ++ i) {
        if(cols.indexOf(i) === -1) {
            r.push(i)
        }
    }
    return r
}

function generate_band(start, width) {
    var r = []
    for(var i = 0; i < width; ++ i) {
        r.push(start + i)
    }
    return r
}

function colour_band(colours) {
    return {
        contains: function(c) {
            return colours.indexOf(c) !== -1
        },
        next: function(c) {
            return colours[(colours.indexOf(c) + 1) % colours.length]
        },
        prev: function(c) {
            return colours[(colours.indexOf(c) + colours.length - 1) % colours.length]
        }
    }
}

function random_colour_band(colours) {
    return {
        contains: function(c) {
            return colours.indexOf(c) !== -1
        },
        next: function() {
            return colours[RAND_SEED % colours.length]
        }
    }
}

function fast_diagonal(colourBand) {
    var m = try_all_angles([
        // Avoid nearby checked areas
        (rot) => {
            if(
                !colourBand.contains(view[rot[0]].color) &&
                colourBand.contains(view[rot[5]].color) &&
                colourBand.contains(view[rot[7]].color)
            ) {
                return {cell: rot[0]}
            }
        },

        // Go in a straight diagonal line if possible
        (rot) => {
            if(
                !colourBand.contains(view[rot[0]].color) &&
                colourBand.contains(view[rot[8]].color)
            ) {
                return {cell: rot[0]}
            }
        },

        // When in doubt, pick randomly but avoid doubling-back
        (rot) => (colourBand.contains(view[rot[0]].color) ? null : {cell: rot[0]}),

        // Double-back when absolutely necessary
        (rot) => ({cell: rot[0]})
    ])

    // Lay a colour track so that we can avoid doubling-back
    // (and mess up our foes as much as possible)
    if(!colourBand.contains(view[CENTRE].color)) {
        var prevCol = m ? view[8-m.cell].color : WHITE

        var colours = [0, 0, 0, 0, 0, 0, 0, 0, 0]
        for(var i = 0; i < 9; ++ i) {
            ++ colours[view[i].color]
        }

        return {cell: CENTRE, color: colourBand.next(prevCol)}
    }

    return m
}

function checkAllNearEnvirons(colours, buddies) {
        var nearMoves = [victims.length]
        for(var e = 0; e < victims.length; e++) {
                var env = victims[e]
                nearMoves[e] = null
                if(env.near_nest(colours)) {
                        nearMoves[e] = env.near_nest_move(colours, buddies)
                }
        }
        return best_of(nearMoves)
}

function follow_edge(obstacleFn, side) {
    // Since we don't know which direction we came from, this can cause us to get
    // stuck on islands, but the random orientation helps to ensure we don't get
    // stuck forever.

    var order = ((side === SIDE_LEFT)
        ? [0, 3, 6, 7, 8, 5, 2, 1, 0]
        : [0, 1, 2, 5, 8, 7, 6, 3, 0]
    )
    return try_all(
        [obstacleFn],
        order.length - 1,
        (fn, i) => (fn(order[i+1]) && !fn(order[i])) ? {cell: order[i]} : null,
        sanity_check
    )
}

function start_dotted_path(colourBand, side, protectedCols) {
    var right = (side === SIDE_RIGHT)
    return try_all_angles([
        (rot) => ((
            !protectedCols.contains(view[rot[right ? 5 : 3]].color) &&
            !colourBand.contains(view[rot[right ? 5 : 3]].color) &&
            !colourBand.contains(view[rot[right ? 2 : 0]].color) &&
            !colourBand.contains(view[rot[1]].color)
        )
            ? {cell: rot[right ? 5 : 3], color: colourBand.next(WHITE)}
            : null)
    ])
}

function lay_dotted_path(colourBand, side, protectedCols) {
    var right = (side === SIDE_RIGHT)
    return try_all_angles([
        (rot) => {
            var ahead = rot[right ? 2 : 0]
            var behind = rot[right ? 8 : 6]
            if(
                colourBand.contains(view[behind].color) &&
                !protectedCols.contains(view[ahead].color) &&
                !colourBand.contains(view[ahead].color) &&
                !colourBand.contains(view[rot[right ? 6 : 8]].color)
            ) {
                return {cell: ahead, color: colourBand.next(view[behind].color)}
            }
        }
    ])
}

function follow_dotted_path(colourBand, side, direction) {
    var forwards = (direction === DIR_REVERSE) ? 7 : 1
    var right = (side === SIDE_RIGHT)

    return try_all_angles([
        // Cell on our side? advance
        (rot) => {
            if(
                colourBand.contains(view[rot[right ? 5 : 3]].color) &&
                // Prevent sticking / trickery
                !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
                !colourBand.contains(view[rot[0]].color) &&
                !colourBand.contains(view[rot[2]].color)
            ) {
                return {cell: rot[forwards]}
            }
        },

        // Cell ahead and behind? advance
        (rot) => {
            var passedCol = view[rot[right ? 8 : 6]].color
            var nextCol = view[rot[right ? 2 : 0]].color
            if(
                colourBand.contains(passedCol) &&
                nextCol === colourBand.next(passedCol) &&

                // Prevent sticking / trickery
                !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
                !colourBand.contains(view[rot[right ? 0 : 2]].color)
            ) {
                return {cell: rot[forwards]}
            }
        }
    ])
}

function escape_dotted_path(colourBand, side, newColourBand) {
    var right = (side === SIDE_RIGHT)
    if(!newColourBand) {
        newColourBand = colourBand
    }

    return try_all_angles([
        // Escape from beside the line
        (rot) => {
            var approachingCol = view[rot[right ? 2 : 0]].color
            if(
                !colourBand.contains(view[rot[right ? 8 : 6]].color) ||
                !colourBand.contains(approachingCol) ||
                colourBand.contains(view[rot[7]].color) ||
                colourBand.contains(view[rot[right ? 6 : 8]].color)
            ) {
                // not oriented, or in a corner
                return null
            }
            return best_of([
                {cell: rot[right ? 0 : 2], color: newColourBand.next(approachingCol)},
                {cell: rot[right ? 3 : 5]},
                {cell: rot[right ? 0 : 2]},
                {cell: rot[right ? 6 : 8]},
                {cell: rot[right ? 2 : 0]},
                {cell: rot[right ? 8 : 6]},
                {cell: rot[right ? 5 : 3]}
            ])
        },

        // Escape from inside the line
        (rot) => {
            if(
                !colourBand.contains(view[rot[7]].color) ||
                !colourBand.contains(view[rot[1]].color) ||
                colourBand.contains(view[CENTRE].color)
            ) {
                return null
            }
            return best_of([
                {cell: rot[3]},
                {cell: rot[5]},
                {cell: rot[0]},
                {cell: rot[2]},
                {cell: rot[6]},
                {cell: rot[8]}
            ])
        }
    ])
}

function latch_to_dotted_path(colourBand, side) {
    var right = (side === SIDE_RIGHT)

    return try_all_angles([
        (rot) => {
            var approachingCol = view[rot[right ? 2 : 0]].color
            if(
                colourBand.contains(approachingCol) &&
                view[rot[right ? 8 : 6]].color === colourBand.next(approachingCol) &&
                !colourBand.contains(view[rot[right ? 5 : 3]].color)
            ) {
                // We're on the wrong side; go inside the line
                return {cell: rot[right ? 5 : 3]}
            }
        },

        // Inside the line? pick a side
        (rot) => {
            var passedCol = view[rot[7]].color
            var approachingCol = view[rot[1]].color
            if(
                !colourBand.contains(passedCol) ||
                !colourBand.contains(approachingCol) ||
                colourBand.contains(view[CENTRE].color)
            ) {
                return null
            }
            if((approachingCol === colourBand.next(passedCol)) === right) {
                return best_of([{cell: rot[3]}, {cell: rot[6]}, {cell: rot[0]}])
            } else {
                return best_of([{cell: rot[5]}, {cell: rot[2]}, {cell: rot[8]}])
            }
        }
    ])
}


// == High-level logic begins here ==


var TARGET_COLOURS_ZIG = colour_band([4, 5, 7, 8])
var TARGET_COLOURS_FIREFLY = colour_band([2, 5, 8])
var GROUND_COLOURS_BH = colour_band([2, 7, 8])
var SAFE_COLOURS = random_colour_band([8])

var THIEF = 1
var BOUNCER = 2
var LANCE = 4
var LANCE_TIP = 3

var INITIAL_GATHER = 12

function colour_band_prev(band, base) {
    if(!band.contains(base)) {
        return band.next(WHITE)
    }
    var cur = band.next(base)
    var c
    while((c = band.next(cur)) !== base) {
        cur = c
    }
    return cur
}

function white_near(p) {
    for(var i = 0; i < 9; ++ i) {
        if(near(i, p) && view[i].color === WHITE) {
            return true
        }
    }
    return false
}

function white_near(p, min) {
    var c = 0
    for(var i = 0; i < 9; ++ i) {
        if(near(i, p) && view[i].color === WHITE) {
            if(++c >= min) return true
        }
    }
    return false
}

var TARGET_ARRANGEMENT_RAIL = [
    [8,4,5,8,5,2,4,2,6],
    [8,5,2,4,2,6,6,4,5],
    [4,2,6,6,4,5,8,4,5],
    [6,4,5,8,4,5,8,5,2]
]
var TARGET_NEAR_RAIL = [
    [2,4,0,5,8,0,4,8,0,1], //Not Valid for Worker #1
    [2,6,0,4,5,0,4,5,0,0],
    [4,6,0,2,4,0,5,8,0,0],
    [4,8,0,4,6,0,2,4,0,0],
    [4,5,0,5,2,0,2,6,0,1], //NV 1
    [4,5,0,4,5,0,5,2,0,5], //NV Q
    [5,2,0,2,6,0,4,5,0,0],
    [5,8,0,4,8,0,4,6,0,5]  //NV Q
]
var TARGET_COLOURS_RAIL = colour_band([4,5,2,4])
var rail_miners = {
    name:function() { return "rail_miners"; },
    near_nest: function(colours) {
        var bestScore = 0
        var enemyQueen = false
        // check every rotation for each 3x3 rail possibility
        TARGET_NEAR_RAIL.forEach(function (arrangement) {
            ROTATIONS.forEach(function (rot){
                var sevenVal = 1
                var score = 0
                for(var i = 0; i < 9; i++) {
                    score += arrangement[i] == view[rot[i]].color?1:0
                    score += (arrangement[i] == 0 && view[rot[i]].color == 7)?sevenVal:0
                    score += (arrangement[i] == 0 && !(view[rot[i]].color == 7 || view[rot[i]].color == 1))?-1:0
                    if(arrangement[i] == 0 && view[rot[i]].color == view[rot[i-2]].color) score -= 2
                    if(view[rot[i]].color) sevenVal = 0
                    enemyQueen |= view[i].ant && view[i].ant.type == QUEEN && !view[i].ant.friend
                    if(view[i].ant != null && view[i].ant.friend && view[i].ant.type == THIEF && view[i].color == WHITE) score++
                }
                if(score > bestScore && arrangement[9] != view[4].ant.type) {
                    bestScore = score
                }
            })
        })
        if(bestScore >= (5 - (enemyQueen && view[4].ant.type == 1?1:0))) {
            if(highway.likely_nest(colours)) return false
            return true
        }
        return false
    },
    worth_leeching: function(myFood, buddies) {
        var numFours = 0
        var foodNeed = 11
        for(var i = 0; i < 9; i++) {
            if(foe_at(i) == 4) numFours++
        }
        if(!buddies[THIEF]) return false
        if(view[4].ant.type != 5 && buddies[QUEEN] && myFood < 500 && myFood+buddies[THIEF] > (foodNeed-numFours*3)) return true
        return myFood < 500 && myFood >= (foodNeed-numFours*3)
    },
    near_nest_move: function(colours, buddies) {
        var victim_pos = -1
        var avoid_pos = -1
        var friend_pos = -1
        for(var i = 0; i < 9; i++) {
            if(foe_at(i) == QUEEN) victim_pos = i
            if(foe_at(i) > 0 && foe_at(i) < 4) avoid_pos = i
            if(friend_at(i) == THIEF) friend_pos = i
        }
        if(victim_pos >= 0) return rail_miners.follow_victim(view[4].ant, buddies, colours, victim_pos)
        if(view[4].ant.type == THIEF && buddies[QUEEN]) return NOP
        if(view[4].ant.type == QUEEN && rail_miners.worth_leeching(view[4].ant.food, buddies)) {
            if(avoid_pos >= 0 && view[4].color != WHITE) {
                return best_of([
                    try_all_angles.bind(null, [
                        (rot) => (friend_at(rot[1]) === THIEF ? {cell: rot[0]} : null),
                        (rot) => (friend_at(rot[0]) === THIEF ? {cell: rot[3]} : null)
                    ]),
                    try_all_angles.bind(null, [
                        (rot) => (friend_at(rot[1]) === THIEF ? {cell: rot[2]} : null),
                        (rot) => (friend_at(rot[0]) === THIEF ? {cell: rot[1]} : null)
                    ]),
                    NOP
                ])
            }
            var allowed = [[8,4,8],[4,6,8],[6,8,4],[5,5,6],[6,5,2],[2,6,5]]
            var curr = [view[4].color,view[friend_pos].color,view[8-friend_pos].color]
            var found = false
            allowed.forEach(function (al) {
                if(al[0] == curr[0] && al[1] == curr[1] && al[2] == curr[2]) {
                    found = true
                }
            })
            if(!found) {
                return best_of([
                    try_all_angles.bind(null, [
                        (rot) => (friend_at(rot[1]) === THIEF && [2,4,5].indexOf(view[rot[2]].color) >= 0 ? {cell: rot[2]} : null),
                        (rot) => (friend_at(rot[1]) === THIEF && [2,4,5].indexOf(view[rot[0]].color) >= 0 ? {cell: rot[0]} : null)
                    ]),
                    NOP
                ])
            }
            return NOP
        }
        return null
    },
    likely_nest: function(colours) {
        var bestScore = 0
        // check every rotation for each 3x3 rail possibility
        var q = 0
        TARGET_ARRANGEMENT_RAIL.forEach(function (arrangement) {
            var j = 0
            ROTATIONS.forEach(function (rot){
                var score = 0
                for(var i = 0; i < 9; i++) {
                    score += arrangement[i] == view[rot[i]].color?1:0
                    if(view[i].ant != null && view[i].ant.friend && view[i].ant.type == THIEF && view[i].color == WHITE) score++
                }
                if(score > bestScore) {
                    bestScore = score
                }
                j++
            })
            q++
        })
        if(view[4].ant.type == THIEF && rail_miners.near_nest(colours)) return true
        if(bestScore >= 7) {
            if(highway.likely_nest(colours)) return false
            return true
        }
        return false
    },

    likely_victim: function(victim_pos) {
        return true
    },

    follow_victim: function(me, buddies, colours, victim_pos) {
        if(me.type == QUEEN) {
            if(victim_pos % 2 == 0) {
                return best_of([
                    try_all_angles.bind(null, [
                        (rot) => (foe_at(rot[0]) === QUEEN && friend_at(rot[5]) == THIEF ? {cell: rot[2]} : null),
                        (rot) => (foe_at(rot[0]) === QUEEN /*&& friend_at(rot[7]) == THIEF*/ ? {cell: rot[6]} : null)
                    ]),
                    NOP
                ])
            }
            else {
                return best_of([
                    try_all_angles.bind(null, [
                        (rot) => (foe_at(rot[1]) === QUEEN && friend_at(rot[2]) == THIEF ? {cell: rot[5], type: THIEF} : null),
                        (rot) => (foe_at(rot[1]) === QUEEN ? {cell: rot[3], type: THIEF} : null),
                        (rot) => (foe_at(rot[1]) === QUEEN ? {cell: rot[5], type: THIEF} : null),
                        (rot) => (buddies[THIEF] < 4 && foe_at(rot[1]) === QUEEN ? {cell: rot[2], type: THIEF} : null),
                        (rot) => (buddies[THIEF] < 4 && foe_at(rot[1]) === QUEEN ? {cell: rot[0], type: THIEF} : null)
                    ]),
                    NOP
                ])
            }
        }
        return NOP
    },
    find_victim: function(me, buddies, colours) {
        var forwardCell = -1
        var current = view[CENTRE].color
        var target = TARGET_COLOURS_RAIL.next(current)
        var antitarget = TARGET_COLOURS_RAIL.prev(current)
        var queenPos = -1
        for(var i = 0; i < 9; i++) {
            if(i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget && current != WHITE){
                forwardCell = i
            }
            if(friend_at(i) == QUEEN) queenPos = i
        }
        if(forwardCell < 0 && current == 4) {
            target = 4
            antitarget = 2
            for(var i = 0; i < 9; i++) {
                if(i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget){
                    forwardCell = i
                }
            }
        }
        if(me.type == QUEEN) {
            var numEn = 0
            for(var i = 0; i < 9; i++) {
                if(i % 2 == 1 && friend_at(i) == THIEF && friend_at(8-i) == THIEF){
                    if(foe_at(deRotate(i,1)) > 0)
                        return {cell:forwardCell}
                    if(foe_at(deRotate(i,-1)) > 0)
                        return {cell:forwardCell}
                    return NOP
                }
                if(i % 2 == 0 && friend_at(i) == THIEF && friend_at(deRotate(i,2)) == THIEF){
                    return {cell:deRotate(i,3), type:THIEF}
                }
            }
            return wilderness.find_victim(me, buddies, colours)
        }
        else if(forwardCell >= 0) {
            if(friend_at(forwardCell) == QUEEN) {
                return best_of([
                    try_all_angles.bind(null, [
                        (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
                        (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null)
                    ]),
                    go_anywhere
                ])
            }
        }
        else if(queenPos>=0 && view[queenPos].color == WHITE && (foe_at(deRotate(queenPos,2)) && foe_at(deRotate(queenPos,-2)))) {
            return wilderness.find_victim(me, buddies, colours)
        }
        if(me.type == THIEF && forwardCell >= 0 && buddies[THIEF] == 1) {
            return wilderness.find_victim(me, buddies, colours)
        }
        return NOP
    }
}

var TARGET_ARRANGEMENT_WIND = [
        [5,4,0,7,6,0,6,4,0],
        [7,6,0,6,4,0,5,4,0],
        [6,4,0,5,4,0,7,6,0]
]
var TARGET_ARRANGEMENT_WINDCENTER = [
        [2,7,6,2,6,4,6,5,4],
        [2,6,4,6,5,4,2,7,6],
        [6,5,4,2,7,6,2,6,4]
]
var WIND_BAND = colour_band([5,6,7])
var windmill = {
    name:function() { return "windmill"; },
    near_nest: function(colours) { return false; },
    near_nest_move: function(colours, buddies) { return null; },
    likely_nest: function(colours) { // Main nest detection
        var bestScore = 0
        // check every rotation for each 3x3 rail possibility
        TARGET_ARRANGEMENT_WIND.forEach(function (arrangement) {
            ROTATIONS.forEach(function (rot){
                var score = 0
                for(var i = 0; i < 9; i++) {
                    score += arrangement[i] == view[rot[i]].color?1:0
                }
                if(score > bestScore) {
                    bestScore = score
                }
            })
        })
        if(bestScore >= 5 && view[4].ant.type != THIEF) {
            return true
        }

        var bestScore = 0
        // check every rotation for each 3x3 rail possibility
        TARGET_ARRANGEMENT_WINDCENTER.forEach(function (arrangement) {
            ROTATIONS.forEach(function (rot){
                var score = 0
                for(var i = 0; i < 9; i++) {
                    score += arrangement[i] == view[rot[i]].color?1:0
                }
                if(score > bestScore) {
                    bestScore = score
                }
            })
        })
        if(bestScore >= 8) {
            return true
        }
        var buddies = [0, 0, 0, 0, 0, 0]
        for(var i = 0; i < 9; ++ i) {
            ++ buddies[friend_at(i)]
        }
        return buddies[LANCE] || buddies[LANCE_TIP]
    },
    worth_leeching: function(myFood, buddies) {
        if(view[4].ant.type == THIEF && (buddies[LANCE] > 0 || buddies[LANCE_TIP] > 0)) return true
        return myFood > 5 || (myFood > 1 && buddies[LANCE])
    },
    likely_victim: function(victim_pos) {
        return false
    },

    follow_victim: function(me, buddies, colours, victim_pos) {
        // nest is chaotic and varies by direction of approach
        // We'll let the Find Victim logic handle this
        return NOP
    },

    find_victim: function(me, buddies, colours) {
        if(me.type == THIEF) {
            var queenPos = -1
            var lancePos = -1
            var tipPos = -1
            for(var i=0;i<9;i++) {
                if(friend_at(i) == QUEEN) queenPos = i
                if(friend_at(i) == LANCE) lancePos = i
                if(friend_at(i) == LANCE_TIP) tipPos = i
            }
            if(queenPos < 0 || (foe_at(deRotate(queenPos,1)) > 0 && foe_at(deRotate(queenPos,2)) > 0)) {
                if(queenPos < 0)
                    return go_anywhere
                return {cell:8-queenPos}
            }
            if(queenPos % 2 == 1 && tipPos % 2 == 0) {
                return go_anywhere
            }
            if(queenPos % 2 == 0 && lancePos % 2 == 1) {
                return go_anywhere
            }
            if(queenPos % 2 == 1 && foe_at(deRotate(queenPos,-2)) > 0) {
                return go_anywhere
            }
            return NOP
        }
        if(buddies[LANCE_TIP]) {
            var lancePos = -1
            for(var i=0;i<9;i++) {
                if(friend_at(i) == LANCE_TIP) {
                    lancePos = i
                }
            }
            if(buddies[LANCE]) {
                if(friend_at(8-lancePos) == LANCE) {
                    if(foe_at(deRotate(8-lancePos,1)) == 1 || foe_at(deRotate(8-lancePos,2)) == 1) {
                        var ret = NOP
                        if(lancePos % 2 == 1)
                            ret = {cell:deRotate(8-lancePos,-2)}
                        if(lancePos % 2 == 0)
                            ret = {cell:deRotate(8-lancePos,-3)}
                        if(!sanity_check(ret)) {
                            ret = best_of([
                                try_all_cells_near(lancePos, (i) => (ant_type_at(i) == 0 && view[i].color == 6 ? {cell: i} : null), true),
                                NOP
                            ])
                        }
                        return ret
                    }
                    if(foe_at(deRotate(lancePos,-2)) > 0) {
                        return {cell:deRotate(lancePos,2)}
                    }
                    return NOP
                }
                if(friend_at(deRotate(lancePos,3)) == LANCE) {
                    if((view[lancePos].color == 2 && view[4].color == 7) || foe_at(8-lancePos)) {
                        return {cell:deRotate(lancePos,1)}
                    }
                    return NOP
                }
                if(view[4].color == 6 && view[lancePos].color == 6 && friend_at(deRotate(lancePos,1)) == LANCE) {
                    if(foe_at(deRotate(lancePos,2)) > 0) {

                        return {cell:8-deRotate(lancePos,2)}
                    }
                    return NOP
                }
                if(view[lancePos].color == 2 && view[deRotate(lancePos,-3)].color == 5 && friend_at(deRotate(lancePos,-3)) == LANCE) {
                    return NOP
                }
                if(lancePos % 2 == 0) {
                    if(foe_at(deRotate(lancePos,-1)) > 0 && lancePos % 2 == 1) return {cell:deRotate(lancePos,2)}
                    if(view[deRotate(lancePos,-1)].color != 5) return {cell:deRotate(lancePos,-1),color:5}
                    if(view[deRotate(lancePos,-1)].color == 3 && view[4].color == 1) return {cell:4,color:3}
                    if(view[deRotate(lancePos,-1)].color == 5 && view[4].color == 3) return {cell:4,color:2}
                    if(view[deRotate(lancePos,-1)].color == 5 && view[4].color == 2) return {cell:4,color:1}
                    if(view[deRotate(lancePos,-1)].color == 5 && view[4].color == 7 && view[deRotate(lancePos,-1)].ant == null) return {cell:deRotate(lancePos,-1),type:THIEF}
                    if(view[deRotate(lancePos,-1)].color == 5 && view[4].color == 7) return {cell:4,color:3}
                }
                return {cell:deRotate(lancePos,-1)}
            }
            if(view[4].color == WHITE && view[lancePos].color == WHITE) {
                return {cell:deRotate(lancePos,-2),type:BOUNCER}
            }
            if(view[deRotate(lancePos,-1)].ant != null && view[deRotate(lancePos,-1)].ant.type == 5) {
                return {cell:deRotate(lancePos,2)}
            }
            if(view[4].color == 6 && view[deRotate(lancePos,1)].color == 7) {
                return {cell:deRotate(lancePos,1)}
            }
            if(foe_at(deRotate(lancePos,-2)) > 0 || foe_at(deRotate(lancePos,-3)) > 0) {
                if(foe_at(deRotate(lancePos,-2)) > 0 && foe_at(deRotate(lancePos,3)) > 0 && (foe_at(deRotate(lancePos,-1)) > 0 || foe_at(deRotate(lancePos,4)) > 0)) {
                    return {cell:deRotate(lancePos,1)}
                }
                if(foe_at(deRotate(lancePos,3)) > 0) {
                    return NOP
                }
                return {cell:deRotate(lancePos,1)}
            }
            if(foe_at(deRotate(lancePos,2)) > 0 && view[deRotate(lancePos,-1)].color != 2) {
                return {cell:deRotate(lancePos,-1),color:2}
            }
            if(foe_at(deRotate(lancePos,-1)) > 0) {
                return {cell:deRotate(lancePos,1)}
            }
            if(lancePos % 2 == 1 && friend_at(deRotate(lancePos,-1)) == THIEF) {
                return {cell:deRotate(lancePos,-2)}
            }
            return {cell:deRotate(lancePos,-1)}
        }
        else if(buddies[LANCE]) {
            var lancePos = -1
            for(var i=0;i<9;i++) {
                if(view[i].ant && view[i].ant.friend && view[i].ant.type == LANCE) {
                    lancePos = i
                }
            }
            if(view[4].color == 3 && lancePos % 2 == 1) return NOP
            var moveNext = lancePos % 2 == 1 ? {cell:deRotate(lancePos,2)} : {cell:deRotate(lancePos,1)}
            if(view[moveNext.cell].ant != null && !view[moveNext.cell].ant.friend) {
                moveNext = {cell:deRotate(lancePos,1),type:LANCE_TIP}
            }
            if(view[lancePos].ant.food > 0) {
                if(lancePos % 2 == 1)
                    return {cell:deRotate(lancePos,4),type:LANCE_TIP}
                else
                    return {cell:deRotate(lancePos,3),type:LANCE_TIP}
            }
            if(view[lancePos].color == 6 && view[moveNext.cell].color == 8 && view[deRotate(lancePos,2)].color == 5) {
                return {cell:moveNext.cell,type:LANCE_TIP}
            }

            return moveNext
        }
        else {
            var current = view[CENTRE].color
            var standOn = WIND_BAND.next(WIND_BAND.next(WIND_BAND.next(current)))
            var target = WIND_BAND.next(current)
            var antitarget = WIND_BAND.next(target)
            if(current != standOn) return wilderness.find_victim(me, buddies, colours)

            var ret = best_of([
                try_all_cells((i) => ((i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget && ([2,5,6].indexOf(view[deRotate(i,-1)].color) >= 0) && (view[i].color != 5 || view[deRotate(i,1)].color == 4)) ? {cell: i, type: LANCE} : null), true),
                NOP
            ])
            if(ret.cell == 4) {
                return wilderness.find_victim(me, buddies, colours)
            }
            return ret
        }
        return NOP
    }
}

var TARGET_ARRANGEMENT_HIGHWAY = [
    [2,3,7,6,8,2,3,7,6],
    [2,3,7,7,6,4,4,2,3],
    [2,4,6,7,3,2,4,6,7],
    [3,2,4,4,6,7,7,3,2],
    [3,4,7,7,2,6,6,3,4],
    [3,4,7,2,6,3,4,7,2],
    [3,6,2,2,7,4,4,3,6],
    [4,7,2,2,5,6,3,4,7],
    [4,6,7,2,6,3,3,4,7],
    [4,6,7,7,3,2,2,4,6],
    [6,4,2,3,7,6,4,2,3],
    [7,3,2,2,4,6,6,7,3],
    [7,4,3,6,2,7,4,3,5]
]
var HIGHWAY_BAND = colour_band([2,7,4,3,6])
var HIGHWAY_BAND2 = colour_band([2,3,7,6,4])

var highway = {
    name:function() { return "highway"; },                                     // For debugging
    near_nest: function(colours) { return false; },                // For dodging enemy workers without getting lost
    near_nest_move: function(colours, buddies) { return null; }, // How to move when near_nest is true
    likely_nest: function(colours) { // Main nest detection
        var bestScore = 0
        // check every rotation for each 3x3 rail possibility
        TARGET_ARRANGEMENT_HIGHWAY.forEach(function (arrangement) {
            ROTATIONS.forEach(function (rot){
                var score = 0
                for(var i = 0; i < 9; i++) {
                    score += arrangement[i] == view[rot[i]].color?1:0
                }
                if(score > bestScore) {
                    bestScore = score
                }
            })
        })
        if(bestScore >= 7) {
            return true
        }
        if(this.isCenter(colours)) return true

        return false
    },         // Main nest detection
    isCenter: function(colours) {
        var bestScore = 0
        ROTATIONS.forEach(function (rot){
            var score = 0
            for(var i = 0; i < 9; i++) {
                if(i >= 3 && i <= 5 && [2,7,4,3,6].indexOf(view[rot[i]].color) >= 0 && (i == 4 || view[rot[i]].color != view[rot[8-i]].color)) {
                    if(i != 4) {
                        score++
                    }
                    else {
                        if(view[rot[3]].color != view[rot[5]].color && view[rot[1]].color == view[rot[7]].color && (view[rot[4]].color != view[rot[1]].color && view[rot[4]].color != view[rot[3]].color && view[rot[4]].color != view[rot[5]].color && view[rot[4]].color != view[rot[7]].color)) {
                            score++
                        }
                    }
                }
                else if(i >= 6) {
                    if(view[rot[i]].color == view[rot[i-6]].color && [2,7,4,3,6].indexOf(view[rot[i]].color) >= 0 && (i == 7 || view[rot[i]].color != view[rot[8-i]].color) && view[rot[i]].color != view[4].color) {
                        score += 2
                    }
                }
            }
            if(score > bestScore) {
                bestScore = score
            }
        })
        if(bestScore >= 7) {
            return true
        }
        return false
    },
    worth_leeching:function(myFood, buddies){ return myFood > 80 && myFood < 500; }, // Is this nest worth leeching?
    likely_victim: function(victim_pos) {
        return true
    },   // Identifying the target queen
    follow_victim: function(me, buddies, colours, victim_pos) {
        if(me.type == QUEEN && buddies[THIEF] < 3) {
            return best_of([
                try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true),
                try_all_cells((i) => ({cell: i, type: THIEF}), true)
            ])
        }
        if(me.type == THIEF && buddies[QUEEN])
            return NOP
        return go_anywhere
    },   // How to handle what happens when the enemy queen is found
    find_victim: function(me, buddies, colours) {
        if(me.type == THIEF && !buddies[QUEEN]) {
            for(var i=0;i<9;i++) {
                if(foe_at(i)) return NOP
            }
            var target = HIGHWAY_BAND.prev(view[4].color)
            var followRail = best_of([
                try_all_cells((i) => (i % 2 == 1 && view[i].color == target) ? {cell:i} : null),
                NOP
            ])
        }
        else {
            var target = HIGHWAY_BAND.next(view[4].color)
            var followRail = best_of([
                try_all_cells((i) => (i % 2 == 1 && view[i].color == target) ? {cell:i} : null),
                NOP
            ])
        }
        return followRail
    }                // How to follow the nest
}

var wilderness = {
    name:function() { return "wilderness"; },
    near_nest: function(colours) { return false; },
    near_nest_move: function(colours, buddies) { return null; },
    likely_nest: function(colours) {
        return true
    },
    worth_leeching: function(myFood, buddies) {
        return true
    },
    likely_victim: function(victim_pos) {
        return true
    },

    follow_victim: function(me, buddies, colours, victim_pos) {
        // We stumbled across a random queen; make the most of it
        // TODO
        if(rail_miners.near_nest(colours)) {
            return rail_miners.follow_victim(me, buddies, colours, victim_pos)
        }

        // avoids blocking off the rail miner queen from her workers
        // (we'd like to leech her again)
        if(me.type === QUEEN && !buddies[THIEF] && me.food > 0) {

            // Make a buddy to help us steal
            return best_of([
                try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true),
                try_all_cells((i) => ({cell: i, type: THIEF}), true)
            ])
        }
        else if(me.type === QUEEN){
            var enemyCount = 0
            var allyPos = -1
            for(var a=0; a<9; a++) {
                if(a != 4 && view[a].ant != null) {
                    if(view[a].ant.friend) {
                        if(near(a,victim_pos)){
                            allyPos = a
                        }
                    }
                    else if(view[a].ant.type != 5) {
                        enemyCount++
                    }
                }
            }
            if(enemyCount >= buddies[THIEF] && allyPos >= 0) {
                //if next to the queen and we're outnumbered, move back to the center of the rail.
                var target = TARGET_COLOURS_RAIL.prev(view[allyPos].color)
                var best = best_of([
                    try_all_cells((i) => (near(i, victim_pos) && i % 2 == 0 ? {cell: i, type: THIEF} : null), true),
                    try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true)
                ])
                if(best != null) return best

                best_of([
                    try_all_cells((i) => ((view[i].color == target && i != 4 && areAdjacent(i,a)) ? {cell: i} : null))
                ])
                if(best != null) return best

                return best_of([
                    {cell:deRotate(allyPos,1)},
                    {cell:deRotate(allyPos,-1)}
                ])
            }
        }

        return NOP
    },
    find_victim: function(me, buddies, colours) {
        if(me.type === QUEEN) {
            var in_void = true
            for(var i = 0; i < 9; ++ i) {
                if(view[i].color !== WHITE && !SAFE_COLOURS.contains(view[i].color)) {
                    in_void = false
                    break
                }
            }
            if(!in_void) {
                // because of avoiding returning Miner on a Rail workers
                // we dodge sideways and this takes us back onto track
                var nearMove = checkAllNearEnvirons(colours, buddies)
                if(nearMove) return nearMove
            }
            return best_of([
                // Make a buddy once we have a reasonable stash of food so we can
                // search the board faster
                // (but avoid making buddies when there's a potential nest nearby
                // better to wait until we find their queen)
                (!buddies[THIEF] && me.food >= INITIAL_GATHER && in_void) &&
                    try_all_cells((i) => ({cell: i, type: THIEF}), true),

                // Follow buddy in search of victims
                buddies[THIEF] && try_all_angles.bind(null, [
                    (rot) => (friend_at(rot[1]) === THIEF ? {cell: rot[2]} : null),
                    (rot) => (friend_at(rot[0]) === THIEF ? {cell: rot[1]} : null)
                ]),
                buddies[THIEF] && try_all_angles.bind(null, [
                    (rot) => (friend_at(rot[1]) === THIEF ? {cell: rot[0]} : null),
                    (rot) => (friend_at(rot[0]) === THIEF ? {cell: rot[3]} : null)
                ]),
                buddies[THIEF] && NOP, // Don't lose our buddy!

                // Random walk until we can make a buddy or find the victim
                grab_nearby_food,
                foe_near() ? go_anywhere : fast_diagonal.bind(null, SAFE_COLOURS),
                go_anywhere
            ])
        } else if(me.type === THIEF) {
            return best_of([
                // Lost the queen! Random walk because we have nothing better to do.
                // (don't leave lines; they could disrupt the pattern)
                !buddies[QUEEN] && go_anywhere,
                buddies[BOUNCER] && go_anywhere,
                buddies[THIEF] > 1 && go_anywhere, //untested
                // Follow queen in search of victims
                try_all_angles.bind(null, [
                    (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
                    (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null)
                ]),
                NOP // Don't lose our buddy!
            ])
        }
    }
}

var victims = [highway, rail_miners, windmill]

function guess_environment(colours, buddies) {
    var food = view[4].ant.food
    if(view[4].ant.type !== QUEEN) {
        for(var i = 0; i < 9; i++) {
            if(i != 4 && view[i].ant && view[i].ant.friend && view[i].ant.type === QUEEN) {
                food = view[i].ant.food
            }
        }
    }
    for(var i = 0; i < victims.length; ++ i) {
        if(victims[i].likely_nest(colours) && victims[i].worth_leeching(food, buddies)) {
            return victims[i]
        }
    }

    return wilderness
}

function is_safe(i) {
    var nearThief = false
    var nearOfficer = false
    for(var j = 0; j < 9; ++ j) {
        if(friend_at(j) === THIEF) {
            nearThief = true
        }
        if(foe_at(j) && foe_at(j) !== QUEEN) {
            nearOfficer = true
        }
    }
    return nearThief && !nearOfficer
}

function move(me, buddies) {
    var colours = [0, 0, 0, 0, 0, 0, 0, 0, 0]
    for(var i = 0; i < 9; ++ i) {
        ++ colours[view[i].color]
    }
    var env = guess_environment(colours,buddies)
    var victim_pos = -1
    var queen_pos = -1
    for(var i = 0; i < 9; ++ i) {
        if(foe_at(i) === QUEEN && env.likely_victim(i) && view[i].ant.food > 0) {
            victim_pos = i
            if(view[i].ant.food > 0) {
                break
            }
        }
        if(friend_at(i) === QUEEN) {
            queen_pos = i
        }
    }

    var in_void = true
    for(var i = 0; i < 9; ++ i) {
        if(view[i].color !== WHITE || (i != 4 && me.type === BOUNCER && friend_at(i) === BOUNCER)) {
            in_void = false
            break
        }
    }
    if(me.type === BOUNCER) {
        if(env === wilderness && in_void) {
            // Our work is done; leave queen and wander at random
            if(buddies[QUEEN]) {
                return best_of([
                    try_all_cells((i) => (ant_type_near(i, true) ? null : {cell: i}), true),
                    go_anywhere
                ])
            }
            return NOP
        }
        else if(env === rail_miners) {
            // Our work is done; leave queen and wander at random
            if(buddies[QUEEN]) {
                var allAngles = try_all_angles.bind(null, [
                    (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
                    (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null),
                    NOP
                ])
                return best_of([
                    //if next to an enemy queen, move out of the way
                    try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:9-i} : null), true),
                    try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:7-i} : null), true),
                    allAngles
                ])
            }
            return NOP
        } else if(buddies[QUEEN]) {
            // Escort queen out of nest
            var allAngles = try_all_angles.bind(null, [
                (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
                (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null),
                NOP
            ])

            return best_of([
                //if next to an enemy queen, move out of the way
                try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:9-i} : null), true),
                try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:7-i} : null), true),
                allAngles
            ])
        }
        else {
            return go_anywhere
        }
    } else if(buddies[BOUNCER]) {
        if(me.type === QUEEN) {
            // Be escorted out of nest
            return try_all_angles.bind(null, [
                (rot) => (friend_at(rot[1]) === BOUNCER ? {cell: rot[2]} : null),
                (rot) => (friend_at(rot[0]) === BOUNCER ? {cell: rot[1]} : null),
                go_anywhere,
                NOP
            ])
        } else {
            // Get out of the way
            return try_all_angles.bind(null, [
                (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[7]} : null),
                (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[8]} : null),
                (rot) => (friend_at(rot[1]) === BOUNCER ? {cell: rot[7]} : null),
                (rot) => (friend_at(rot[0]) === BOUNCER ? {cell: rot[8]} : null),
                go_anywhere
            ])
        }
    }
    if(victim_pos !== -1) {
        // abandon the queen if she's dry.
        // abandon rail miner's queen so she has at least 10 food (otherwise she produces workers 3:4 food she aquires)
        // value is higher than 10 because there's two to three rounds of theft (at 4 ants each) before the queen gets out of range
        // this can still leave the rail miner's queen lower than 10, but unlikely
        // other queens are abandoned if they have less than 5 food, due to the "max 4 ants stealing" and at 0 food, she's not a target.
        if(view[victim_pos].ant.food < 5 || (env == rail_miners && view[victim_pos].ant.food < 28)) {
            if(me.type == THIEF) {
                if(rail_miners.near_nest(colours)) {
                    // we'd rather reuse the workers
                    return NOP
                }
            }
            // Victim is out of food; bounce out of nest
            if(env == rail_miners) {
                if(me.type == QUEEN && me.food < 300 && !buddies[BOUNCER]) {
                    if(friend_at(deRotate(victim_pos,2)) == THIEF && foe_at(deRotate(victim_pos,3)) == 0) return {cell:deRotate(victim_pos,3),type:BOUNCER}
                    if(friend_at(deRotate(victim_pos,-2)) == THIEF && foe_at(deRotate(victim_pos,-3)) == 0) return {cell:deRotate(victim_pos,-3),type:BOUNCER}
                }
                // murder SlM
                return NOP
            }
            var m = try_all_cells((i) => ({cell: i, type: BOUNCER}), true)
            if(m) {
                return m
            }
        }
        if(me.type === QUEEN && buddies[THIEF] && !is_safe(CENTRE)) {
            // Try to avoid getting food stolen back from us
            var m = try_all_cells((i) => (is_safe(i) ? {cell: i} : null), true)
            if(m) {
                return m
            }
        }
        return env.follow_victim(me, buddies, colours, victim_pos)
    } else {
        return env.find_victim(me, buddies, colours)
    }
}

// LANCE is only used by windmill targetting, easier to break this out as its own method
function moveLance(me, buddies) {
    var queenPos = -1
    var tipPos = -1
    var enQueenPos = -1
    if(buddies[BOUNCER]) {
        for(var i=0;i<9;i++) {
            if(friend_at(i) == BOUNCER) {
                return {cell:8-i}
            }
        }
    }
    for(var i=0;i<9;i++) {
        if(friend_at(i) == QUEEN) {
            queenPos = i
        }
        if(friend_at(i) == LANCE_TIP) {
            tipPos = i
        }
        if(foe_at(i) == QUEEN) enQueenPos = i
    }
    if(!buddies[QUEEN]) {
        for(var i=0;i<9;i++) {
            if(i % 2 == 0 && friend_at(i) == QUEEN) {
                if(view[deRotate(i,3)].ant != null && view[deRotate(i,3)].ant.friend && view[deRotate(i,3)].ant.type == LANCE_TIP) return NOP
                return {cell:deRotate(i,1)}
            }
        }
        if(!buddies[LANCE_TIP] && !buddies[THIEF] && view[4].color == 2) {
            for(var i = 0; i < 9; ++ i) {
                if(view[i].color == 1) return {cell:i}
            }
        }
        if(enQueenPos >= 0 && enQueenPos % 2 == 0 && foe_at(deRotate(enQueenPos,1)) == 1) {
            return {cell:deRotate(enQueenPos,-3)}
        }
        if(enQueenPos >= 0 && enQueenPos % 2 == 1 && foe_at(deRotate(enQueenPos,2)) == 1) {
            return {cell:8-enQueenPos}
        }
        if(enQueenPos >= 0 && (me.food > 0 || foe_at(deRotate(enQueenPos,-1)) || foe_at(deRotate(enQueenPos,3)))) {
            if(enQueenPos % 2 == 0 && (foe_at(deRotate(enQueenPos,4)) || friend_at(deRotate(enQueenPos,4)) == THIEF)) {
                return {cell:deRotate(enQueenPos,-3)}
            }
        }
        return NOP
    }
    if(buddies[LANCE_TIP]) {
        if(deRotate(queenPos,-1) == tipPos && view[tipPos].color == 8) return {cell:8-tipPos}
        if(deRotate(queenPos,-1) == tipPos) return try_all_cells((i) => (areAdjacent(i,tipPos) && view[i].color == 5 ? {cell:i} : null))
        if(foe_at(8-tipPos) == QUEEN) return {cell:8-tipPos,color:6}
        if(foe_at(8-queenPos) > 0 || foe_at(deRotate(8-queenPos,1)) > 0) return NOP
        return try_all_cells((i) => (!areAdjacent(i,queenPos) && !areAdjacent(i,tipPos) ? {cell:i} : null))
    }
    if(view[4].color != 4 && view[4].color != 6) {
        if(foe_at(8-queenPos) == QUEEN) {
            var formation = try_all_angles.bind(null, [
                (rot) => (foe_at(rot[1]) === 1 && foe_at(rot[2]) === QUEEN ? {cell: rot[3]} : null),
                (rot) => (foe_at(rot[1]) === 1 && foe_at(rot[0]) === QUEEN ? {cell: rot[7]} : null),
                (rot) => (foe_at(rot[1]) === 1 && view[rot[1]].ant.food > 0 && foe_at(rot[6]) === QUEEN && friend_at(rot[2]) === QUEEN ? {cell: rot[5]} : null),
            ])()
            if(formation != null) {
                return formation
            }
            return NOP
        }
        if(foe_at(deRotate(queenPos,1)) > 0 && foe_at(deRotate(queenPos,-1)) > 0) {
            return {cell:deRotate(queenPos,-3)}
        }
        return best_of([
            try_all_cells((i) => (enQueenPos ==-1 && i % 2 == 1 && (view[i].color == 4 || view[i].color == 6) && view[deRotate(i,1)].color != 2 && view[deRotate(i,-1)].color != 2 && areAdjacent(i,queenPos) ? {cell: i} : null), true),
            ((view[4].color != 6 || view[4].color != 4) && queenPos % 2 == 0 && view[deRotate(queenPos,-3)].color == 5) ? {cell:4,color:6} : null,
            NOP
        ])
    }
    else {
        var queenOn = view[8-queenPos].color
        var target = WIND_BAND.next(queenOn)
        var prior = WIND_BAND.next(target)
        var followRail = best_of([
            try_all_cells((i) => (view[deRotate(i,-3)].color == prior && view[deRotate(i,-1)].color == target && areAdjacent(i,queenPos) && (view[i].color == 4 || view[i].color == 6) ? {cell: i} : null), true),
            queenPos % 2 == 1 ? (view[queenPos].color == 4 || view[4].color == 4 ? NOP : {cell:deRotate(queenPos,-2)}) : (view[queenPos].color == 4 || view[queenPos].color == 6 ? {cell:deRotate(queenPos,-1)} : NOP)
        ])

        if(view[deRotate(queenPos,-1)].ant != null) {
            if(!view[deRotate(queenPos,-1)].ant.friend && view[deRotate(queenPos,-2)].ant != null && !view[deRotate(queenPos,-2)].ant.friend) {
                return NOP
            }
            if(queenPos % 2 == 0 && !view[deRotate(queenPos,-1)].ant.friend && view[deRotate(queenPos,-2)].ant == null && (view[queenPos].color == 3 || view[queenPos].color == WHITE)) {
                return {cell:deRotate(queenPos,-3)}
            }
            if(queenPos % 2 == 0 && friend_at(deRotate(queenPos,-1)) == THIEF && view[deRotate(queenPos,-2)].ant == null && (view[queenPos].color == 3 || view[queenPos].color == WHITE)) {
                return {cell:deRotate(queenPos,-3)}
            }
            return NOP
        }
        if(me.food > 0 && queenPos % 2 == 0) {
            return {cell:deRotate(queenPos,-1)}
        }
        if(foe_at(deRotate(queenPos,-3)) > 0 && (view[queenPos].color == 1 || view[deRotate(queenPos,1)].color == 1 || view[deRotate(queenPos,-1)].color == 1)) {
            if(view[queenPos].color == 3) {
                return followRail
            }
            if(view[queenPos].color == 7) return NOP
            return {cell:queenPos,color:3}
        }
        if((foe_at(deRotate(queenPos,-2)) > 0 || foe_at(deRotate(queenPos,-3)) > 0) && queenPos % 2 == 0 && view[deRotate(queenPos,-1)].color == 5) {
            if(view[queenPos].color == 7) return NOP
            return {cell:queenPos,color:3}
        }
        if(view[deRotate(queenPos,-4)].ant != null && !view[deRotate(queenPos,-4)].ant.friend && (view[queenPos].color == 1 || view[deRotate(queenPos,1)].color == 1 || view[deRotate(queenPos,-1)].color == 1)) {
            if(view[queenPos].color == 7) return NOP
            return {cell:queenPos,color:3}
        }
        if((followRail == null || followRail.cell == 4) && foe_at(deRotate(queenPos,-2)) == 1) {
            if(view[queenPos].color == 7) return NOP
            return {cell:queenPos,color:3}
        }
        if(followRail != null && followRail.cell != 4 && view[followRail.cell].color == 6 && view[deRotate(followRail.cell,1)].color == 6) {
            followRail = {cell:deRotate(followRail.cell,1)}
        }
        return followRail
    }
    return NOP
}

// LANCE_TIP never needs to move
// Unfortunately, reusing an existing worker type for this purpose is not easily possible.
// Used against Sliding Miners as a stationary blocker to prevent the queen slipping past.
function moveTip(me, buddies) {
    var queenPos = -1
    var in_void = true
    for(var i=0;i<9;i++) {
        if(friend_at(i) == QUEEN) {
            queenPos = i
        }
        if(view[i].color != WHITE) {
            in_void = false
        }
    }
    var colours = [0, 0, 0, 0, 0, 0, 0, 0, 0]
    var enemies = 0
    for(var i = 0; i < 9; ++ i) {
        ++ colours[view[i].color]
        if(foe_at(i) > 0) enemies++
    }
    var onRails = rail_miners.near_nest(colours)
    if(buddies[QUEEN] && !buddies[LANCE]) {
        if(onRails) return NOP
        if(foe_at(8-queenPos) == 4) {
            return {cell:deRotate(queenPos,2)}
        }
        if(in_void) return {cell:deRotate(queenPos,4)}
        if(enemies == 2 && queenPos % 2 == 0 && view[deRotate(queenPos,1)].ant == null) {
            return {cell:queenPos,color:7}
        }
        if(enemies == 2 && queenPos % 2 == 1) {
            return {cell:deRotate(queenPos,-1),color:7}
        }
    }
    if(buddies[QUEEN] && buddies[LANCE]) {
        if(enemies == 0 && view[queenPos].color == 1) return NOP
        if(view[deRotate(queenPos,1)].color == 5 && friend_at(deRotate(queenPos,1)) == LANCE) return {cell:deRotate(queenPos,4)}
        return {cell:deRotate(queenPos,2)}
    }
    if(!buddies[QUEEN] && view[4].color == 2) {
        for(var i = 0; i < 9; ++ i) {
            if(view[i].color == 8) return {cell:i}
        }
    }
    if(queenPos >=0 && foe_at(deRotate(queenPos,2)) > 0 && view[deRotate(queenPos,2)].ant.food == 0 && foe_at(deRotate(queenPos,4)) > 0 && view[deRotate(queenPos,4)].ant.food > 0) {
        return {cell:queenPos,color:7}
    }
    return NOP
}

function deRotate(m, amt) {
    var rotationsCW = [1,2,5,8,7,6,3,0]
    var rotationsCCW = [3,6,7,8,5,2,1,0]
    if(m == 4 || m < 0 || m > 8 || amt == 0) return m
    if(amt > 0)
        return rotationsCW[(rotationsCW.indexOf(m)+amt)%8]
    amt = -amt
    return rotationsCCW[(rotationsCCW.indexOf(m)+amt)%8]
}

return play_safe(move_agent([
    THIEF, move,
    QUEEN, move,
    BOUNCER, move,
    LANCE, moveLance,
    LANCE_TIP, moveTip
]))

Ніхто не опублікував жодних відповідей, що експлуатують правило "Непрацюючий працівник, що знаходиться поруч з ворожою королевою, вкраде 1 шматок їжі, якщо він присутній", тому я вирішив це виправити!

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

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

Описи цілей випливають у порядку, який вони відображаються в коді:

Чорна діра (ціль відключена)

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

Зіггурат (цільовий вимкнено)

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

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

Для шахтарів на залізниці він використовує грубі сили пошуку, щоб перевірити, чи сидить королева в центрі головної залізничної системи шахтера. Потім він повертається назад до свого джерела (дозволяючи шахтарським робітникам обійтися). Опинившись у королеви, вона народжує злодія як звичайного і зливає королеву до 9 їжі. До 10 їжі королева шахтаря генерує 3 робітники на кожні 4 їжі, які їй дають, роблячи дотримуватися перспективи з низькою віддачею. Відмовтеся та поверніться пізніше. (Дякую Draco18s, що надали код цього середовища!)

Коли це знаходить Ziggurat, він справляється досить добре і викликає цікаві вибухи, як це було показано в цьому ранньому прототипі (до того, як підстрибування було додано):

вибухнув зигурат

FireFly (ціль відключена)

Блок, націлений на Firefly, використовує код Ziggurat в якості базової лінії, але переосмислює використовувані кольори. Це полегшує будь-якому автору створення нової цілі вампіра з легкістю, зберігаючи кількість байтів низькою. Код Firefly був відключений, коли Firefly не збирав їжу, але був знову включений у Mk. 5.

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

Шахтарі на залізниці (MoaR) / Розсувні міндери (SlM)

Коли це знаходить «Гірників на залізниці», він відслідковує залізницю у виїзному напрямку (у тому ж напрямку, в якому рухаються ненавантажені залізничники), щоб уникнути викрадення їжі. Коли він знаходить кінець залізниці, він діє як шахтарі на власного працівника залізниці 4 (ремонтника). Ми знаємо, де знаходиться залізниця, можемо також слідувати цьому напрямку та петлі назад до початку!

Але найпоширеніший результат - ми ніколи нічого не знаходимо. У цьому випадку все одно добре; про середину табло.

Вітряк

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

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

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

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

Поновлювана енергетика (Mk 7) переглядає логіку націлювання на вітряк, значною мірою скасовуючи зміни Mk 6, оскільки на Вітряному млині відбулися зміни, які спричиняли невдачу коду в 100% часу. Переглянута успішність становить близько 70%, решта 30% - через тупу удачу, яка неминуча. Вектор підходу незначно змінений.

Шосе

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

Знайти королеву просто: знайдіть центр шосе і зачекайте. Вона врешті-решт прийде. Робочі домовленості визначатимуть, чи вона зупиняється чи не доведеться чекати чергового циклу, але затримка не має великої різниці: врешті-решт ми зловимо її та вкрадемо ВСЕ її їжі.

Якщо у вампіра 1000 їжі або більше, вона ігнорує Шоссе, тому що в цей момент це вже не має значення.


1
@trichoplax немає проблем; коли я отримаю час поглянути на це, я побачу, що я можу вдосконалити зараз, коли працівникові не потрібно ковзати. Щодо часу, поки це не було зроблено, я думаю, що головне було чекати, коли суперника легко знайти!
Дейв

9
це справді мало.
Руйнуючий лимон

2
trichoplax явно дозволив такі стратегії експлуатування слабкості
pppery

1
Я схвалив "це справді означає", тому що я згоден, і це одна з найгірших речей, яку я спеціально хотів побачити на цьому конкурсі ...
trichoplax

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

11

Мурашник Ленґтона

Усі мої відповіді будуть містити подібну логіку низького рівня у формі Рамки форматичних функцій. "ЛОГІЧНІ ПОЧАТКИ ВИСОКОГО РОЗВИТКУ ТУТ" позначає кінець коду Рамки.

// FORMIC FUNCTIONS FRAMEWORK //
// Version 1.0                //

var WHITE = 1;
var QUEEN = 5;
var HERE = 4;
var MY_VO = view[HERE];
var ME = MY_VO.ant;
var NOP = move(HERE);

var ORTHOGONALS = [1, 3, 5, 7];
var DIAGONALS = [0, 2, 6, 8];
var DIRECTIONS = [0, 1, 2, 3, 5, 6, 7, 8];
var ALL_CELLS = [0, 1, 2, 3, 4, 5, 6, 7, 8];
var VIEW_ORIENTATIONS = [
  [0,1,2,
   3,4,5,
   6,7,8],

  [6,3,0,
   7,4,1,
   8,5,2],

  [8,7,6,
   5,4,3,
   2,1,0],

  [2,5,8,
   1,4,7,
   0,3,6]
];

function rotateCW(cell, amount) {
  if (cell === HERE) return cell;
  var order = [0, 1, 2, 5, 8, 7, 6, 3];
  return order[(order.indexOf(cell) + amount + 8) % 8];
}

function isDiagonal(cell) {
  return DIAGONALS.includes(cell);
}
function isOrthogonal(cell) {
  return ORTHOGONALS.includes(cell);
}

function move(cell) {
  return {cell: cell};
}
function moveMany(cells) {
  var p = [];
  for (var i = 0; i < cells.length; i++) p.push(move(cells[i]));
  return p;
}

function color(cell, col) {
  return {cell: cell, color: col};
}
function colorMany(cells, col) {
  var p = [];
  for (var i = 0; i < cells.length; i++) p.push(color(cells[i], col));
  return p;
}

function spawn(cell, type) {
  return {cell: cell, type: type};
}
function spawnMany(cells, type) {
  var p = [];
  for (var i = 0; i < cells.length; i++) p.push(spawn(cells[i], type));
  return p;
}

function isSane(action, ant) {
  // TODO: Minimize this
  if (ant === undefined || !ant.isObject) ant = ME;
  if (action === undefined || action.cell < 0 || action.cell >= 9) return false;
  if (action.color !== undefined) {
    if (action.color < 1 || action.color > 8) return false;
    return true;
  }
  else if (action.type !== undefined) {
    if (action.type < 1 || action.type > 4) return false;
    if (ant.type !== QUEEN || ant.food === 0) return false;
    if (isOccupied(action.cell, ant) || view[action.cell].food !== 0) return false;
    return true;
  }
  else {
    if (isOccupied(action.cell, ant) && action.cell !== HERE) return false;
    return true;
  }
}
function isOccupied(cell, ant) {
  if (ant === undefined || !ant.isObject) ant = ME;
  return view[cell].ant !== null || (view[cell].food > 0 && ant.type !== QUEEN && ant.food === 1);
}
function isColoringMeaningful(action) {
  return isSane(action) && action.color !== undefined && view[action.cell].color !== action.color;
}

function test(cell, test) {
  var vo = view[cell];
  return (test.color === undefined || test.color === vo.color) &&
         (test.food === undefined || test.food === vo.food) &&
         (test.ant === undefined || test.ant === vo.ant || (
           (test.ant !== null && vo.ant !== null) &&
           (test.ant.food === undefined || test.ant.food === vo.ant.food) &&
           (test.ant.type === undefined || test.ant.type === vo.ant.type) &&
           (test.ant.friend === undefined || test.ant.friend === vo.ant.friend)
         ));
}

function findOrientation(tests) {
  var best = {orientation: null, matches: []};
  for (var o = 0; o < VIEW_ORIENTATIONS.length; o++) {
    var matches = [];
    for (var i = 0; i < tests.length; i++) {
      if (test(VIEW_ORIENTATIONS[o][tests[i].cell], tests[i])) {
        matches.push(tests[i]);
      }
    }
    if (matches.length > best.matches.length) {
      best.orientation = o;
      best.matches = matches;
    }
  }

  return best;
}

function orientCells(orientation, cells) {
  if (orientation === null || orientation < 0 || orientation >= 4) orientation = 0;
  for (var i = 0; i < cells.length; i++) {
    cells[i] = VIEW_ORIENTATIONS[orientation][cells[i]];
  }
  return cells;
}

function findFirst(func, cells) {
  if (cells === undefined) cells = ALL_CELLS;
  for (var i = 0; i < cells.length; i++) {
    if (func(cells[i])) return cells[i];
  }
  return null;
}
function findAll(func, cells) {
  if (cells === undefined) cells = ALL_CELLS;
  var found = [];
  for (var i = 0; i < cells.length; i++) {
    if (func(cells[i])) found.push(cells[i]);
  }
  return found;
}

// HIGH-LEVEL LOGIC STARTS HERE //
var ROAD_COL = 3;
var SIM_COLS = [ 1, 8, 5, 6, 4, 2, 7, 3]; // SIM_COLS.length must be greater or equal to SIM_ROTS.length
var SIM_ROTS = [-1,-1,+1,-1,-1,+1,-1,-1];
// Here are some additional rulesets to play around with:
// [+1,-1,+1,-1,+1,-1,+1,-1] // Classic Langton's ant, extended to use all 8 colors cyclically
// [+1,+1,-1,+1,-1,+1,+1]    // Produces a very interesting highway
// [-1,-1,+1,-1,-1,+1,-1,-1] // The default one -- chosen because it produces a highway nearly instantly, and the highway itself is pretty efficient
// [-1,+1,-1,-1,-1,+1,+1,-1]
// [-1,-1,+1,-1,-1,+1,-1,-1]
// [-1,+1,-1,-1,-1,+1,+1,-1]
// [+1,-1,+1,+1,+1,-1]

var ANCHOR = 1;
var TAIL = 2;

var DEBUG_MODE = false;

function getSimColIndex(cell) {
  return Math.max(SIM_COLS.lastIndexOf(view[cell].color, SIM_ROTS.length - 1), 0);
}
function getNextSimCol(simColIndex) {
  return SIM_COLS[(simColIndex + 1) % SIM_ROTS.length];
}
function getSimRot(simColIndex) {
  return SIM_ROTS[simColIndex];
}

function run() {
  switch (ME.type) {
    case QUEEN: {
      var anch = findFirst(c => test(c, {ant: {type: ANCHOR, friend: true}}), DIRECTIONS);
      if (anch !== null) {
        if (isOrthogonal(anch)) {
          var tail = findFirst(c => test(c, {ant: {type: TAIL, friend: true}}), DIAGONALS);
          if (tail !== null) {
            return [move(rotateCW(anch, -2 * getSimRot(getSimColIndex(HERE))))].filter(isSane)[0] || NOP;
          } else {
            var nanch = rotateCW(anch, -getSimRot(getSimColIndex(anch)));
            return color(nanch, getNextSimCol(getSimColIndex(nanch)));
          }
        } else {
          return NOP;
        }
      } else {
        var tailO;
        if (ME.food === 2) {
          return spawnMany(ORTHOGONALS, TAIL).filter(isSane)[0] || NOP;
        } else if (ME.food === 1 && (tailO = findOrientation([{cell: 1, ant: {type: TAIL, friend: true}}])).orientation !== null) {
          return spawnMany(orientCells(tailO.orientation, [3, 5]), ANCHOR).filter(isSane)[0] || NOP;
        }

        var f;
        if ((f = findFirst(c => test(c, {food: 1})))) {
          return move(f);
        }

        if (test(HERE, {color: ROAD_COL})) {
          return moveMany(orientCells(findOrientation([{cell: 0, color: ROAD_COL}]).orientation, [8, 6, 2])).filter(a => isSane(a) && !test(a.cell, {color: ROAD_COL}))[0] || moveMany(DIAGONALS).filter(isSane)[0] || NOP;
        } else {
          return color(HERE, ROAD_COL);
        }
      }
      break;
    }
    case ANCHOR: {
      var queen = findFirst(c => test(c, {ant: {type: QUEEN, friend: true}}), DIAGONALS);
      var tail = findFirst(c => test(c, {ant: {type: TAIL, friend: true}}), ORTHOGONALS);
      var qp = [rotateCW(queen, -1), rotateCW(queen, +1)];
      var tp = [rotateCW(tail, -2), rotateCW(tail, +2)];
      for (var i = 0; i < qp.length; i++) {
        for (var j = 0; j < tp.length; j++) {
          if (qp[i] === tp[j]) return [move(qp[i])].filter(isSane)[0] || NOP;
        }
      }
      return NOP;
      break;
    }
    case TAIL: {
      var anch = findFirst(c => test(c, {ant: {type: ANCHOR, friend: true}}), DIAGONALS);
      if (anch !== null) {
        var rot = getSimRot(getSimColIndex(anch));
        var nanch = rotateCW(anch, rot);
        if (test(nanch, {ant: {type: QUEEN, friend: true}})) return [move(rotateCW(anch, -rot))].filter(isSane)[0] || NOP;
        return [move(nanch)].filter(isSane)[0] || NOP;
      } else {
        return NOP;
      }
      break;
    }
  }
}

var output = run();
if (isSane(output)) return output;
else {
  if (DEBUG_MODE) {
    return;
  } else {
    return NOP;
  }
}

Мураха Ленґтона ("RL") Мураха Ленґтона ("LLRLLRLL")


Вступ

Це реалізація мураха Ленґтона в межах виклику Formic Functions QOTH. Як додатковий бонус, він підтримує різнобарвні варіанти мурашки Лангтона. Також мені вдалося використати повну палітру до 8 кольорів. Крім того, цей мураш завершує один крок моделювання за два етапи часу гри.

На жаль, більш цікаві варіанти мурашника Ленґтона беруть більше 8 кольорів, тому я замість цього зупинився на ефективному. Я написав додаткову програму, щоб знайти мурашок, які охоплюють найбільшу кількість землі протягом визначеного строку, і натрапив на кілька варіантів набору правил "RRL". Всі вони охоплюють 4168 комірок протягом 15000 кроків.

Тепер 4000 клітин - це безглуздо! Це означає, що в середньому цей запис закінчить гру з ЧОТИРОЮ їжею. І це на порожній дошці! Чесно кажучи, броунівський джиг робить краще за це. Коротше кажучи, це не є серйозним претендентом, коли мова йде про позицію на табло. Однак це може забруднити інші записи. І це єдина причина, чому я навіть потрудився подати це. Дуже складні взаємодії часто виникають між цим записом та тими, які сильно покладаються на кольори. А ті, що покладаються на кольори, зазвичай страждають.


Пояснення

Кольори ( SIM_COLS) мають відповідне обертання ( SIM_ROTS), яке використовує Королева, щоб змінити напрямок, в якому вона прямує. +1означає обертання за годинниковою стрілкою ("R"), -1означає обертання проти годинникової стрілки ("L"). Мураха Класичного Ленгтон б SIM_COLS = [ 1, 8], SIM_ROTS = [+1,-1]. SIM_COLSМасив ефективно видаляє всі елементи , які не мають відповідний елемент в SIM_ROTSмасиві. Всі кольори, які не містяться в усіченому SIM_COLSмасиві, розглядаються як кольоровий 0 і ніколи не буде вироблений самим мурахом.

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

Закінчивши початкову сутичку, Королева породила двох мурах, названих Якір та Хвіст . Королева породжує Хвіст першим, примикаючи ортогонально (клітини 1, 3, 5, 7) до Королеви. Потім вона породжує Якор, сусідній ортогонально до Королеви і по діагоналі (клітини 0, 2, 6, 8) до Хвоста.

Поведінка складається з постійно повторюваного циклу двох різних кроків.

Крок 1

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

Ведуча: нічого не робить.

Королева: Знаходить якір і трактує його як зворотний напрямок, яким рухається мураха. Потім напрямок обертається відповідно до кольору під Королевою. Королева рухається до клітини, позначеної обернутим напрямком.

Крок 2

Хвіст: нічого не робить.

Якір: шукає клітинку, яка діагонально примикає до хвоста і ортогонально примикає до Королеви, і йде туди. Там є щонайменше одна така клітина.

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

Визначення, що робити

Королева визначає поточний крок, шукаючи Хвіст ортогонально. Якщо вона знайде це, вона на кроці 1. В іншому випадку вона на кроці 2. У рідкісних випадках вона може знайти Якір, що примикає по діагоналі до себе. У такому випадку вона нічого не зробить.

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

Хвіст визначає поточний крок шляхом пошуку Якоря по діагоналі. Якщо він знайде, це на кроці 1. Інакше - на кроці 2.

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


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


3
Ласкаво просимо до PPCG!
Steadybox

10

Ziggurat v3.0

var clockwise = [1,2,5,0,4,8,3,6,7];
var opposite = [8,7,6,5,4,3,2,1,0];
var cyclic_cw = [0,1,2,5,8,7,6,3,0];
var worker_colors = [4,5,7,8];
var next_worker_color = [1,1,1,1,5,7,1,8,4];
var prev_worker_color = [1,1,1,1,8,4,1,5,7];
var diags = [0,2,6,8];
var orthos = [1,3,5,7];
var cleaning_color = 6;


// Borrowed from Medusa
function clean(move) {
    if (move["color"] == undefined) {
        if (view[move["cell"]].ant != null) {
            move = {cell: 4};
        }
        if (move["type"] == undefined) {
            if (view[move["cell"]].food == 1 && view[4].ant.type < 5 && view[4].ant.food > 0) {
                move = {cell: 4};
            }
        } else if (view[4].ant.type != 5 || view[4].ant.food == 0 || view[move["cell"]].food == 1) {
            move = {cell: 4};
        }
    }
    return move;
}

function worker_blank(cel) {
    return (worker_colors.indexOf(view[cel].color) < 0);
}

// Own status
var my_color = view[4].color;
var my_food = view[4].ant.food;
var my_type = view[4].ant.type;

// Random free cell
var free_cell = 4;
for (var cel = 0; cel < 9; cel++) {
    if (view[cel].ant == null) {
    free_cell = cel;
    }
}

// Check surroundings
var blanks = 0;
var outer_edge = 0;
var inner_edge = 0;
var next_edge = -1;
var prev_nonblank = -1;
var some_nonblank = -1;
var prev_cell = -1;
var next_cell = -1;
var food_cell = -1;
var nonblank_color = 0;
var cleaning_marker = -1;
var uniform = 1;
var low_type = -1;
var friend_workers = 0;
var guards = 0;
var enemies = 0;
var enemy_queens = 0;
var my_queen = -1;

if (!worker_blank(4)) {
    nonblank_color = view[4].color;
}
for (var ix = 0; ix < 8; ix++) {
    var cel = cyclic_cw[ix];
    var cel2 = cyclic_cw[ix+1];
    if (view[cel].food == 1) {
    food_cell = cel;
    }
    if (worker_blank(cel)) {
    blanks++;
    if (!worker_blank(cel2)) {
        if (worker_blank(4)) {
        outer_edge = 1;
        } else {
        inner_edge = 1;
        }
        next_edge = cel;
            prev_nonblank = cel2;
    }
    if (view[cel].color == cleaning_color) {
        cleaning_marker = cel;
    }
    } else {
    some_nonblank = cel;
    if (nonblank_color == 0) {
        nonblank_color = view[cel].color;
    } else if (view[cel].color != nonblank_color) {
        uniform = 0;
    }
    }
    if ((!worker_blank(4) && view[cel2].color == prev_worker_color[my_color] && view[cel2].ant == null) || (worker_blank(4) && !worker_blank(cel2))) {
    prev_cell = cel2;
    }
    if (!worker_blank(4) && view[cel2].color == next_worker_color[my_color] && view[cel2].ant == null) {
    next_cell = cel2;
    }
    if (view[cel].ant != null) {
    var the_ant = view[cel].ant;
    if (the_ant.friend) {
        if (low_type < 0 || the_ant.type < low_type) {
        low_type = the_ant.type;
        }
        if (the_ant.type == 4) {
        guards++;
        } else if (the_ant.type == 5) {
        my_queen = cel;
        } else {
        friend_workers++;
        }
    } else {
        enemies++;
        if (the_ant.type == 5) {
        enemy_queens++;
        }
    }
    }
}

// Queen before finding food (motile)
if (my_type == 5 && worker_blank(4)) {
    if (my_food > 1) {
    return {cell:4, color:worker_colors[1]};
    }
    if (food_cell >= 0 && my_color != 1) {
    return clean({cell:food_cell});
    }
    if (my_color == 2) {
    for (var ix = 0; ix < 4; ix++) {
        var cel = diags[ix];
        var oppo = opposite[cel];
        if (view[cel].color == 2) {
        if (view[oppo].color == 1 && worker_blank(oppo) && view[oppo].ant == null) {
            return clean({cell:oppo});
        }
                if (view[oppo].color != 2) {
            return {cell:oppo, color:1};
                }
        }
    }
        for (var ix = 0; ix < 4; ix++) {
        var cel = diags[ix];
        if (view[cel].color != 2) {
        return {cell:cel, color:2};
        }
    }
    }
    if (my_color == 1) {
    return {cell:4, color:2};
    }
    return clean({cell:free_cell});
}

// Queen after finding food (sessile)
if (my_type == 5) {
    for (var ix = 0; ix < 8; ix++) {
        cel = cyclic_cw[ix];
    if (worker_blank(cel)) {
            return {cell:cel, color:worker_colors[1]};
    }
    }

    if (my_color != worker_colors[0]) {
        if (my_food > 0) {
        return clean({cell:free_cell, type:1});
        } else {
            return {cell:4, color:worker_colors[0]};
        }
    }
    if (my_food > 0) {
    if (my_food > 3 && guards < 2) {
        return clean({cell:free_cell, type:4});
    }
    if (0 < low_type && low_type < 3) {
        return clean({cell:free_cell, type:(low_type + 1)});
    }
    }
    return {cell:4};
}

// Queen's guard

if (my_type == 4) {
    // Queen is a nbor
    if (my_queen >= 0) {
    if (enemy_queens > 0) {
        return {cell:4};
    }
    if (my_queen == 1) {
        return clean({cell:5});
    }
    return clean({cell:clockwise[my_queen]});
    }
    // Try to get to queen
    if (prev_cell >= 0) {
    return clean({cell:prev_cell});
    }
    // Wander
    return clean({cell:free_cell});
}

// Worker

// Create new ziggurat
if (blanks == 8 && cleaning_marker < 0 && my_color != cleaning_color) {
    if (worker_colors.indexOf(my_color) >= 0) {
    return {cell:free_cell, color:my_color};
    }
    return {cell:free_cell, color:worker_colors[0]};
}

var front = view[1].color;
if (!worker_blank(4) && !worker_blank(1) && my_color != front) {
    if (view[7].color == front || (view[6].color == front && view[8].color == front)) {
        return {cell:4, color:front};
    }
}

if (my_food == 0) {
    // Grab food
    if (food_cell >= 0 && (!worker_blank(4) || !worker_blank(food_cell))) {
        return clean({cell:food_cell});
    }

    // Clear marked uniform region
    if (my_color == cleaning_color && uniform) {
    if (blanks < 7) {
        return {cell:some_nonblank, color:1};
    } else if (blanks == 7) {
        return {cell:some_nonblank, color:cleaning_color};
    }
    }

    // Follow cleaning color
    if (blanks == 8) {
    if (cleaning_marker < 0 || my_color == cleaning_color) {
        return {cell:4, color:1};
    } else if (cleaning_marker >= 0) {
        return clean({cell:cleaning_marker});
    }
    }

    // Dive into uniform region
    if (blanks > 3 && worker_blank(4) && worker_blank(1) && !worker_blank(2) && view[2].color == view[5].color && view[2].color == view[8].color) {
    return clean({cell:2});
    }

    // Mark uniform region for clearing
    if (!worker_blank(4) && uniform && (blanks < 4 || (blanks < 7 && ((worker_blank(1) && worker_blank(7)) || (worker_blank(3) && worker_blank(5)))))) {
    return {cell:4, color:cleaning_color};
    }

    // Extend edge
    if (outer_edge) {
    var new_color = 0;
    var cl = clockwise[next_edge];
    var cl2 = clockwise[cl];
    if (!worker_blank(1) && view[7].color == view[1].color) {
        new_color = view[1].color;
    } else if (!worker_blank(3) && view[5].color == view[3].color) {
        new_color = view[3].color;
        } else if (!worker_blank(cl2) && view[cl].color == next_worker_color[view[cl2].color]) {
        if (cl > 1) {
        new_color = view[cl].color;
        } else {
        new_color = next_worker_color[view[cl].color];
        }
    } else if (!worker_blank(cl)) {
        new_color = next_worker_color[view[cl].color];
        } else if (prev_nonblank >= 0) {
            new_color = next_worker_color[prev_nonblank];
    }
    if (new_color == 0 && blanks < 2) {
        new_color = worker_colors[0];
    }
        if (new_color > 0) {
            return {cell:4, color:new_color};
        } else {
            return clean({cell:next_edge});
        }
    }

    // Escape from hole
    if (worker_blank(4) && blanks == 0) {
        return {cell:4, color:next_worker_color[nonblank_color]};
    }

    // Go outside or fill it
    if (inner_edge && next_edge >= 0) {
    if (friend_workers > 1) {
        return clean({cell:free_cell});
    }
        if (blanks == 4 && prev_nonblank >= 0) {
            return {cell:next_edge, color:next_worker_color[view[prev_nonblank].color]};
        }
    if (view[next_edge].ant == null) {
            return clean({cell:next_edge});
    }
    return {cell:4};
    }

    // Go toward border
    if (!worker_blank(4) && next_cell >= 0 && next_cell != 1) {
    return clean({cell:next_cell});
    }

    // Wander
    return clean({cell:free_cell});
}

if (my_food > 0) {
    // Take food to queen
    if ((prev_cell >= 0 && prev_cell != 1) || (prev_cell >= 0 && worker_blank(4))) {
    return clean({cell:prev_cell});
    }

    // Wander
    if (worker_blank(free_cell) && !worker_blank(4)) {
    return {cell:4};
    }
    return clean({cell:free_cell});
}

return clean({cell:free_cell});

Як це працює

Цей мураш - це поєднання Чорної діри та Медузи . Королева починається з пошуку їжі по діагоналі. Після того, як вона має дві одиниці їжі, вона стає сидною і робить двох працівників. Робітники кружляють навколо королеви і роблять зростаючий смугастий регіон ("зиггурат"), використовуючи чотири кольори. Коли працівник знаходить їжу, слідкує за смужками і відносить її до королеви, яка сидить у центрі площі. Якщо робітник був одним із первинних двох, королева робить нового робітника; інакше вона зберігає їжу.

Коментарі

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

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

У версії 3.0 більше налаштувань на побудову побудови. Як (в основному неперевірений) контрзахід проти Чорної діри та дикого вогню, працівники, які виявляють рівномірні кольорові регіони, намагатимуться їх стерти. Королева вже не породить десятки робітників при нападі; натомість у неї навколо орбіти виділена пара мурах-охоронців. Вона також буде чекати довше, перш ніж почати прибирати їжу.

Я насправді не знаю JS, тому код - жахливий безлад.

Ось малюнок смугастого регіону:

Зигурат


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

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

1
@trichoplax Я щойно оновив бота. Він ніколи не повинен робити незаконний крок зараз, а загалом трохи розумніший.
Згарб

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

1
@trichoplax Слід виправити зараз. Я також додав інші вдосконалення.
Згарб

9

Wildfire Mk.3

Усі мої відповіді поділяють один і той же набір допоміжних функцій низького рівня. Шукайте "Логіка високого рівня починається тут", щоб побачити код, специфічний для цієї відповіді.

// == Shared low-level helpers for all solutions ==

var QUEEN = 5;

var WHITE = 1;
var COL_MIN = WHITE;
var COL_LIM = 9;

var CENTRE = 4;

var NOP = {cell: CENTRE};

var DIR_FORWARDS = false;
var DIR_REVERSE = true;
var SIDE_RIGHT = true;
var SIDE_LEFT = false;

function sanity_check(movement) {
  var me = view[CENTRE].ant;
  if(!movement || (movement.cell|0) !== movement.cell || movement.cell < 0 || movement.cell > 8) {
    return false;
  }
  if(movement.type) {
    if(movement.color) {
      return false;
    }
    if((movement.type|0) !== movement.type || movement.type < 1 || movement.type > 4) {
      return false;
    }
    if(view[movement.cell].ant || view[movement.cell].food) {
      return false;
    }
    if(me.type !== QUEEN || me.food < 1) {
      return false;
    }
    return true;
  }
  if(movement.color) {
    if((movement.color|0) !== movement.color || movement.color < COL_MIN || movement.color >= COL_LIM) {
      return false;
    }
    if(view[movement.cell].color === movement.color) {
      return false;
    }
    return true;
  }
  if(movement.cell !== CENTRE && view[movement.cell].ant) {
    return false;
  }
  if(view[movement.cell].food + me.food > 1 && me.type !== QUEEN) {
    return false;
  }
  return true;
}

function as_array(o) {
  if(Array.isArray(o)) {
    return o;
  }
  return [o];
}

function best_of(movements) {
  var m;
  for(var i = 0; i < movements.length; ++ i) {
    if(typeof(movements[i]) === 'function') {
      m = movements[i]();
    } else {
      m = movements[i];
    }
    if(sanity_check(m)) {
      return m;
    }
  }
  return null;
}

function play_safe(movement) {
  // Avoid disqualification: no-op if moves are invalid
  return best_of(as_array(movement)) || NOP;
}

var RAND_SEED = (() => {
  var s = 0;
  for(var i = 0; i < 9; ++ i) {
    s += view[i].color * (i + 1);
    s += view[i].ant ? i * i : 0;
    s += view[i].food ? i * i * i : 0;
  }
  return s % 29;
})();

var ROTATIONS = [
  [0, 1, 2, 3, 4, 5, 6, 7, 8],
  [6, 3, 0, 7, 4, 1, 8, 5, 2],
  [8, 7, 6, 5, 4, 3, 2, 1, 0],
  [2, 5, 8, 1, 4, 7, 0, 3, 6],
];

function try_all(fns, limit, wrapperFn, checkFn) {
  var m;
  fns = as_array(fns);
  for(var i = 0; i < fns.length; ++ i) {
    if(typeof(fns[i]) !== 'function') {
      if(checkFn(m = fns[i])) {
        return m;
      }
      continue;
    }
    for(var j = 0; j < limit; ++ j) {
      if(checkFn(m = wrapperFn(fns[i], j))) {
        return m;
      }
    }
  }
  return null;
}

function identify_rotation(testFns) {
  // testFns MUST be functions, not constants
  return try_all(
    testFns,
    4,
    (fn, r) => fn(ROTATIONS[r]) ? ROTATIONS[r] : null,
    (r) => r
  );
}

function near(a, b) {
  return (
    Math.abs(a % 3 - b % 3) < 2 &&
    Math.abs(Math.floor(a / 3) - Math.floor(b / 3)) < 2
  );
}

function try_all_angles(solverFns) {
  return try_all(
    solverFns,
    4,
    (fn, r) => fn(ROTATIONS[r]),
    sanity_check
  );
}

function try_all_cells(solverFns, skipCentre) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i === CENTRE && skipCentre) ? null : fn(i)),
    sanity_check
  );
}

function try_all_cells_near(p, solverFns) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i !== p && near(p, i)) ? fn(i) : null),
    sanity_check
  );
}

function ant_type_at(i, friend) {
  return (view[i].ant && view[i].ant.friend === friend) ? view[i].ant.type : 0;
}

function friend_at(i) {
  return ant_type_at(i, true);
}

function foe_at(i) {
  return ant_type_at(i, false);
}

function ant_type_near(p, friend) {
  for(var i = 0; i < 9; ++ i) {
    if(i !== 4 && ant_type_at(i, friend) && near(i, p)) {
      return true;
    }
  }
  return false;
}

function move_agent(agents) {
  var me = view[CENTRE].ant;
  var buddies = [0, 0, 0, 0, 0, 0];
  for(var i = 0; i < 9; ++ i) {
    ++ buddies[friend_at(i)];
  }

  for(var i = 0; i < agents.length; i += 2) {
    if(agents[i] === me.type) {
      return agents[i+1](me, buddies);
    }
  }
  return null;
}

function grab_nearby_food() {
  return try_all_cells((i) => (view[i].food ? {cell: i} : null), true);
}

function go_anywhere() {
  return try_all_cells((i) => ({cell: i}), true);
}

function colours_excluding(cols) {
  var r = [];
  for(var i = COL_MIN; i < COL_LIM; ++ i) {
    if(cols.indexOf(i) === -1) {
      r.push(i);
    }
  }
  return r;
}

function generate_band(start, width) {
  var r = [];
  for(var i = 0; i < width; ++ i) {
    r.push(start + i);
  }
  return r;
}

function colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function(c) {
      return colours[(colours.indexOf(c) + 1) % colours.length];
    }
  };
}

function random_colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function() {
      return colours[RAND_SEED % colours.length];
    }
  };
}

function fast_diagonal(colourBand, avoidedColours) {
  if(!avoidedColours) {
    avoidedColours = colourBand;
  }
  var m = try_all_angles([
    // Avoid nearby checked areas
    (rot) => {
      if(
        !avoidedColours.contains(view[rot[0]].color) &&
        avoidedColours.contains(view[rot[5]].color) &&
        avoidedColours.contains(view[rot[7]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // Go in a straight diagonal line if possible
    (rot) => {
      if(
        !avoidedColours.contains(view[rot[0]].color) &&
        avoidedColours.contains(view[rot[8]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // When in doubt, pick randomly but avoid doubling-back
    (rot) => (avoidedColours.contains(view[rot[0]].color) ? null : {cell: rot[0]}),

    // Double-back when absolutely necessary
    (rot) => ({cell: rot[0]})
  ]);

  // Lay a colour track so that we can avoid doubling-back
  // (and mess up our foes as much as possible)
  if(!avoidedColours.contains(view[CENTRE].color)) {
    var prevCol = m ? view[8-m.cell].color : WHITE;
    return {cell: CENTRE, color: colourBand.next(prevCol)};
  }

  return m;
}

function follow_edge(obstacleFn, side, resultFn) {
  // Since we don't know which direction we came from, this can cause us to get
  // stuck on islands, but the random orientation helps to ensure we don't get
  // stuck forever.

  if(!resultFn) {
    resultFn = (i) => ({cell: i});
  }

  var order = ((side === SIDE_LEFT)
    ? [0, 3, 6, 7, 8, 5, 2, 1, 0]
    : [0, 1, 2, 5, 8, 7, 6, 3, 0]
  );
  return try_all(
    [obstacleFn],
    order.length - 1,
    (fn, i) => ((fn(order[i+1]) && !fn(order[i])) ? resultFn(order[i]) : null),
    sanity_check
  );
}

function start_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => ((
      !protectedCols.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 2 : 0]].color) &&
      !colourBand.contains(view[rot[1]].color)
    )
      ? {cell: rot[right ? 5 : 3], color: colourBand.next(WHITE)}
      : null)
  ]);
}

function lay_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => {
      var ahead = rot[right ? 2 : 0];
      var behind = rot[right ? 8 : 6];
      if(
        colourBand.contains(view[behind].color) &&
        !protectedCols.contains(view[ahead].color) &&
        !colourBand.contains(view[ahead].color) &&
        !colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        return {cell: ahead, color: colourBand.next(view[behind].color)};
      }
    }
  ]);
}

function follow_dotted_path(colourBand, side, direction) {
  var forwards = (direction === DIR_REVERSE) ? 7 : 1;
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    // Cell on our side? advance
    (rot) => {
      if(
        colourBand.contains(view[rot[right ? 5 : 3]].color) &&
        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[0]].color) &&
        !colourBand.contains(view[rot[2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    },

    // Cell ahead and behind? advance
    (rot) => {
      var passedCol = view[rot[right ? 8 : 6]].color;
      var nextCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(passedCol) &&
        nextCol === colourBand.next(passedCol) &&

        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[right ? 0 : 2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    }
  ]);
}

function escape_dotted_path(colourBand, side, newColourBand) {
  var right = (side === SIDE_RIGHT);
  if(!newColourBand) {
    newColourBand = colourBand;
  }

  return try_all_angles([
    // Escape from beside the line
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        !colourBand.contains(view[rot[right ? 8 : 6]].color) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[rot[7]].color) ||
        colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        // not oriented, or in a corner
        return null;
      }
      return best_of([
        {cell: rot[right ? 0 : 2], color: newColourBand.next(approachingCol)},
        {cell: rot[right ? 3 : 5]},
        {cell: rot[right ? 0 : 2]},
        {cell: rot[right ? 6 : 8]},
        {cell: rot[right ? 2 : 0]},
        {cell: rot[right ? 8 : 6]},
        {cell: rot[right ? 5 : 3]}
      ]);
    },

    // Escape from inside the line
    (rot) => {
      if(
        !colourBand.contains(view[rot[7]].color) ||
        !colourBand.contains(view[rot[1]].color) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      return best_of([
        {cell: rot[3]},
        {cell: rot[5]},
        {cell: rot[0]},
        {cell: rot[2]},
        {cell: rot[6]},
        {cell: rot[8]}
      ]);
    }
  ]);
}

function latch_to_dotted_path(colourBand, side) {
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(approachingCol) &&
        view[rot[right ? 8 : 6]].color === colourBand.next(approachingCol) &&
        !colourBand.contains(view[rot[right ? 5 : 3]].color)
      ) {
        // We're on the wrong side; go inside the line
        return {cell: rot[right ? 5 : 3]};
      }
    },

    // Inside the line? pick a side
    (rot) => {
      var passedCol = view[rot[7]].color;
      var approachingCol = view[rot[1]].color;
      if(
        !colourBand.contains(passedCol) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      if((approachingCol === colourBand.next(passedCol)) === right) {
        return best_of([{cell: rot[3]}, {cell: rot[6]}, {cell: rot[0]}]);
      } else {
        return best_of([{cell: rot[5]}, {cell: rot[2]}, {cell: rot[8]}]);
      }
    }
  ]);
}


// == High-level logic begins here ==


var groundCol = 5;
var poisonCol = 8;

var DIRECTOR = 1;
var FORAGER0 = 2;
var FORAGER1 = 3;
var FORAGER2 = 4;
var MAX_FORAGER_TYPES = 3; // Worker creation throttle
var MIN_FOOD = 3; // Don't embarrass ourselves when things go bad
var MAX_FOOD_SPAWN = 80; // If we're doing well, don't spoil it all

var GROUND_COLOURS = colour_band([groundCol, poisonCol]);
var POISON_COLOURS = colour_band([poisonCol]);
var SAFE_COLOURS = random_colour_band(colours_excluding([WHITE, groundCol, poisonCol]));
var INITIAL_OBSTACLES = random_colour_band(colours_excluding([WHITE]));

function ground_at(i) {
  return GROUND_COLOURS.contains(view[i].color);
}

function unlaiden_friend_at(i) {
  return friend_at(i) && (friend_at(i) === QUEEN || !view[i].ant.food);
}

function obstacle_at(i) {
  // foes are unpredictable, so don't consider them obstacles
  return view[i].food || unlaiden_friend_at(i) || GROUND_COLOURS.contains(view[i].color);
}

function wait_if_blocked(i) {
  return friend_at(i) ? {cell:CENTRE} : {cell: i};
}

function move_director(me, buddies) {
  if(!buddies[QUEEN]) {
    // Lost the queen!
    return go_anywhere();
  }

  var rot = identify_rotation((rot) => (
    friend_at(rot[0]) === QUEEN || friend_at(rot[1]) === QUEEN
  ));

  var ready = (friend_at(rot[1]) === QUEEN && view[rot[1]].color === groundCol);
  var shift = (view[rot[2]].color === groundCol && GROUND_COLOURS.contains(view[rot[5]].color));

  return best_of([
    // Ensure we never end up underground unless we mean to, and provide a
    // base poison layer to help workers find the right side if lost
    {cell: CENTRE, color: poisonCol},
//    {cell: rot[5], color: poisonCol},
    {cell: rot[3], color: poisonCol},

    // Move up to avoid own line after wrapping (us being underground is a signal)
    (ready && shift) && {cell: rot[2]},

    // Advance
    (ready && !shift) && {cell: rot[5]},

    // Make the poison layer more solid if we have extra time
    {cell: rot[7], color: poisonCol},
    {cell: rot[6], color: poisonCol},
    {cell: rot[8], color: poisonCol},

    // Don't lose the queen
    NOP
  ]);
}

function move_forager(me, buddies) {
  var underground = GROUND_COLOURS.contains(view[CENTRE].color);
  var buried = 0;
  for(var i = 0; i < 9; ++ i) {
    if(i !== 4 && GROUND_COLOURS.contains(view[i].color)) {
      ++ buried;
    }
  }
  var travelCol = underground ? POISON_COLOURS : SAFE_COLOURS;

  if(buddies[DIRECTOR]) {
    // We've somehow got in the way of the line; get out of the way
    return try_all_angles((rot) =>
      ((friend_at(rot[6]) === DIRECTOR || friend_at(rot[7]) === DIRECTOR) &&
      best_of([{cell: rot[0]}, {cell: rot[1]}, {cell: rot[2]}])));
  }

  if(me.food) {
    // We have food for the queen; run ahead to find her as fast as we can

    return best_of([
      // Identify confusing pinch points and close them (don't get stuck on islands)
      try_all_angles((rot) => (
        obstacle_at(rot[1]) && obstacle_at(rot[7]) &&
        !obstacle_at(rot[5]) && !GROUND_COLOURS.contains(view[CENTRE].color)
      ) && {cell: CENTRE, color: groundCol}),

      // We're enclosed; mark this as a dead-end
      (buried >= 7) && {cell: CENTRE, color: poisonCol},

      // Race to queen, but don't climb over each other and cause a blockage
      follow_edge(obstacle_at, SIDE_RIGHT, wait_if_blocked),

      // Lost? Travel quickly to find the surface again
      fast_diagonal.bind(null, travelCol),

      // Totally lost
      go_anywhere
    ]);
  }

  if(buddies[QUEEN]) {
    // Don't overtake the queen!
    return NOP;
  }

  // Paint the ground
  if(!underground) {
    return {cell: CENTRE, color: groundCol};
  }

  return best_of([
    // Unpaint small islands which would confuse us or our buddies
    (buried >= 3) && try_all_angles((rot) => (
      !view[rot[0]].ant &&
      GROUND_COLOURS.contains(view[rot[0]].color) &&
      !GROUND_COLOURS.contains(view[rot[1]].color) &&
      !GROUND_COLOURS.contains(view[rot[3]].color)
    ) && {cell: rot[0], color: SAFE_COLOURS.next(WHITE)}),

    (buried >= 3) && try_all_angles((rot) => (
      !view[rot[1]].ant &&
      GROUND_COLOURS.contains(view[rot[1]].color) &&
      !GROUND_COLOURS.contains(view[rot[0]].color) &&
      !GROUND_COLOURS.contains(view[rot[2]].color)
    ) && {cell: rot[1], color: SAFE_COLOURS.next(WHITE)}),

    // Follow line
    follow_edge(ground_at, SIDE_RIGHT, wait_if_blocked),

    // Disoriented; find the surface again
    fast_diagonal.bind(null, travelCol),

    // Totally lost; random walk
    {cell: 0}
  ]);
}

function move_queen(me, buddies) {
  if(buddies[DIRECTOR]) {
    var rot = identify_rotation((rot) => (
      (friend_at(rot[7]) === DIRECTOR && view[rot[7]].color !== groundCol) ||
      (friend_at(rot[5]) === DIRECTOR && view[rot[5]].color === groundCol) ||
      friend_at(rot[8]) === DIRECTOR
    ));

    var rand14 = rot === ROTATIONS[0];
    var existing = friend_at(rot[0]);
    var nextType = existing ? (existing + 1) : FORAGER0;
    var workerSpawn = (
      me.food > MIN_FOOD && me.food < MAX_FOOD_SPAWN &&
      view[rot[3]].color === groundCol && // Don't spawn if disrupted
      view[rot[0]].color === WHITE && // Don't spawn while stuck in a nest
      view[rot[1]].color === WHITE &&
      !friend_at(rot[1]) && !friend_at(rot[3]) && !friend_at(rot[6]) &&
      (existing || rand14) // reduce likelihood of spawning new chains
    );

    return best_of([
      // Paint ground
      {cell: CENTRE, color: groundCol},

      // Follow director up slopes
      (friend_at(rot[5]) === DIRECTOR) && {cell: rot[2]},
      (friend_at(rot[5]) === DIRECTOR) && {cell: rot[1]},

      // Recognise likely erasure issues and correct
      view[rot[2]].color === groundCol && GROUND_COLOURS.contains(view[rot[8]].color) &&
        {cell: CENTRE, color: groundCol},

      // Clear cells which could confuse workers
      GROUND_COLOURS.contains(view[rot[2]].color) && {cell: rot[2], color: SAFE_COLOURS.next(WHITE)},

      // Spawn new workers when ready (throttle probabilistically)
      (workerSpawn && nextType < FORAGER0 + MAX_FORAGER_TYPES) && {cell: rot[1], type: nextType},

      // Follow director along flat planes
      (friend_at(rot[8]) === DIRECTOR) && {cell: rot[5]},

      // Don't lose director
      NOP
    ]);
  }

  return best_of([
    // Begin wildfire
    (me.food >= MIN_FOOD + MAX_FORAGER_TYPES + 1) && try_all_angles((rot) =>
      (view[rot[5]].color !== groundCol && sanity_check({cell: rot[5]}) &&
        {cell: rot[8], type: DIRECTOR})),

    // Hungry or too crowded to begin; frantically find food
    grab_nearby_food,
    fast_diagonal.bind(null, SAFE_COLOURS, INITIAL_OBSTACLES),
    go_anywhere,
    {cell: 1, color: SAFE_COLOURS.next(WHITE)}
  ]);
}

return play_safe(move_agent([
  DIRECTOR, move_director,
  FORAGER0, move_forager,
  FORAGER1, move_forager,
  FORAGER2, move_forager,
  QUEEN, move_queen,
]));

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

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

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

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

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

Ось результат одного з прототипів:

ПОЖАР

(так, це 865 робочих мурашок… і е, 5 продуктів харчування)


Ось скріншот із конкуренцією:

Очищення вогню


І ось як я уявляю мурашок у своєму розумі (плюс відповідну музику):

Парад паприки


Коментарі не для розширеного обговорення; ця розмова переміщена до чату .
Мартін Ендер

не чую вашу музику ...
NH.

1
@NH. жаби / хаос / загальне божевілля - з фільму «Паприка». Якщо ви хочете знати музику, дивіться це!
Дейв

9

Світлодіодний

Усі мої відповіді будуть містити подібну логіку низького рівня у формі Рамки форматичних функцій. "ЛОГІЧНІ ПОЧАТКИ ВИСОКОГО РОЗВИТКУ ТУТ" позначає кінець коду Рамки.

 //  FORMIC FRAMEWORK  //
// Version 6.1.10     //
const WHITE = 1;
const QUEEN = 5;
const CENTER = 4;
const HERE = view[CENTER];
const ME = HERE.ant;
const ORTHOGONALS = [1, 3, 5, 7];
const DIAGONALS = [0, 2, 6, 8];
const DIAGONALS_ORTHOGONALS = [0, 2, 6, 8, 1, 3, 5, 7];
const DIRECTIONS = [0, 1, 2, 3, 5, 6, 7, 8];
const CLOCKWISE_DIRECTIONS = [0, 1, 2, 5, 8, 7, 6, 3];
const CELLS = [0, 1, 2, 3, 4, 5, 6, 7, 8];
const ROTATIONS = [
  [0, 1, 2,
   3, 4, 5,
   6, 7, 8],

  [6, 3, 0,
   7, 4, 1,
   8, 5, 2],

  [8, 7, 6,
   5, 4, 3,
   2, 1, 0],

  [2, 5, 8,
   1, 4, 7,
   0, 3, 6]
];
const NEIGHBORS = [
  [1, 4, 3],
  [2, 5, 4, 3, 0],
  [5, 4, 1],
  [0, 1, 4, 7, 6],
  [0, 1, 2, 5, 8, 7, 6, 3],
  [8, 7, 4, 1, 2],
  [3, 4, 7],
  [6, 3, 4, 5, 8],
  [7, 4, 5]
];
const HORIZONTAL_FLIP = [2, 1, 0, 5, 4, 3, 8, 7, 6];
const VERTICAL_FLIP = [6, 7, 8, 3, 4, 5, 0, 1, 2];

const DEBUG_MODE = false;
function dump() {
  if (DEBUG_MODE) {
    throw "dump() not implemented";
  }
}
function log(...args) {
  if (DEBUG_MODE) {
    console.log(...args);
  }
}
function error(...args) {
  log("Transformed view state:", view);
  log(...args);
  throw "A critical error has occurred!";
}

function createArray(func, length) {
  const arr = [];
  for (let i = 0; i < length; i++) {
    arr.push(func(i, arr));
  }
  return arr;
}

class Test {
  run(cell) {
    error("No run method defined for this instance of Test:", this);
  }
  find(cells = CELLS) {
    return cells.find((c) => this.run(c));
  }
  findIndex(cells = CELLS) {
    return cells.findIndex((c) => this.run(c));
  }
  filter(cells = CELLS) {
    return cells.filter((c) => this.run(c));
  }
  every(cells = CELLS) {
    return cells.every((c) => this.run(c));
  }
  some(cells = CELLS) {
    return cells.some((c) => this.run(c));
  }
  count(cells = CELLS) {
    return this.filter(cells).length;
  }
  invert() {
    return new InverseTest(this);
  }
  and(test) {
    return new EveryTest(this, test);
  }
  or(test) {
    return new SomeTest(this, test);
  }
}

class InverseTest extends Test {
  constructor(test) {
    super();
    this.test = test;
  }
  run(cell) {
    return !this.test.run(cell);
  }
  invert() {
    return this.test;
  }
}

class CombinedTest extends Test {
  constructor(...tests) {
    super();
    this.tests = tests;
  }
  append(test) {
    this.tests.push(test);
    return this;
  }
}
class EveryTest extends CombinedTest {
  run(cell) {
    return this.tests.every((test) => test.run(cell));
  }
  and(test) {
    return this.append(test);
  }
}
class SomeTest extends CombinedTest {
  run(cell) {
    return this.tests.some((test) => test.run(cell));
  }
  or(test) {
    return this.append(test);
  }
}

class ColorTest extends Test {
  constructor(color) {
    super();
    this.color = color;
  }
  run(cell) {
    return view[cell].color === this.color;
  }
}
class ColorBandTest extends SomeTest {
  constructor(colorBand) {
    super(...colorBand.map((color) => new ColorTest(color)));
  }
}

class FoodTest extends Test {
  constructor(hasFood = true) {
    super();
    this.food = hasFood ? 1 : 0;
  }
  run(cell) {
    return view[cell].food === this.food;
  }
}

class AntTest extends Test {
  constructor(friend, type, food) {
    super();
    this.friend = friend;
    this.type = type;
    this.food = food;
  }
  run(cell) {
    const ant = view[cell].ant;
    return ant !== null && (this.type === undefined || ant.type === this.type) && (this.friend === undefined || ant.friend === this.friend) && (this.food === undefined || (this.food ? ant.food > 0 : ant.food === 0));
  }
}

class NeighborTest extends Test {
  constructor(test) {
    super();
    this.test = test;
  }
  run(cell) {
    return this.test.some(NEIGHBORS[cell]);
  }
}

class MatchTest extends Test {
  constructor(matches) {
    super();
    this.matches = matches;
  }
  run(cell) {
    return this.matches[cell];
  }
}

class CustomTest extends Test {
  constructor(func, ...args) {
    super();
    this.func = func;
    this.args = args;
  }
  run(cell) {
    return this.func(cell, ...this.args);
  }
}

class Action {
  constructor(cell, test) {
    this.cell = cell;
    this.test = test;
  }

  valid() {
    return this.cell >= 0 && this.cell < 9 && (!this.test || this.test.run(this.cell));
  }
  attempt() {
    return this.valid() ? this : null;
  }
  static tryAll(...actions) {
    return actions.find((action) => action instanceof this && action.valid()) || null;
  }
}
class Move extends Action {
  constructor(cell, test) {
    super(cell, test);
  }

  valid() {
    return super.valid() && view[this.cell].ant === null && (view[this.cell].food === 0 || ME.food === 0 || ME.type === QUEEN);
  }
  static many(cells, test) {
    return cells.map((cell) => new this(cell, test));
  }
}
class Paint extends Action {
  constructor(cell, color, test) {
    super(cell, test);
    this.color = color;
  }

  valid() {
    return super.valid() && view[this.cell].color !== this.color && this.color >= 1 && this.color <= 8;
  }
  static many(cells, colors, test) {
    return cells.map((cell, i) => new this(cell, colors[i % colors.length], test));
  }
}
class Spawn extends Action {
  constructor(cell, type, test) {
    super(cell, test);
    this.type = type;
  }

  valid() {
    return super.valid() && view[this.cell].ant === null && view[this.cell].food === 0 && ME.food > 0 && ME.type === QUEEN && this.type >= 1 && this.type <= 4;
  }
  static many(cells, type, test) {
    return cells.map((cell, i) => new this(cell, type, test));
  }
}
class NOP extends Action {
  constructor() {
    super(CENTER);
  }

  valid() {
    return true;
  }
}

class Context {
  apply(func, ...args) {
    const hiddenView = view;
    if (this.viewTranslator) {
      view = this.viewTranslator(hiddenView);
    }

    let output = func(...args);
    if (output instanceof Action && this.outputTranslator) {
      this.outputTranslator(output);
    }

    view = hiddenView;
    return output;
  }
}

class TranslationContext extends Context {
  constructor(translationArray) {
    super();
    this.translationArray = translationArray;
  }

  viewTranslator(oldView) {
    const newView = [];
    for (let i = 0; i < 9; i++) {
      newView.push(oldView[this.translationArray[i]]);
    }
    return newView;
  }

  outputTranslator(out) {
    out.cell = this.translationArray[out.cell];
  }
}
class RotationContext extends TranslationContext {
  constructor(orientation) {
    super(ROTATIONS[orientation]);
    this.orientation = orientation;
  }
}
class OffsetContext extends TranslationContext {
  constructor(centerCell) {
    throw "OffsetContext not implemented";
  }
}
class HorizontalReflectionContext extends TranslationContext {
  constructor() {
    super(HORIZONTAL_FLIP);
  }
}
class VerticalReflectionContext extends TranslationContext {
  constructor() {
    super(VERTICAL_FLIP);
  }
}

class ColorMapContext extends Context {
  constructor(map, unmap) {
    super();
    this.map = map;
    this.unmap = unmap;
  }

  viewTranslator(oldView) {
    return oldView.map((cell) => ({color: this.map[cell.color - 1], food: cell.food, ant: cell.ant}));
  }

  outputTranslator(out) {
    out.color = this.unmap[out.color - 1];
  }
}

class XY {
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
  }

  static fromTuples(...xyTuples) {
    return xyTuples.map((xy) => new this(xy[0], xy[1]));
  }
}

class WrapProperties {
  constructor(horizontal, vertical, size, wrapOffsets) {
    this.horizontal = !!horizontal;
    this.vertical = !!vertical;
    this.size = size;
    this.wrapOffsets = wrapOffsets || {};
  }
}

class ScoredTest {
  constructor(test, score = 1) {
    this.test = test;
    this.score = score;
  }

  run(cell) {
    return this.test.run(cell) ? this.score : 0;
  }
}

class Environment {
  constructor(tests, wrapping) {
    this.tests = tests.map((test) => test instanceof Test ? new ScoredTest(test) : test);
    this.wrapping = wrapping;
  }

  at(x, y) {
    const w = this.wrapping;
    while ((w.horizontal && (x < 0 || x >= w.size.x)) || (w.vertical && (y < 0 || y >= w.size.y))) {
      if (w.horizontal) {
        if (x < 0) {
          x += w.size.x;
          y += w.wrapOffsets.left || 0;
        } else if (x >= w.size.x) {
          x -= w.size.x;
          y += w.wrapOffsets.right || 0;
        }
      }

      if (w.vertical) {
        if (y < 0) {
          y += w.size.y;
          x += w.wrapOffsets.up || 0;
        } else if (y >= w.size.y) {
          y -= w.size.y;
          x += w.wrapOffsets.down || 0;
        }
      }
    }

    if ((!w.horizontal || (x >= 0 && x < w.size.x)) && (!w.vertical || (y >= 0 || y < w.size.y))) {
      return this.tests[x + y * w.size.x];
    } else {
      return null;
    }
  }
  around(x, y) {
    const arr = [];
    for (let oy = -1; oy <= 1; oy++) {
      for (let ox = -1; ox <= 1; ox++) {
        arr.push(this.at(x + ox, y + oy));
      }
    }
    return arr;
  }

  detect(...positions) {
    return createArray((i) => new RotationContext(i), 4).reduce((best, context) => {
      const next = context.apply(() => {
        return positions.reduce((best, pos, i) => {
          let score = 0;
          const matches = this.around(pos.x, pos.y).map((test, i) => {
            if (test && (test.test instanceof Test)) {
              const result = test.run(i);
              if (result) {
                score += result;
                return true;
              } else {
                return false;
              }
            } else {
              return null;
            }
          });
          if (score > best.score) {
            return {position: pos, positionIndex: i, orientation: context.orientation, environment: this, matches: matches, score: score, confidence: score - best.score};
          } else {
            best.confidence = Math.min(best.score - score, best.confidence);
            return best;
          }
        }, {position: positions[0], positionIndex: 0, orientation: 0, environment: this, matches: [], score: 0, confidence: 0});
      });
      if (next.score > best.score) {
        next.confidence = next.score - best.score;
        return next;
      } else {
        best.confidence = Math.min(best.score - next.score, best.confidence);
        return best;
      }
    }, {position: positions[0], positionIndex: 0, orientation: 0, environment: this, matches: [], score: 0, confidence: 0});
  }

  static chooseBest(...detectionResults) {
    const r = detectionResults.reduce((best, result, i) => {
      if (result.score > best.score) {
        result.index = i;
        result.confidence = result.score - best.score;
        return result;
      } else {
        best.confidence = Math.min(best.score - result.score, best.confidence);
        return best;
      }
    });
    r.index = r.index || 0;
    return r;
  }
}

class ColoredEnvironment extends Environment {
  constructor(colors, wrapping) {
    super(colors.map((color) => new ColorTest(color)), wrapping);
  }

  getPainter(detectionResult) {
    return new (class Painter {
      constructor(loc) {
        this.pos = loc.position;
        this.matches = loc.matches;
        this.colors = loc.environment.around(this.pos.x, this.pos.y).map((test) => test && test.test instanceof ColorTest ? test.test.color : null);
        this.test = new MatchTest(loc.matches).invert();
        this.orient = loc.orientation;
      }

      paint(...cells) {
        return cells.map((cell) => new Paint(cell, this.colors[cell], this.test));
      }
      cleanup(eraseColor, eraseTargets, ...cells) {
        const eraseQual = this.test.and(new ColorBandTest(eraseTargets));
        return cells.map((cell) => new Paint(cell, eraseColor, eraseQual));
      }
    })(detectionResult);
  }
}

// HIGH-LEVEL LOGIC STARTS HERE //
const PARTNER = 1;

// TODO: Do a 180 when 3 workers are in front of us
function logicOrthogonal(frontC, sideC, backC, backCells, moveCells) {
  const a = [frontC, sideC, backC, backCells, moveCells];
  const f = new FoodTest;
  return Action.tryAll(
    ...Move.many([frontC, sideC], f),
    f.some(backCells) ? new Move(backC) : null,
    ...Move.many(moveCells),
    new NOP
  );
}
function logicDiagonal(adjacentC) {
  return Action.tryAll(...Move.many(adjacentC), new NOP);
}
function logic(partnerTest, partnerOrthC, partnerDiagC, frontC, sideC, backC, backCells, moveCells, adjacentC) {
  function detectEnv(c) {
    return new Environment(createArray((i) => i === c ? partnerTest : undefined, 9), new WrapProperties(false, false, new XY(3, 3), null)).detect(new XY(1, 1));
  }
  const orth = detectEnv(partnerOrthC);
  const diag = detectEnv(partnerDiagC);
  return orth.score === 1 ? new RotationContext(orth.orientation).apply(() => logicOrthogonal(frontC, sideC, backC, backCells, moveCells)) : 
    diag.score === 1 ? new RotationContext(diag.orientation).apply(() => logicDiagonal(adjacentC)) : 
    error("How did we get here?");
}

if (ME.type === QUEEN) {
  const partner = new AntTest(true, PARTNER);
  if (partner.some(DIRECTIONS)) {
    return logic(partner, 5, 8, 2, 1, 7, [0, 3, 6, 7], [2, 7, 1, 8], [5, 7]);
  } else {
    const COLOR = 5;
    const bgTest = new ColorTest(WHITE);
    if (bgTest.run(CENTER)) {
      return new Paint(CENTER, COLOR).attempt() || error("Something went terribly wrong while painting own cell");
    }

    const food = new FoodTest().find(DIRECTIONS);
    if (food !== undefined) {
      return new Move(food);
    }

    const det = new ColoredEnvironment([
      WHITE, WHITE, WHITE, 
      WHITE, undefined, undefined,
      WHITE, undefined, COLOR
    ], new WrapProperties(false, false, new XY(3, 3))).detect(new XY(1, 1));
    return (ME.food > 0 ? Action.tryAll(...Spawn.many(ORTHOGONALS, PARTNER)) : null) ||
      (det.score === 6 ? new RotationContext(det.orientation).apply(() => Action.tryAll(...Move.many([0, 2, 6, 1, 3, 5, 7, 8]))) : null) ||
      Action.tryAll(...Move.many(DIAGONALS_ORTHOGONALS), new NOP);
  }
} else {
  return logic(new AntTest(true, QUEEN), 1, 0, 2, 5, 3, [8, 7, 6, 3], [2, 3, 5, 0], [1, 3]);
}

Пояснення

Це такий простий мураш, що я приголомшений, ніхто про це ніколи не думав ...

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

Версія 2.0+ вийшла! У коді є коментарі, що пояснюють специфіку цього запису.


Журнал змін

Версія 1.0

  • початковий випуск

Версія 2.0

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

Версія 2.1

  • вирішили екзотичний глухий кут

Версія 2.2

  • вирішили ще один екзотичний тупик

Версія 2.3

  • уникнення ворогів зараз значно ефективніше

Версія 2.3.1

  • уникати ворогів все-таки трохи ефективніше

Версія 2.4

  • Formic Framework оновлено до версії 5.0.4 (з 1.0)
  • реконструйований код (поведінка майже ідентична версії 2.3.1)

Версія 2.5

  • Formic Framework оновлено до версії 6.1.10 (з 5.0.4)
  • рефакторований код, який відповідає новому стандарту кодування (поведінка майже ідентична версії 2.4)
  • видалено коментарі до коду :(

Версія 2.5.0.1

  • незначні виправлення

Версія 2.5.0.2

  • виправлена ​​помилка дискваліфікації за допомогою dzaima в чаті

Версія 2.5.0.3

  • відключена налагодження

8

Стежка-гумка

var i, j
var orthogonals = [1, 3, 7, 5]  // These are the non-diagonal cells
if(view[4].ant.type == 5) {
//Queen moves straight to get food
// Color own cell if white
if (view[4].color != 6) { 
    return {cell:4, color:6}
}
var specified = null;
// Otherwise move to a white cell opposite a colored cell
for (i=0; i<4; i++) {
    j = (i+2) % 4
    if (view[orthogonals[i]].color !== 6 &&
        view[orthogonals[j]].color == 6 && !view[orthogonals[i]].ant) {
        specified = {cell:orthogonals[i]}
    } else if (view[4].ant.food < 8 && view[4].ant.food && view[orthogonals[i]].color !== 6 && !view[orthogonals[i]].ant && !view[orthogonals[i]].food && view[orthogonals[i]].color !== 1) {
        //create workers once I encounter a trail
        return {cell:orthogonals[i], type:(view[orthogonals[i]].color%4)+1};
    } else if (view[orthogonals[i]].food) {
        return {cell:orthogonals[i]}
    }
}
if(specified) { return specified; }
// Otherwise move to one of the vertical or horizontal cells if not occupied
for (i=1; i<9; i+=2) {
    if (!view[i].ant) {
        return {cell:i}
    }
}

// Otherwise move to one of the diagonal cells if not occupied
for (i=0; i<9; i+=2) {
    if (!view[i].ant) {
        return {cell:i};
    }
}

// Otherwise don't move at all
return {cell:4};
}
//workers erase their trails
//Follow the trail to erase
var move, color, enemyAnt = false;
var nearbyColoredCells = 0;
if(view[4].color != 1){
   color =  {cell:4, color:1}
}
for(i=0;i<9;i++) {
    if(i != 4 && view[i].color != 1 && !view[i].ant && (!view[4].ant.food || !view[i].food) && (!move || (view[i].color % 4 + 1) == view[4].type || (view[move.cell].color == 6 && view[i].color != 6))) {
        move = {cell:i}
    }
    if(view[i].ant && view[i].ant.friend && view[i].ant.type == 5){
       return {cell:4}
    }
    if(i != 4 && view[i].color != 1 && view[i].color != 6){
        nearbyColoredCells += 1;
    }
    if(view[i].ant && !view[i].ant.friend) {
         enemyAnt = i;
    }
}
if(nearbyColoredCells <= 1 || enemyAnt > 1) {
    // Either I'm following a standard trail or there are enemy workers; possibly decolor own cell and move
    if(color && (!move || !enemyAnt)) { return color; }
    if(move) { return move; }
} else if (nearbyColoredCells > 1){
   for (i = 0; i < 9; i++){
       if(view[i].color != 1){ return {cell:i, color:1} }
   }
}
// uh-oh, our trail ended or we got lost -- random walk
// find a safe place to move
for (i=0;i<9;i+=1) {
    if (!view[i].ant && (!view[4].ant.food || !view[i].food)) {
       return {cell:i}
    }
}
return {cell:4}

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


1
Диверсія! 😮 Що вам робили колись римляни?
Дейв

Цей запис дуже весело возиться з чорними дірами ...
Шалений Лі

2
@Dave Вони подали змагальну заявку на цей виклик ...
pppery

2
Так, цей гравець іноді витрачає всю свою їжу, так як римські мурахи
заплутаються

2
Друге оновлення: на початку більше не зберігається їжа, натомість починає збирати їжу, а не саботувати пізніше
pppery

8

Гіперхвиля

Усі мої відповіді будуть містити подібну логіку низького рівня у формі Рамки форматичних функцій. "ЛОГІЧНІ ПОЧАТКИ ВИСОКОГО РОЗВИТКУ ТУТ" позначає кінець коду Рамки.

 //  FORMIC FRAMEWORK  //
// Version 6.1.10     //
const WHITE = 1;
const QUEEN = 5;
const CENTER = 4;
const HERE = view[CENTER];
const ME = HERE.ant;
const ORTHOGONALS = [1, 3, 5, 7];
const DIAGONALS = [0, 2, 6, 8];
const DIAGONALS_ORTHOGONALS = [0, 2, 6, 8, 1, 3, 5, 7];
const DIRECTIONS = [0, 1, 2, 3, 5, 6, 7, 8];
const CLOCKWISE_DIRECTIONS = [0, 1, 2, 5, 8, 7, 6, 3];
const CELLS = [0, 1, 2, 3, 4, 5, 6, 7, 8];
const ROTATIONS = [
  [0, 1, 2,
   3, 4, 5,
   6, 7, 8],

  [6, 3, 0,
   7, 4, 1,
   8, 5, 2],

  [8, 7, 6,
   5, 4, 3,
   2, 1, 0],

  [2, 5, 8,
   1, 4, 7,
   0, 3, 6]
];
const NEIGHBORS = [
  [1, 4, 3],
  [2, 5, 4, 3, 0],
  [5, 4, 1],
  [0, 1, 4, 7, 6],
  [0, 1, 2, 5, 8, 7, 6, 3],
  [8, 7, 4, 1, 2],
  [3, 4, 7],
  [6, 3, 4, 5, 8],
  [7, 4, 5]
];
const HORIZONTAL_FLIP = [2, 1, 0, 5, 4, 3, 8, 7, 6];
const VERTICAL_FLIP = [6, 7, 8, 3, 4, 5, 0, 1, 2];

const DEBUG_MODE = true;
function dump() {
  if (DEBUG_MODE) {
    throw "dump() not implemented";
  }
}
function log(...args) {
  if (DEBUG_MODE) {
    console.log(...args);
  }
}
function error(...args) {
  log("Transformed view state:", view);
  log(...args);
  throw "A critical error has occurred!";
}

function createArray(func, length) {
  const arr = [];
  for (let i = 0; i < length; i++) {
    arr.push(func(i, arr));
  }
  return arr;
}

class Test {
  run(cell) {
    error("No run method defined for this instance of Test:", this);
  }
  find(cells = CELLS) {
    return cells.find((c) => this.run(c));
  }
  findIndex(cells = CELLS) {
    return cells.findIndex((c) => this.run(c));
  }
  filter(cells = CELLS) {
    return cells.filter((c) => this.run(c));
  }
  every(cells = CELLS) {
    return cells.every((c) => this.run(c));
  }
  some(cells = CELLS) {
    return cells.some((c) => this.run(c));
  }
  count(cells = CELLS) {
    return this.filter(cells).length;
  }
  invert() {
    return new InverseTest(this);
  }
  and(test) {
    return new EveryTest(this, test);
  }
  or(test) {
    return new SomeTest(this, test);
  }
}

class InverseTest extends Test {
  constructor(test) {
    super();
    this.test = test;
  }
  run(cell) {
    return !this.test.run(cell);
  }
  invert() {
    return this.test;
  }
}

class CombinedTest extends Test {
  constructor(...tests) {
    super();
    this.tests = tests;
  }
  append(test) {
    this.tests.push(test);
    return this;
  }
}
class EveryTest extends CombinedTest {
  run(cell) {
    return this.tests.every((test) => test.run(cell));
  }
  and(test) {
    return this.append(test);
  }
}
class SomeTest extends CombinedTest {
  run(cell) {
    return this.tests.some((test) => test.run(cell));
  }
  or(test) {
    return this.append(test);
  }
}

class ColorTest extends Test {
  constructor(color) {
    super();
    this.color = color;
  }
  run(cell) {
    return view[cell].color === this.color;
  }
}
class ColorBandTest extends SomeTest {
  constructor(colorBand) {
    super(...colorBand.map((color) => new ColorTest(color)));
  }
}

class FoodTest extends Test {
  constructor(hasFood = true) {
    super();
    this.food = hasFood ? 1 : 0;
  }
  run(cell) {
    return view[cell].food === this.food;
  }
}

class AntTest extends Test {
  constructor(friend, type, food) {
    super();
    this.friend = friend;
    this.type = type;
    this.food = food;
  }
  run(cell) {
    const ant = view[cell].ant;
    return ant !== null && (this.type === undefined || ant.type === this.type) && (this.friend === undefined || ant.friend === this.friend) && (this.food === undefined || (this.food ? ant.food > 0 : ant.food === 0));
  }
}

class NeighborTest extends Test {
  constructor(test) {
    super();
    this.test = test;
  }
  run(cell) {
    return this.test.some(NEIGHBORS[cell]);
  }
}

class MatchTest extends Test {
  constructor(matches) {
    super();
    this.matches = matches;
  }
  run(cell) {
    return this.matches[cell];
  }
}

class CustomTest extends Test {
  constructor(func, ...args) {
    super();
    this.func = func;
    this.args = args;
  }
  run(cell) {
    return this.func(cell, ...this.args);
  }
}

class Action {
  constructor(cell, test) {
    this.cell = cell;
    this.test = test;
  }

  valid() {
    return this.cell >= 0 && this.cell < 9 && (!this.test || this.test.run(this.cell));
  }
  attempt() {
    return this.valid() ? this : null;
  }
  static tryAll(...actions) {
    return actions.find((action) => action instanceof this && action.valid()) || null;
  }
}
class Move extends Action {
  constructor(cell, test) {
    super(cell, test);
  }

  valid() {
    return super.valid() && view[this.cell].ant === null && (view[this.cell].food === 0 || ME.food === 0 || ME.type === QUEEN);
  }
  static many(cells, test) {
    return cells.map((cell) => new this(cell, test));
  }
}
class Paint extends Action {
  constructor(cell, color, test) {
    super(cell, test);
    this.color = color;
  }

  valid() {
    return super.valid() && view[this.cell].color !== this.color && this.color >= 1 && this.color <= 8;
  }
  static many(cells, colors, test) {
    return cells.map((cell, i) => new this(cell, colors[i % colors.length], test));
  }
}
class Spawn extends Action {
  constructor(cell, type, test) {
    super(cell, test);
    this.type = type;
  }

  valid() {
    return super.valid() && view[this.cell].ant === null && view[this.cell].food === 0 && ME.food > 0 && ME.type === QUEEN && this.type >= 1 && this.type <= 4;
  }
  static many(cells, type, test) {
    return cells.map((cell, i) => new this(cell, type, test));
  }
}
class NOP extends Action {
  constructor() {
    super(CENTER);
  }

  valid() {
    return true;
  }
}

class Context {
  apply(func, ...args) {
    const hiddenView = view;
    if (this.viewTranslator) {
      view = this.viewTranslator(hiddenView);
    }

    let output = func(...args);
    if (output instanceof Action && this.outputTranslator) {
      this.outputTranslator(output);
    }

    view = hiddenView;
    return output;
  }
}

class TranslationContext extends Context {
  constructor(translationArray) {
    super();
    this.translationArray = translationArray;
  }

  viewTranslator(oldView) {
    const newView = [];
    for (let i = 0; i < 9; i++) {
      newView.push(oldView[this.translationArray[i]]);
    }
    return newView;
  }

  outputTranslator(out) {
    out.cell = this.translationArray[out.cell];
  }
}
class RotationContext extends TranslationContext {
  constructor(orientation) {
    super(ROTATIONS[orientation]);
    this.orientation = orientation;
  }
}
class OffsetContext extends TranslationContext {
  constructor(centerCell) {
    throw "OffsetContext not implemented";
  }
}
class HorizontalReflectionContext extends TranslationContext {
  constructor() {
    super(HORIZONTAL_FLIP);
  }
}
class VerticalReflectionContext extends TranslationContext {
  constructor() {
    super(VERTICAL_FLIP);
  }
}

class ColorMapContext extends Context {
  constructor(map, unmap) {
    super();
    this.map = map;
    this.unmap = unmap;
  }

  viewTranslator(oldView) {
    return oldView.map((cell) => ({color: this.map[cell.color - 1], food: cell.food, ant: cell.ant}));
  }

  outputTranslator(out) {
    out.color = this.unmap[out.color - 1];
  }
}

class XY {
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
  }

  static fromTuples(...xyTuples) {
    return xyTuples.map((xy) => new this(xy[0], xy[1]));
  }
}

class WrapProperties {
  constructor(horizontal, vertical, size, wrapOffsets) {
    this.horizontal = !!horizontal;
    this.vertical = !!vertical;
    this.size = size;
    this.wrapOffsets = wrapOffsets || {};
  }
}

class ScoredTest {
  constructor(test, score = 1) {
    this.test = test;
    this.score = score;
  }

  run(cell) {
    return this.test.run(cell) ? this.score : 0;
  }
}

class Environment {
  constructor(tests, wrapping) {
    this.tests = tests.map((test) => test instanceof Test ? new ScoredTest(test) : test);
    this.wrapping = wrapping;
  }

  at(x, y) {
    const w = this.wrapping;
    while ((w.horizontal && (x < 0 || x >= w.size.x)) || (w.vertical && (y < 0 || y >= w.size.y))) {
      if (w.horizontal) {
        if (x < 0) {
          x += w.size.x;
          y += w.wrapOffsets.left || 0;
        } else if (x >= w.size.x) {
          x -= w.size.x;
          y += w.wrapOffsets.right || 0;
        }
      }

      if (w.vertical) {
        if (y < 0) {
          y += w.size.y;
          x += w.wrapOffsets.up || 0;
        } else if (y >= w.size.y) {
          y -= w.size.y;
          x += w.wrapOffsets.down || 0;
        }
      }
    }

    if ((!w.horizontal || (x >= 0 && x < w.size.x)) && (!w.vertical || (y >= 0 || y < w.size.y))) {
      return this.tests[x + y * w.size.x];
    } else {
      return null;
    }
  }
  around(x, y) {
    const arr = [];
    for (let oy = -1; oy <= 1; oy++) {
      for (let ox = -1; ox <= 1; ox++) {
        arr.push(this.at(x + ox, y + oy));
      }
    }
    return arr;
  }

  detect(...positions) {
    return createArray((i) => new RotationContext(i), 4).reduce((best, context) => {
      const next = context.apply(() => {
        return positions.reduce((best, pos, i) => {
          let score = 0;
          const matches = this.around(pos.x, pos.y).map((test, i) => {
            if (test && (test.test instanceof Test)) {
              const result = test.run(i);
              if (result) {
                score += result;
                return true;
              } else {
                return false;
              }
            } else {
              return null;
            }
          });
          if (score > best.score) {
            return {position: pos, positionIndex: i, orientation: context.orientation, environment: this, matches: matches, score: score, confidence: score - best.score};
          } else {
            best.confidence = Math.min(best.score - score, best.confidence);
            return best;
          }
        }, {position: positions[0], positionIndex: 0, orientation: 0, environment: this, matches: [], score: 0, confidence: 0});
      });
      if (next.score > best.score) {
        next.confidence = next.score - best.score;
        return next;
      } else {
        best.confidence = Math.min(best.score - next.score, best.confidence);
        return best;
      }
    }, {position: positions[0], positionIndex: 0, orientation: 0, environment: this, matches: [], score: 0, confidence: 0});
  }

  static chooseBest(...detectionResults) {
    const r = detectionResults.reduce((best, result, i) => {
      if (result.score > best.score) {
        result.index = i;
        result.confidence = result.score - best.score;
        return result;
      } else {
        best.confidence = Math.min(best.score - result.score, best.confidence);
        return best;
      }
    });
    r.index = r.index || 0;
    return r;
  }
}

class ColoredEnvironment extends Environment {
  constructor(colors, wrapping) {
    super(colors.map((color) => new ColorTest(color)), wrapping);
  }

  getPainter(detectionResult) {
    return new (class Painter {
      constructor(loc) {
        this.pos = loc.position;
        this.matches = loc.matches;
        this.colors = loc.environment.around(this.pos.x, this.pos.y).map((test) => test && test.test instanceof ColorTest ? test.test.color : null);
        this.test = new MatchTest(loc.matches).invert();
        this.orient = loc.orientation;
      }

      paint(...cells) {
        return cells.map((cell) => new Paint(cell, this.colors[cell], this.test));
      }
      cleanup(eraseColor, eraseTargets, ...cells) {
        const eraseQual = this.test.and(new ColorBandTest(eraseTargets));
        return cells.map((cell) => new Paint(cell, eraseColor, eraseQual));
      }
    })(detectionResult);
  }
}

// HIGH-LEVEL LOGIC STARTS HERE //
// TODO:
// - more food checkpoints (no disadvantages because it's illogical for WFW to "premanently lose" workers)
// - randomly shifting 1 up (5% chance? watch out for hoarding stealing your randomness!)
// - randomly skip painting a tiny bit of local cells (prevent deadlock against ants outside of view)
// - escape routine when situation is dire (many workers near the queen/partner)

const COLOR_BAND = [4, 7, 3, 2, 8];

const PARTNER = 2;
const WORKER = 1;

const START_FOOD = 6;
const MIN_CONFIDENCE = 2;

const PATTERN = new ColoredEnvironment(COLOR_BAND, new WrapProperties(true, true, new XY(COLOR_BAND.length, 1), {up: 2, down: -2})).detect(...createArray((i) => new XY(i, 0), COLOR_BAND.length));

function checkpoint(val, tolerance) {
  return ME.food >= val - tolerance && ME.food <= val;
}
function shouldSpawn() {
  return PATTERN.orientation === 0 && PATTERN.positionIndex % 3 === 0 &&
    ME.food < 400 &&
    (ME.food < 75 || PATTERN.positionIndex === 0) && 
    !checkpoint(300, 4) &&
    !checkpoint(200, 5) &&
    !checkpoint(160, 3) &&
    !checkpoint(130, 2) &&
    !checkpoint(100, 2) &&
    !checkpoint(75, 2) &&
    !checkpoint(50, 2) &&
    !checkpoint(35, 1) &&
    !checkpoint(20, 1) &&
    !checkpoint(10, 0);
}

function lightspeed() {
  // TODO: Do a 180 when 3 workers are in front of us
  function logicOrthogonal(frontC, sideC, backC, backCells, moveCells) {
    const a = [frontC, sideC, backC, backCells, moveCells];
    const f = new FoodTest;
    return Action.tryAll(
      ...Move.many([frontC, sideC], f),
      f.some(backCells) ? new Move(backC) : null,
      ...Move.many(moveCells),
      new NOP
    );
  }
  function logicDiagonal(adjacentC) {
    return Action.tryAll(...Move.many(adjacentC), new NOP);
  }
  function logic(partnerTest, partnerOrthC, partnerDiagC, frontC, sideC, backC, backCells, moveCells, adjacentC) {
    function detectEnv(c) {
      return new Environment(createArray((i) => i === c ? partnerTest : undefined, 9), new WrapProperties(false, false, new XY(3, 3), null)).detect(new XY(1, 1));
    }
    const orth = detectEnv(partnerOrthC);
    const diag = detectEnv(partnerDiagC);
    return orth.score === 1 ? new RotationContext(orth.orientation).apply(() => logicOrthogonal(frontC, sideC, backC, backCells, moveCells)) : 
      diag.score === 1 ? new RotationContext(diag.orientation).apply(() => logicDiagonal(adjacentC)) : 
      error("How did we get here?");
  }

  if (ME.type === QUEEN) {
    const partner = new AntTest(true, PARTNER);
    if (partner.some(DIRECTIONS)) {
      return logic(partner, 5, 8, 2, 1, 7, [0, 3, 6, 7], [2, 7, 1, 8], [5, 7]);
    } else {
      const COLOR = 5;
      const bgTest = new ColorTest(WHITE);
      if (bgTest.run(CENTER)) {
        return new Paint(CENTER, COLOR).attempt() || error("Something went terribly wrong while painting own cell");
      }

      const food = new FoodTest().find(DIRECTIONS);
      if (food !== undefined) {
        return new Move(food);
      }

      const det = new ColoredEnvironment([
        WHITE, WHITE, WHITE, 
        WHITE, undefined, undefined,
        WHITE, undefined, COLOR
      ], new WrapProperties(false, false, new XY(3, 3))).detect(new XY(1, 1));
      return (ME.food > 0 ? Action.tryAll(...Spawn.many(ORTHOGONALS, PARTNER)) : null) ||
        (det.score === 6 ? new RotationContext(det.orientation).apply(() => Action.tryAll(...Move.many([0, 2, 6, 1, 3, 5, 7, 8]))) : null) ||
        Action.tryAll(...Move.many(DIAGONALS_ORTHOGONALS), new NOP);
    }
  } else {
    return logic(new AntTest(true, QUEEN), 1, 0, 2, 5, 3, [8, 7, 6, 3], [2, 3, 5, 0], [1, 3]);
  }
}

function queen() {
  const partnerTest = new AntTest(true, PARTNER);
  if (PATTERN.confidence < MIN_CONFIDENCE && (ME.food < START_FOOD || partnerTest.some(DIAGONALS))) return lightspeed();
  return new RotationContext(PATTERN.orientation).apply(() => {
    const partnerCell = new AntTest(true, PARTNER).find(DIRECTIONS);
    const p = PATTERN.environment.getPainter(PATTERN);
    const e = new AntTest(false);
    const enemy = e.some(DIRECTIONS);
    return Action.tryAll(
      ...!PATTERN.matches[8] ? [
        ...!enemy ? [...p.paint(7, 4, 5, 1, 2), ...shouldSpawn() && PATTERN.score === 8 ? Spawn.many([0, 2], WORKER) : []] : [],
        ...partnerCell === 1 ? Move.many(e.run(5) ? [0, 2] : PATTERN.orientation === 1 && PATTERN.positionIndex % 3 === 1 ? [2, 0, 5] : [5, 2, 0]) : 
          partnerCell === 0 ? Move.many(e.run(1) ? [3] : enemy ? [1, 3] : []) :
          partnerCell === 2 ? Move.many(e.run(1) ? [5] : []) :
          []
      ] : [
        ...!enemy ? p.paint(8, 7, 6, 5, 4, 3, 2, 1, 0) : [],
        ...Move.many(partnerCell === 1 ? [2, 0] : partnerCell === 0 ? [1, 3] : partnerCell === 2 && e.run(1) ? [5] : [])
      ],
      new NOP
    )
  });
}
function partner() {
  const queenTest = new AntTest(true, QUEEN)
  const queenCell = queenTest.find(DIRECTIONS);
  if (queenCell === undefined) {
    return new NOP; // TODO: What do we do if we've lost our queen?
  }
  if (PATTERN.confidence < MIN_CONFIDENCE && (view[queenCell].ant.food < START_FOOD || DIAGONALS.includes(queenCell))) return lightspeed();
  return PATTERN.confidence >= MIN_CONFIDENCE ? new RotationContext(PATTERN.orientation).apply(() => {
    const queenCell = queenTest.find(DIRECTIONS);
    const e = new AntTest(false);
    const enemy = e.some(DIRECTIONS);
    return Action.tryAll(
      ...!enemy ? PATTERN.environment.getPainter(PATTERN).paint(...CELLS) : [],
      ...Move.many([
        [1],
        [0, 2],
        [1],
        [0, 1],
        [], // Queen can't be on cell 4 - I'm here, after all!
        [2, 1],
        [3],
        [],
        [5]
      ][queenCell]),
      new NOP
    )
  }) : new NOP;
}
function worker() {
  const m = new MatchTest(PATTERN.matches);
  const n = m.invert();
  const u = new AntTest(true, WORKER, false);
  const l = new AntTest(true, WORKER, true);
  const q = new AntTest(true, QUEEN);
  const pt = new AntTest(true, PARTNER);
  const p = PATTERN.environment.getPainter(PATTERN);
  return new RotationContext(PATTERN.orientation).apply(() => { // TODO: Unique (random?) behavior when confidence low
    if (ME.food === 0) {
      const f = new FoodTest();
      const count = n.count([6, 7, 8]);
      return Action.tryAll(
        ...PATTERN.confidence >= 2 ? p.paint(4, 0, 1, 2) : [],
        //...p.cleanup(WHITE, COLOR_BAND, ...CELLS),
        ...((food) => food !== undefined ? [...p.paint(...NEIGHBORS[food], food), new Move(food)] : [])(f.find(DIRECTIONS)),
        ...q.or(pt).some(DIRECTIONS) || u.some([6, 7, 8, 5, 2]) ? Move.many([0, 1, 3], m) : [],
        ...count > 1 ? p.paint(...[6, 7, 8]) : [],
        ...count === 1 ? [...p.paint(...[3, 5]), ...Move.many([7, 8, 6, 3], m)] : [],
        /*n.run(6) ? new Move(3, m) : null,
        ...n.run(7) ? Move.many([6, 3], m) : [],
        n.run(8) ? new Move(7, m) : null,*/
        n.run(5) ? new Move(5) : null,
        ...Move.many([2, 1, 0, 3], m),
        new NOP
        /*
        ...(PATTERN.confidence >= 2 ? [...(PATTERN.score < 8 || new AntTest(false).some(DIRECTIONS) ? p.paint(4, 3, 0, 1, 2, 5) : []), ...p.cleanup(WHITE, COLOR_BAND, ...CELLS)] : []),
        ...((food) => food !== undefined ? [...p.paint(...NEIGHBORS[food]), new Move(food)] : [])(f.find(DIRECTIONS)), ...(
          w ? Move.many([1, 0, 2]) :
          m.some([4, 3]) ? p.paint(4, 3) :
          m.some([0, 1, 2]) ? Move.many([1, 5]) :
          m.run(6) ? [new Move(3)] :
          m.run(7) ? Move.many([6, 3]) :
          m.run(8) ? [new Move(7)] :
          m.run(5) ? Move.many([5, 7]) :
          Move.many([2, 1, 5])
        )
        new NOP*/
      );
    } else {
      return Action.tryAll(
        //...Move.many(new AntTest(true, WORKER).some([2, 5, 8, 7]) || PATTERN.score < 9 ? [8, 7, 6, 3] : [5, 8, 2], m.invert()),
        ...((test) => createArray((i) => new Move(CLOCKWISE_DIRECTIONS[(6 - i) % 8], test), 5))(new CustomTest((cell, moveTest, blockTest) => {
          const i = CLOCKWISE_DIRECTIONS.findIndex((c) => c === cell);
          return moveTest.run(CLOCKWISE_DIRECTIONS[i]) && blockTest.run(CLOCKWISE_DIRECTIONS[((i - 1) + 8) % 8]);
        }, m, n.or(new AntTest().and(l.invert())))),
        ...Move.many([2, 5, 1, 8], m),
        //...Move.many([...(PATTERN.score === 9 && !new AntTest(true, WORKER).some(DIRECTIONS) ? [2] : []), 5, 8, 7, 6, 3], m),
        new NOP
      );
    }
  });
}

switch (ME.type) {
  case QUEEN: {
    return queen();
  }
  case PARTNER: {
    return partner();
  }
  case WORKER: {
    return worker();
  }
}

Я тимчасово видалив увесь опис цього запису через часовий тиск. Пізніше я додам ґрунтовний опис цього основного оновлення.


Журнал змін

Версія 1.0

  • початковий випуск

Версія 2.0

  • замінив Шосе на Гіперхвилю

Версія 2.0.1

  • механізм накопичення з гарячим фіксом

Цей починає красиво дивитися, потім стає жахливим ...
trichoplax

1
Час прийняти вампіричний едикт проти доріг.
Draco18s

І я буду тільки годувати Вампіра більше, ніж протікає час @ Draco18s - я хочу зробити багато вдосконалень.
Аліон

3
Перший запис, який я бачив, який може повністю поглинати дошку протягом стандартного часу гри. Дуже вражає.
Дейв

7

Римські мурашки Мк.2

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

// == Shared low-level helpers for all solutions ==

var QUEEN = 5;

var WHITE = 1;
var COL_MIN = WHITE;
var COL_LIM = 9;

var CENTRE = 4;

var NOP = {cell: CENTRE};

var DIR_FORWARDS = false;
var DIR_REVERSE = true;
var SIDE_RIGHT = true;
var SIDE_LEFT = false;

function sanity_check(movement) {
  var me = view[CENTRE].ant;
  if(!movement || movement.cell < 0 || movement.cell > 8) {
    return false;
  }
  if(movement.type) {
    if(movement.color) {
      return false;
    }
    if(movement.type < 1 || movement.type > 4) {
      return false;
    }
    if(view[movement.cell].ant || view[movement.cell].food) {
      return false;
    }
    if(me.type !== QUEEN || me.food < 1) {
      return false;
    }
    return true;
  }
  if(movement.color) {
    if(movement.color < COL_MIN || movement.color >= COL_LIM) {
      return false;
    }
    if(view[movement.cell].color === movement.color) {
      return false;
    }
    return true;
  }
  if(view[movement.cell].ant) {
    return false;
  }
  if(view[movement.cell].food + me.food > 1 && me.type !== QUEEN) {
    return false;
  }
  return true;
}

function as_array(o) {
  if(Array.isArray(o)) {
    return o;
  }
  return [o];
}

function best_of(movements) {
  var m;
  for(var i = 0; i < movements.length; ++ i) {
    if(typeof(movements[i]) === 'function') {
      m = movements[i]();
    } else {
      m = movements[i];
    }
    if(sanity_check(m)) {
      return m;
    }
  }
  return null;
}

function play_safe(movement) {
  // Avoid disqualification: no-op if moves are invalid
  return best_of(as_array(movement)) || NOP;
}

var RAND_SEED = (() => {
  var s = 0;
  for(var i = 0; i < 9; ++ i) {
    s += view[i].color * (i + 1);
    s += view[i].ant ? i * i : 0;
    s += view[i].food ? i * i * i : 0;
  }
  return s % 29;
})();

var ROTATIONS = [
  [0, 1, 2, 3, 4, 5, 6, 7, 8],
  [6, 3, 0, 7, 4, 1, 8, 5, 2],
  [8, 7, 6, 5, 4, 3, 2, 1, 0],
  [2, 5, 8, 1, 4, 7, 0, 3, 6],
];

function try_all(fns, limit, wrapperFn, checkFn) {
  var m;
  fns = as_array(fns);
  for(var i = 0; i < fns.length; ++ i) {
    if(typeof(fns[i]) !== 'function') {
      if(checkFn(m = fns[i])) {
        return m;
      }
      continue;
    }
    for(var j = 0; j < limit; ++ j) {
      if(checkFn(m = wrapperFn(fns[i], j))) {
        return m;
      }
    }
  }
  return null;
}

function identify_rotation(testFns) {
  // testFns MUST be functions, not constants
  return try_all(
    testFns,
    4,
    (fn, r) => fn(ROTATIONS[r]) ? ROTATIONS[r] : null,
    (r) => r
  );
}

function near(a, b) {
  return (
    Math.abs(a % 3 - b % 3) < 2 &&
    Math.abs(Math.floor(a / 3) - Math.floor(b / 3)) < 2
  );
}

function try_all_angles(solverFns) {
  return try_all(
    solverFns,
    4,
    (fn, r) => fn(ROTATIONS[r]),
    sanity_check
  );
}

function try_all_cells(solverFns, skipCentre) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i === CENTRE && skipCentre) ? null : fn(i)),
    sanity_check
  );
}

function try_all_cells_near(p, solverFns) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i !== p && near(p, i)) ? fn(i) : null),
    sanity_check
  );
}

function ant_type_at(i, friend) {
  return (view[i].ant && view[i].ant.friend === friend) ? view[i].ant.type : 0;
}

function friend_at(i) {
  return ant_type_at(i, true);
}

function foe_at(i) {
  return ant_type_at(i, false);
}

function foe_near(p) {
  for(var i = 0; i < 9; ++ i) {
    if(foe_at(i) && near(i, p)) {
      return true;
    }
  }
  return false;
}

function move_agent(agents) {
  var me = view[CENTRE].ant;
  var buddies = [0, 0, 0, 0, 0, 0];
  for(var i = 0; i < 9; ++ i) {
    ++ buddies[friend_at(i)];
  }

  for(var i = 0; i < agents.length; i += 2) {
    if(agents[i] === me.type) {
      return agents[i+1](me, buddies);
    }
  }
  return null;
}

function grab_nearby_food() {
  return try_all_cells((i) => (view[i].food ? {cell: i} : null), true);
}

function go_anywhere() {
  return try_all_cells((i) => ({cell: i}), true);
}

function colours_excluding(cols) {
  var r = [];
  for(var i = COL_MIN; i < COL_LIM; ++ i) {
    if(cols.indexOf(i) === -1) {
      r.push(i);
    }
  }
  return r;
}

function generate_band(start, width) {
  var r = [];
  for(var i = 0; i < width; ++ i) {
    r.push(start + i);
  }
  return r;
}

function colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function(c) {
      return colours[(colours.indexOf(c) + 1) % colours.length];
    }
  };
}

function random_colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function() {
      return colours[RAND_SEED % colours.length];
    }
  };
}

function fast_diagonal(colourBand) {
  var m = try_all_angles([
    // Avoid nearby checked areas
    (rot) => {
      if(
        !colourBand.contains(view[rot[0]].color) &&
        colourBand.contains(view[rot[5]].color) &&
        colourBand.contains(view[rot[7]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // Go in a straight diagonal line if possible
    (rot) => {
      if(
        !colourBand.contains(view[rot[0]].color) &&
        colourBand.contains(view[rot[8]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // When in doubt, pick randomly but avoid doubling-back
    (rot) => (colourBand.contains(view[rot[0]].color) ? null : {cell: rot[0]}),

    // Double-back when absolutely necessary
    (rot) => ({cell: rot[0]})
  ]);

  // Lay a colour track so that we can avoid doubling-back
  // (and mess up our foes as much as possible)
  if(!colourBand.contains(view[CENTRE].color)) {
    var prevCol = m ? view[8-m.cell].color : WHITE;
    return {cell: CENTRE, color: colourBand.next(prevCol)};
  }

  return m;
}

function follow_edge(obstacleFn, side) {
  // Since we don't know which direction we came from, this can cause us to get
  // stuck on islands, but the random orientation helps to ensure we don't get
  // stuck forever.

  var order = ((side === SIDE_LEFT)
    ? [0, 3, 6, 7, 8, 5, 2, 1, 0]
    : [0, 1, 2, 5, 8, 7, 6, 3, 0]
  );
  return try_all(
    [obstacleFn],
    order.length - 1,
    (fn, i) => (fn(order[i+1]) && !fn(order[i])) ? {cell: order[i]} : null,
    sanity_check
  );
}

function start_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => ((
      !protectedCols.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 2 : 0]].color) &&
      !colourBand.contains(view[rot[1]].color)
    )
      ? {cell: rot[right ? 5 : 3], color: colourBand.next(WHITE)}
      : null)
  ]);
}

function lay_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => {
      var ahead = rot[right ? 2 : 0];
      var behind = rot[right ? 8 : 6];
      if(
        colourBand.contains(view[behind].color) &&
        !protectedCols.contains(view[ahead].color) &&
        !colourBand.contains(view[ahead].color) &&
        !colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        return {cell: ahead, color: colourBand.next(view[behind].color)};
      }
    }
  ]);
}

function follow_dotted_path(colourBand, side, direction) {
  var forwards = (direction === DIR_REVERSE) ? 7 : 1;
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    // Cell on our side? advance
    (rot) => {
      if(
        colourBand.contains(view[rot[right ? 5 : 3]].color) &&
        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[0]].color) &&
        !colourBand.contains(view[rot[2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    },

    // Cell ahead and behind? advance
    (rot) => {
      var passedCol = view[rot[right ? 8 : 6]].color;
      var nextCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(passedCol) &&
        nextCol === colourBand.next(passedCol) &&

        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[right ? 0 : 2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    }
  ]);
}

function escape_dotted_path(colourBand, side, newColourBand) {
  var right = (side === SIDE_RIGHT);
  if(!newColourBand) {
    newColourBand = colourBand;
  }

  return try_all_angles([
    // Escape from beside the line
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        !colourBand.contains(view[rot[right ? 8 : 6]].color) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[rot[7]].color) ||
        colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        // not oriented, or in a corner
        return null;
      }
      return best_of([
        {cell: rot[right ? 0 : 2], color: newColourBand.next(approachingCol)},
        {cell: rot[right ? 3 : 5]},
        {cell: rot[right ? 0 : 2]},
        {cell: rot[right ? 6 : 8]},
        {cell: rot[right ? 2 : 0]},
        {cell: rot[right ? 8 : 6]},
        {cell: rot[right ? 5 : 3]}
      ]);
    },

    // Escape from inside the line
    (rot) => {
      if(
        !colourBand.contains(view[rot[7]].color) ||
        !colourBand.contains(view[rot[1]].color) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      return best_of([
        {cell: rot[3]},
        {cell: rot[5]},
        {cell: rot[0]},
        {cell: rot[2]},
        {cell: rot[6]},
        {cell: rot[8]}
      ]);
    }
  ]);
}

function latch_to_dotted_path(colourBand, side) {
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(approachingCol) &&
        view[rot[right ? 8 : 6]].color === colourBand.next(approachingCol) &&
        !colourBand.contains(view[rot[right ? 5 : 3]].color)
      ) {
        // We're on the wrong side; go inside the line
        return {cell: rot[right ? 5 : 3]};
      }
    },

    // Inside the line? pick a side
    (rot) => {
      var passedCol = view[rot[7]].color;
      var approachingCol = view[rot[1]].color;
      if(
        !colourBand.contains(passedCol) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      if((approachingCol === colourBand.next(passedCol)) === right) {
        return best_of([{cell: rot[3]}, {cell: rot[6]}, {cell: rot[0]}]);
      } else {
        return best_of([{cell: rot[5]}, {cell: rot[2]}, {cell: rot[8]}]);
      }
    }
  ]);
}


// == High-level logic begins here ==


var QUEEN_COL = colour_band(generate_band(3, 3));
var WORKER_COL = colour_band(generate_band(6, 3));
var AVOID_COL = colour_band([2]);
var INVERT_COL = colour_band([WHITE, 2]);

var MIN_FOOD = 5;
var MAX_WORKER_FOOD = 10;

function decide() {
  var me = view[CENTRE].ant;
  var queen = me.type === QUEEN;

  if(queen && me.food > MIN_FOOD && me.food < MAX_WORKER_FOOD) {
    var m = try_all_cells((i) => {
      if(view[i].food && !foe_near(i)) {
        // Try to spawn a worker next to the food;
        // the worker will pick up the food on the next turn
        return try_all_cells_near(i, (j) => ({cell: j, type: 1}));
      }
    }, true);
    if(sanity_check(m)) {
      return m;
    }
  }

  if(!queen && me.food) {
    return best_of([
      // Look for queen
      follow_dotted_path.bind(null, QUEEN_COL, SIDE_RIGHT, DIR_FORWARDS),
      latch_to_dotted_path.bind(null, QUEEN_COL, SIDE_RIGHT),

      // Failed to find queen's trail; try following worker trails backwards
      follow_dotted_path.bind(null, WORKER_COL, SIDE_RIGHT, DIR_REVERSE),
      latch_to_dotted_path.bind(null, WORKER_COL, SIDE_RIGHT),

      // Failed to find any trail; cover ground as quickly as possible
      fast_diagonal.bind(null, AVOID_COL)
    ]);
  }

  var myCol = queen ? QUEEN_COL : WORKER_COL;
  return best_of([
    grab_nearby_food,

    // Disperse workers away from queen
    !queen && escape_dotted_path.bind(null, QUEEN_COL, SIDE_RIGHT, WORKER_COL),

    // Follow our own path
    follow_dotted_path.bind(null, myCol, SIDE_RIGHT, DIR_FORWARDS),

    // If our path looks suspicious, it could have wrapped; try to escape it
    escape_dotted_path.bind(null, myCol, SIDE_RIGHT),

    // Explore
    // Laying a path causes us to move at 2/3 c, so workers can catch up.
    lay_dotted_path.bind(null, myCol, SIDE_RIGHT, QUEEN_COL),
    start_dotted_path.bind(null, myCol, SIDE_RIGHT, QUEEN_COL),

    // Fall-back to white dots if we're inside a colour block
    lay_dotted_path.bind(null, myCol, SIDE_RIGHT, INVERT_COL),
    start_dotted_path.bind(null, myCol, SIDE_RIGHT, INVERT_COL),

    // Stuck for some reason; try to escape
    fast_diagonal.bind(null, AVOID_COL)
  ]);
}

return play_safe([
  decide,

  // No valid moves; try to find *anywhere* we can go.
  go_anywhere,

  // Try changing a nearby cell's colour for the heck of it.
  {cell: 1, color: view[1].color % 8 + 1}
]);

Римські мурахи люблять будувати дороги, прямо і швидко. Оскільки вони будують пунктирні дороги, вони здатні рухатися зі швидкістю світла на 2/3! Їх дороги також використовують велосипедні кольори, щоб вони знали, слідкують вони за дорогою вперед або назад, а робочі мурахи використовують інший набір кольорів, ніж королева (робітники починають нереститися після того, як королева має неприємну кількість їжа доступна). У сукупності це означає, що коли робочий мураш знайде їжу, він може простежити свій власний шлях назад, а потім простежити шлях королеви вперед, щоб повернути їжу (під час простеження шляху мурахи рухаються зі швидкістю світла).

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

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

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

Нарешті, існує метод перевірки здоровості, застосований до будь-якого виводу, який гарантує, що це не може бути дискваліфіковано (сподіваємось!)


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


(ось один я підготував, поки це було в бета-версії )
Дейв,

Приблизно за розміром: я щойно запустив це і отримав 30 продуктів харчування + 6 робочих мурашок. Це приблизно узгоджується з цифрами, які я бачив під час його розвитку.
Дейв

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

1
@trichoplax oops; Я забув вкласти це в пояснення (забув, що вони це зробили!). Я оновив пояснення. Коротше кажучи: це частина плану, але це не великий план.
Дейв

Чудове пояснення!
трихоплакс

7

Монорельс

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

var c0=5 //red
var c1=4 //cyan
var c2=6 //green
var c3=3 //magenta
var c4=7 //blue
var c5=8 //black
var c6=2 //yellow
var cN=1 //white

var ws=4 //support
var wb=3 //bodyguard
var wg=1 //gather
var wq=5 //queen

var v=[[0, 1, 2, 3, 4, 5, 6, 7, 8],
       [8, 7, 6, 5, 4, 3, 2, 1, 0],
       [6, 3, 0, 7, 4, 1, 8, 5, 2],
       [2, 5, 8, 1, 4, 7, 0, 3, 6]]

var x1=[0,2,6,8]
var x2=[8,6,2,0]

var r1=[1,7,3,5]
var r2=[5,3,7,1]

switch(view[4].ant.type)
{
 case 5: return queen()
 case 4: return support()
 case 3: return bodyguard()
 case 1: return gather()
 default: return {cell:4}
}

function queen()
{
 if(fAlly(ws)<0)
 {
  if(view[4].color==c1&&view[4].ant.food>0)
  {
   var o=findOrient(c1,c2,c3)
   if(sOpen(v[o][7])) return {cell:v[o][7],type:ws}
   return doThing()
  }
  else if(view[4].color==c0&&view[4].ant.food>=6)
  {
   var o=findOrient(c1,c2,c3)
   if(view[v[o][1]].color!=c1) return {cell:v[o][1],color:c1}
   if(view[v[o][0]].color!=c2) return {cell:v[o][0],color:c2}
   if(view[v[o][2]].color!=c3) return {cell:v[o][2],color:c3}
   if(sOpen(v[o][7])) return {cell:v[o][7],type:ws}
  }
  return wander(c0)
 }
 else
 {
  if(view[4].color==c0&&view[4].ant.food<7)
  {
   if(view[4].ant.food >=5)
   {
    var o=findOrient(c1,c2,c3)
    if(sOpen(v[o][1])) return {cell:v[o][1],type:wb}
    if(sOpen(v[o][0])) return {cell:v[o][0],type:wb}
    if(sOpen(v[o][2])) return {cell:v[o][2],type:wb}
   }
   if(view[4].ant.food>0)
   {
    var op=fOpen()
    if(op>=0) return {cell:op,type:wg}
   }
  }
  return doThing()
 }
}

function doThing()
{
 if(view[4].color!=c1) return {cell:4,color:c1}
 var o=findOrient(c1,c2,c3)
 if(view[4].ant.food>0&&fAlly(wg)>=0&&o==0)
 {
  var op=fOpen()
  if(op>=0) return {cell:op,type:wg}
 }
 if(view[v[o][1]].color!=c1&&pootis(v[o][1])) return {cell:v[o][1],color:c1}
 if(view[v[o][0]].color!=c2&&pootis(v[o][0])) return {cell:v[o][0],color:c2}
 if(view[v[o][2]].color!=c3&&pootis(v[o][2])) return {cell:v[o][2],color:c3}
 if(mOpen(v[o][1])) return {cell:v[o][1]}
 if(view[v[o][3]].color!=c2&&pootis(v[o][3])) return {cell:v[o][3],color:c2}
 if(view[v[o][5]].color!=c3&&pootis(v[o][5])) return {cell:v[o][5],color:c3}
 if(view[v[o][6]].color!=c2&&pootis(v[o][6])) return {cell:v[o][6],color:c2}
 if(view[v[o][8]].color!=c3&&pootis(v[o][8])) return {cell:v[o][8],color:c3}
 if(view[v[o][7]].color!=c1&&pootis(v[o][7])) return {cell:v[o][7],color:c1}
 return {cell:4}
}

function support()
{
 var o = findOrient(c1,c2,c3)
 var p = fAlly(wq)

 switch(p)
 {
  case v[o][0]:
   //if(mOpen(v[o][3])) return {cell:v[o][3]}
   //if(mOpen(v[o][1])) return {cell:v[o][1]}
   break
  case v[o][1]:
   //if(view[v[o][0]].food&&mOpen([v[o][0]])) return {cell:v[o][0]}
   //if(view[v[o][2]].food&&mOpen([v[o][2]])) return {cell:v[o][2]}
   break
  case v[o][2]:
   //if(mOpen(v[o][5])) return {cell:v[o][5]}
   //if(mOpen(v[o][1])) return {cell:v[o][1]}
   break
  case v[o][3]:
   //if(mOpen(v[o][6])) return {cell:v[o][6]}
   break
  case v[o][5]:
   //if(mOpen(v[o][8])) return {cell:v[o][8]}
   break
  case v[o][6]: break
  case v[o][7]: break
  case v[o][8]: break
  default:
   if(sOpen(v[o][1])) return {cell:v[o][1]}
   break
 }
 return {cell:4}
}

function bodyguard()
{
 var o=0
 switch(view[4].color)
 {
  case c1: o=findOrient(c1,c2,c3);break
  case c2: o=findOrient(c2,c6,c1);break
  case c3: o=findOrient(c3,c1,c6);break
  default: return {cell:4}
 }
 var p = fAlly(wq)
 if(view[4].ant.food>0)
 {
  switch(p)
  {
   case v[o][0]:
    if(sOpen(v[o][1])) return {cell:v[o][1]}
    if(sOpen(v[o][3])) return {cell:v[o][3]}
    break
   case v[o][1]:
    if(sOpen([v[o][0]])) return {cell:v[o][0]}
    if(sOpen([v[o][2]])) return {cell:v[o][2]}
    break
   case v[o][2]:
    if(sOpen(v[o][1])) return {cell:v[o][1]}
    if(sOpen(v[o][5])) return {cell:v[o][5]}
    break
   case v[o][3]:
    if(sOpen([v[o][0]])) return {cell:v[o][0]}
    if(sOpen([v[o][1]])) return {cell:v[o][1]}
    break
   case v[o][5]:
    if(sOpen([v[o][2]])) return {cell:v[o][2]}
    if(sOpen([v[o][1]])) return {cell:v[o][1]}
    break
   case v[o][6]:
    if(sOpen([v[o][0]])) return {cell:v[o][0]}
    if(sOpen([v[o][1]])) return {cell:v[o][1]}
    if(sOpen([v[o][3]])) return {cell:v[o][3]}
    break
   case v[o][7]:
    if(sOpen([v[o][1]])) return {cell:v[o][1]}
    if(sOpen([v[o][0]])) return {cell:v[o][0]}
    if(sOpen([v[o][2]])) return {cell:v[o][2]}
    break
   case v[o][8]:
    if(sOpen([v[o][2]])) return {cell:v[o][2]}
    if(sOpen([v[o][1]])) return {cell:v[o][1]}
    if(sOpen([v[o][5]])) return {cell:v[o][5]}
    break
   default:
  }
 }
 else
 {
  switch(p)
  {
   case v[o][0]:
    if(mOpen(v[o][1])) return {cell:v[o][1]}
    if(mOpen(v[o][3])) return {cell:v[o][3]}
    break
   case v[o][1]:
    if(view[v[o][2]].food&&mOpen([v[o][2]])) return {cell:v[o][2]}
    if(mOpen([v[o][0]])) return {cell:v[o][0]}
    if(mOpen([v[o][2]])) return {cell:v[o][2]}
    break
   case v[o][2]:
    if(mOpen(v[o][1])) return {cell:v[o][1]}
    if(mOpen(v[o][5])) return {cell:v[o][5]}
    break
   case v[o][3]:
    if(view[v[o][1]].food&&mOpen([v[o][1]])) return {cell:v[o][1]}
    if(mOpen([v[o][0]])) return {cell:v[o][0]}
    if(mOpen([v[o][1]])) return {cell:v[o][1]}
    break
   case v[o][5]:
    if(view[v[o][1]].food&&mOpen([v[o][1]])) return {cell:v[o][1]}
    if(mOpen([v[o][2]])) return {cell:v[o][2]}
    if(mOpen([v[o][1]])) return {cell:v[o][1]}
    break
   case v[o][6]:
    if(view[v[o][1]].food&&mOpen([v[o][1]])) return {cell:v[o][1]}
    if(mOpen([v[o][0]])) return {cell:v[o][0]}
    if(mOpen([v[o][1]])) return {cell:v[o][1]}
    if(mOpen([v[o][3]])) return {cell:v[o][3]}
    break
   case v[o][7]:
    if(view[v[o][0]].food&&mOpen([v[o][0]])) return {cell:v[o][0]}
    if(view[v[o][2]].food&&mOpen([v[o][2]])) return {cell:v[o][2]}
    if(mOpen([v[o][1]])) return {cell:v[o][1]}
    if(mOpen([v[o][0]])) return {cell:v[o][0]}
    if(mOpen([v[o][2]])) return {cell:v[o][2]}
    break
   case v[o][8]:
    if(view[v[o][1]].food&&mOpen([v[o][1]])) return {cell:v[o][1]}
    if(mOpen([v[o][2]])) return {cell:v[o][2]}
    if(mOpen([v[o][1]])) return {cell:v[o][1]}
    if(mOpen([v[o][5]])) return {cell:v[o][5]}
    break
   default:
  }
 }
 if(view[4].color==c1)
 {
  if(view[v[o][1]].color!=c1&&pootis(v[o][1])) return {cell:v[o][1],color:c1}
  if(view[v[o][0]].color!=c2&&pootis(v[o][0])) return {cell:v[o][0],color:c2}
  if(view[v[o][2]].color!=c3&&pootis(v[o][2])) return {cell:v[o][2],color:c3}
  if(view[v[o][3]].color!=c2) return {cell:v[o][3],color:c2}
  if(view[v[o][5]].color!=c3) return {cell:v[o][5],color:c3}
  if(view[v[o][6]].color!=c2) return {cell:v[o][6],color:c2}
  if(view[v[o][8]].color!=c3) return {cell:v[o][8],color:c3}
  if(view[v[o][7]].color!=c1) return {cell:v[o][7],color:c1}
 }
 return {cell:4}
}

function gather()
{
 if(view[4].ant.food>0)
 {
  if(view[4].color==c1&&(testSQ(c1,c2)||testSQ(c1,c3))) return gDoThing(c1,c3,c2)
  if(view[4].color==c2&&testSQ(c2,c1)) return gDoThing(c2,c1,c6)
  if(view[4].color==c3&&testSQ(c3,c1)) return gDoThing(c3,c6,c1)
  return wander(c5)
 }
 var n=fAlly(wq)
 if(n<0) return wander(c4)
 switch(n)
 {
  case 0:
   if(mOpen(8)) return {cell:8}
   if(mOpen(7)) return {cell:7}
   if(mOpen(5)) return {cell:5}
   break
  case 1:
   if(mOpen(6)) return {cell:6}
   if(mOpen(8)) return {cell:8}
   if(mOpen(7)) return {cell:7}
   break
  case 2:
   if(mOpen(6)) return {cell:6}
   if(mOpen(3)) return {cell:3}
   if(mOpen(7)) return {cell:7}
   break
  case 3:
   if(mOpen(8)) return {cell:8}
   if(mOpen(2)) return {cell:2}
   if(mOpen(5)) return {cell:5}
   break
  case 5:
   if(mOpen(0)) return {cell:0}
   if(mOpen(6)) return {cell:6}
   if(mOpen(3)) return {cell:3}
   break
  case 6:
   if(mOpen(2)) return {cell:2}
   if(mOpen(5)) return {cell:5}
   if(mOpen(1)) return {cell:1}
   break
  case 7:
   if(mOpen(2)) return {cell:2}
   if(mOpen(0)) return {cell:0}
   if(mOpen(1)) return {cell:1}
   break
  case 8:
   if(mOpen(0)) return {cell:0}
   if(mOpen(1)) return {cell:1}
   if(mOpen(3)) return {cell:3}
   break
  default:
   break
 }
 return {cell:4}
}

function testSQ(ac1,ac2)
{
 for(var i=0;i<4;i++)
 {
  if(view[r1[i]].color==ac1)
  {
   if(view[v[i][0]].color==ac2&&view[v[i][3]].color==ac2) return 1
   if(view[v[i][2]].color==ac2&&view[v[i][5]].color==ac2) return 1
  }
 }
 return 0
}

function gDoThing(ac1,ac2,ac3)
{
 var o=findOrient(ac1,ac2,ac3)
 if(view[v[o][1]].color!=ac1&&pootis(v[o][1])) return {cell:v[o][1],color:ac1}
 if(view[v[o][0]].color!=ac2&&pootis(v[o][0])) return {cell:v[o][0],color:ac2}
 if(view[v[o][2]].color!=ac3&&pootis(v[o][2])) return {cell:v[o][2],color:ac3}
 if(sOpen(v[o][1])) return {cell:v[o][1]}
 if(ac1==c2)
 {
  if(sOpen(v[o][0])) return {cell:v[o][0]}
  if(sOpen(v[o][3])) return {cell:v[o][3]}
  if(sOpen(v[o][6])) return {cell:v[o][6]}
 }
 else if(ac1==c3)
 {
  if(sOpen(v[o][2])) return {cell:v[o][2]}
  if(sOpen(v[o][5])) return {cell:v[o][5]}
  if(sOpen(v[o][8])) return {cell:v[o][8]}
 }
 else
 {
  if(sOpen(v[o][0])) return {cell:v[o][0]}
  if(sOpen(v[o][2])) return {cell:v[o][2]}
  if(sOpen(v[o][3])) return {cell:v[o][3]}
  if(sOpen(v[o][5])) return {cell:v[o][5]}
 }
 return {cell:4}
}

function pootis(p)
{
 var a=view[p].ant
 if(a!=null&&a.friend&&a.type==wg&&(view[p].color==c4||view[p].color==c5)) return 0
 return 1
}

function fAlly(t)
{
 var a=view[4].ant
 for(var i=0;i<9;i++)
 {
  if(i==4) i++
  a=view[i].ant
  if(a!=null&&a.friend&&a.type==t) return i
 }
 return -1
}

function fOpen()
{
 for(var i=0;i<9;i++)
 {
  if(i==4) i++
  if(sOpen(i)) return i
 }
 return -1
}

function sOpen(p)
{
 return (view[p].ant==null&&!view[p].food)
}

function mOpen(p)
{
 return (view[p].ant==null)
}

function wander(ac)
{
 if(view[4].ant.type==5||view[4].ant.food==0)
 {
  var vf = vFood()
  if(vf>=0) return {cell:vf}
  if(view[4].color!=ac) return {cell:4,color:ac}
  for(var i=0;i<4;i++)
  {
   if(view[x1[i]].color==ac&&view[x2[i]].color!=ac&&view[x2[i]].color!=c1&&mOpen(x2[i])) return {cell:x2[i]}
  }
  for(var i=0;i<4;i++)
  {
   if(mOpen(x1[i])&&view[x1[i]].color!=c1) return {cell:x1[i]}
  }
 }
 else
 {
  if(view[4].color!=ac) return {cell:4,color:ac}
  for(var i=0;i<4;i++)
  {
   if(view[x1[i]].color==ac&&view[x2[i]].color!=ac&&sOpen(x2[i])) return {cell:x2[i]}
  }
  for(var i=0;i<4;i++)
  {
   if(sOpen(x1[i])) return {cell:x1[i]}
  }
 }
 return {cell:4}
}

function findOrient(ac1,ac2,ac3)
{
 var w=[0,0,0,0]
 w[0]=DI(view[0].color,view[1].color,view[2].color,0,ac1,ac2,ac3)
 w[1]=DI(view[8].color,view[7].color,view[6].color,1,ac1,ac2,ac3)
 w[2]=DI(view[6].color,view[3].color,view[0].color,2,ac1,ac2,ac3)
 w[3]=DI(view[2].color,view[5].color,view[8].color,3,ac1,ac2,ac3)
 var t=[0,0,0,0]
 for(var i=0;i<4;i++)
 {
  switch(w[i])
  {
   case 4: t[0]++;break
   case 5: t[1]++;break
   case 6: t[2]++;break
   case 7: t[3]++;break
   case 8: t[0]+=2;break
   case 9: t[1]+=2;break
   case 10: t[2]+=2;break
   case 11: t[3]+=2;break
   case 12: t[0]+=3;break
   case 13: t[1]+=3;break
   case 14: t[2]+=3;break
   case 15: t[3]+=3;break
   default: break
  }
 }
 var m=Math.max(...t)
 for(var i=0;i<4;i++)
 {
  if(t[i]==m) return i
 }
 return 0
}

function DI(v1,v2,v3,d,ac1,ac2,ac3)
{
 var t=[0,0,0,0]
 switch(v1)
 {
  case ac2: t[0]++;t[3]++;break
  case ac3: t[1]++;t[2]++;break
  default: break
 }
 switch(v2)
 {
  case ac1: t[0]++;t[1]++;break
  case ac2: t[3]++;break
  case ac3: t[2]++;break
  default: break
 }
 switch(v3)
 {
  case ac2: t[1]++;t[3]++;break
  case ac3: t[0]++;t[2]++;break
  default: break
 }
 var m=Math.max(...t)
 if(m==0) return 0
 var n=0
 for(var i=0;i<4;i++)
 {
  if(t[i]==m){n=i;break}
 }
 if((d==2&&n==2)||(d==3&&n==3)) n=1
 else if((d==2&&n==3)||(d==3&&n==2)) n=0
 else n^=d
 return m*4+n
}

function vFood()
{
 for(var i=0;i<9;i++)
 {
  if(view[i].food) return i
 }
 return -1
}

function nColor(c)
{
 var t=0
 for(var i=0;i<9;i++)
 {
  if(view[i].color==c) t++
 }
 return t
}

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

Гаразд, ось версія 2. Зараз збирачі насправді щось роблять.

Версія 2.1 зараз. Жовта / блакитна мітка була змінена, щоб Чорна Діра не змогла з'їсти весь слід будь-коли, коли вони з'являться разом в одній грі.

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

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

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


Поки є нові записи / редагування, я буду проводити нові турніри, щоб побачити, як виглядає таблиця лідерів. Дати закриття немає. У поточного турніру може залишитися кілька днів, перш ніж він сходиться на унікальне 1-е, 2-е та 3-е місце, тоді я розпочну новий турнір, включаючи цього нового гравця. Я боюся, що це займе тиждень, перш ніж він покаже, до якої позиції ти займаєш, але я дам тобі знати, коли це станеться.
трихоплакс

@QuoteBeta Я розумію вашу параноїю ... Я щойно бачив це виклик, тому я перебуваю в процесі створення запису до того, як момент виклику припиниться.
Moogie

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

Більшість людей, мабуть, зроблені, але ми чекаємо на лідерах. І це потребує певного часу.
Draco18s

Схоже, ваші мурахи можуть захотіти дещо з мого коду очищення стежок (див. Чорна діра, як мурахи, загублені в салоні, очищають старі стежки). i.stack.imgur.com/DTmA6.png
Draco18s

6

Клаустрофобічна королева

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

var i, j
var orthogonals = [1, 3, 7, 5]  // These are the non-diagonal cells
var move;
var scores = [];   // An array of how desirable each potential move is
var score, neighbor, claustrophobia, newColor;
var crowdedNeighbors = null;   // How many diagonal neighbors are colored CROWDED?
var runningFrom = null;    // When in running phase, which direction did we come from?
var runningTo = null;      // When in running phase, which direction should we head?

// Assign color magic numbers to variables
var EMPTY = 1;
var VISITED = 4;
var CROWDED = 7;
var RUNNING = 8;

function neighbors(cell) {
    switch (cell) {
        case 0: return [1, 3];
        case 1: return [0, 2];
        case 2: return [1, 5];
        case 3: return [0, 6];
        case 4: return orthogonals;
        case 5: return [2, 8];
        case 6: return [3, 7];
        case 7: return [6, 8];
        case 8: return [7, 5];
        default: return null;
    }
}

function isHungry(ant) {
    if (ant.type === 5 || ant.food === 0) {
        return true;
    } else {
        return false;
    }
}

// Color own cell based on the number of neighbors that are colored
claustrophobia = 0;
for (i=0; i<9; i++) {
    if (view[i].color !== EMPTY || i === 4) {
        claustrophobia++;
    }
    if (i % 2 === 0 && i !== 4) {
        if (view[i].color === CROWDED) {
            crowdedNeighbors++;
        } else if (view[i].color === RUNNING) {
            crowdedNeighbors++;
            runningFrom = i;
        }
    }
}

if (claustrophobia > 4) {
    if (crowdedNeighbors > 1 || runningFrom !== null) {
        // We're entering or currently in a straight-line running state
        // in which we keep going until we find sufficient whitespace
        newColor = RUNNING;
    } else {
        newColor = CROWDED;
    }
} else {
    newColor = VISITED;
}
if (view[4].color !== newColor) {
    return {cell:4, color:newColor}
}

// If we've already colored the current cell properly, and we're in running mode,
// then move diametrically away from the runningFrom cell
switch (runningFrom) {
    case 0:
        runningTo = 8;
        break;
    case 2:
        runningTo = 6;
        break;
    case 6:
        runningTo = 2;
        break;
    case 8:
        runningTo = 0;
        break;
    default:
        break;
}

// Calculate a score for each potential move
// Lower numbers are better; null means illegal move
// Unexplored areas are better; food is the best (as long as ant can eat); don't move onto other ants
for (i=0; i<9; i++) {
    // Base score of tile is 2 times color of tile
    score = 2 * (view[i].color);
    // Add colors of neighboring tiles
    for (neighbor of neighbors(i)) {
        score += view[neighbor].color;
    }
    // Give very good score to runningTo tile, unless it's also RUNNING color
    if (i === runningTo && view[i].color !== RUNNING) {
        score -= 4;   // Magic number, possibly to be tweaked
    }
    if (i!==4 && view[i].ant) {
        // Tile contains another ant; give very bad score
        score = null;
    } else if (view[i].food) {
        // If a tile contains food, it's either highly desirable if the ant can eat, or illegal if it can't
        if (isHungry(view[4].ant)) {
            // Ant can eat; give food tile very good score
            score = -1;
        } else {
            // Ant cannot eat; give food tile very bad score
            score = null;
        }
    }
    scores.push(score);
}

// Select best move based on the scores array
move = 4;   // By default, stay put (this probably won't be the best move)
for (i=0; i<9; i++) {
    if (scores[i] !== null && scores[i] < scores[move]) {
        move = i;
    }
}

return {cell:move};

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

введіть тут опис зображення


Не захищений від несанкціонованого впливу стежка-гумка може стерти свій слід, що призведе мурашника до кола по колах
pppery

1
Е, в квадрати?
pppery

1
@ppperry Я сказав: " помірно ... захист від несанкціонованого захисту". ;) Єдине, що може зробити Trail-Eraser, це зробити це більш імовірним покриттям землі, яка вже була вкрита; але це так само легко може продовжувати просуватися на невивчену територію.
DLosc

6

Хоппемавр

Перш за все, я хочу подякувати Trichoplax за створення цього дивовижного виклику, який змусив мене зареєструватися на цьому сайті та почати програмувати в JavaScript. Хочу також подякувати вам, що інші люди все ще ховаються в чаті цього питання, навіть рівно через 3 місяці після його запитання.

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

Деталі бота Hoppemaur з намальованим візерунком

Код

//Hoppemaur
//å hoppe: to jump
//maur: ant

var WHITE = 1;
var OWN = 2;
var FOOD = 6;
var ESCAPE = 3;
var paint = 0;
var score = {0:2,1:-3,2:2,3:-3,4:-1,5:-3,6:2,7:-3,8:2}; 
// The ant should only walk diagonal (except when feeding)
var highest = 0;
var scoreindex;
var diagonals = [0, 2, 6, 8];
var diagonalsandself = [0, 2, 4, 6, 8]
var inversdiagonals = [8, 6, 2, 0];
var orthogonals = [1, 3, 5, 7];
var inverseorthogonals = [7, 5, 3, 1];
var rotate1orthogonals = [3, 1, 7, 5];
var rotate2orthogonals = [5, 7, 1, 3];

//checks if one of the diagonals or the own tile are painted
function checkforemptypattern() {
  for (var i=0; i<diagonalsandself.length; i++) {
    if (view[diagonalsandself[i]].color == OWN){
     return false;}
  }
  return true;
}

//counts the diagonals painted in the requestest colour
function checktrapped(pattern) {
  var diags=0;
  for (var i=0; i<diagonals.length; i++) {
    if (view[diagonals[i]].color == pattern){
     diags=diags+1;
    }
  }
return diags;
}

//Biggest threat to this ant is food on orthogonals,
//it messes up the pattern if not dealt with it
if (view[4].color !== FOOD){
    for (var i=0; i<orthogonals.length; i++) {
        if (view[orthogonals[i]].food) {
            return {cell:4, color:FOOD};
            }
        }
    }

if (view[4].color == FOOD){
    for (var i=0; i<orthogonals.length; i++) {
        if (view[orthogonals[i]].food) {
        if (!view[orthogonals[i]].ant){
            return {cell:orthogonals[i]};
            }
        }
        }
    }

//If food shows up on diagonals while out of pattern,
//before grabbing food, the pattern must be painted
for (var i=0; i<diagonals.length; i++){
    if (view[diagonals[i]].food){
        if (checkforemptypattern()){
            return {cell:4, color: OWN}
            }
        }
    }


//Otherwise, food can easily be grabbed if not ant in way
for (var i=0; i<9; i++) {
    if (view[i].food) {
        if(!view[i].ant){
        return {cell:i}
        }
    }
}


//After food has been grabbed orthogonal, back to food pile
if (view[4].color == WHITE){
    for (i=0; i<orthogonals.length; i++) {
        if (view[orthogonals[i]].color == FOOD && checktrapped(FOOD) == 0 && view[inverseorthogonals[i]].color !== FOOD && view[rotate1orthogonals[i]].color !== FOOD && view[rotate2orthogonals[i]].color !== FOOD){
                 if (!view[orthogonals[i]].ant){
                     return {cell:orthogonals[i]};
                     }
                 }
        }
    }

//First part of scoring
// Scoring to determine next move
// Scoring everything higher than own pattern and escape
for (var i=0; i<9; i++) {
  if (view[i].color !== OWN) {
      score[i] = score[i]+3;
  }
  if (view[i].color !== ESCAPE){
      score[i] = score[i]+5;
  }
}

// Scoring while in painted area (f.e. wildfire)

var l = 0;
for (var i=2; i<9; i++) {
                          var k = 0;
    for (var j=0; j<9; j++) {
                             if (view[j].color == i) {
            k=k+1;
            if (k > 6){
            paint=i;
            }
        if (view[j].color !==WHITE) {
          l=l+1;

        }
    }
}
}

if (paint !== OWN && l >7) {
    for (var i=0; i<diagonals.length; i++){
        if (view[diagonals[i]].color == OWN) {
            score[inversdiagonals[i]]=score[inversdiagonals[i]]+7;
            }
        if (view[diagonals[i]].color == WHITE) {
          score[diagonals[i]]=score[diagonals[i]]+7
        }
        }
    }


if (paint == OWN && l >7) {
    for (var i=0; i<diagonals.length; i++){
        if (view[diagonals[i]].color == ESCAPE) {
            score[inversdiagonals[i]]=score[inversdiagonals[i]]+7;
            }
        if (view[diagonals[i]].color == WHITE) {
          score[diagonals[i]]=score[diagonals[i]]+7
        }
        }
    }

// the following might lead to some traps?
// score diagonals adjactant to white higher
  if (view[1].color === WHITE) {
    score[0] = score[0]+1;
    score[2] = score[2]+1;
   }
  if (view[3].color === WHITE) {
    score[0] = score[0]+1;
    score[6] = score[6]+1;
   }
  if (view[5].color === WHITE) {
    score[2] = score[2]+1;
    score[8] = score[8]+1;
   }
  if (view[7].color === WHITE) {
    score[6] = score[6]+1;
    score[8] = score[8]+1;
   }


//Don't move next to others, they steal your food!
  if (view[0].ant || view[1].ant || view[2].ant){
      score[6] = score [6]+10;
      score[8] = score [8]+10;
  }

  if (view[0].ant || view[3].ant || view[6].ant){
      score[2] = score [2]+10;
      score[8] = score [8]+10;
  }

  if (view[6].ant || view[7].ant || view[8].ant){
      score[0] = score [0]+10;
      score[2] = score [2]+10;

  }
   if (view[2].ant || view[5].ant || view[8].ant){
      score[0] = score [0]+10;
      score[6] = score [6]+10;

  }
//don't step on others!
for (var i=0; i<9; i++) {
  if (i!==4 && view[i].ant) {
        score[i] = -5;
 }
}

//end of scoring, calculate best
for (var i=0; i<9; i++) {
  if (score[i] > highest) {
    highest = score[i];
    scoreindex = i;
    }
  }

//Basic enemy avoidance
for (var i=0; i<9; i++) {
  if (i!==4 && view[i].ant) {
        return {cell:scoreindex}
        }
  }

//basic movement

//when surrounded by other paint
if (paint == ESCAPE && l>7){
    if(view[4].color == OWN){
      return{cell:scoreindex}
    }
}

if (paint !== OWN && paint !== 0 && l>7){
  if(view[4].color !== OWN){
    return{cell:4, color:OWN}
  }
}


if (paint == OWN && l>7){
  if(view[4].color !== ESCAPE){
    return{cell:4, color:ESCAPE}
  }
}

//a) when off pattern
if (view[4].color !== OWN) {
    if (view[4].color == ESCAPE){
         if (checktrapped(ESCAPE)==4){
            return{cell:scoreindex}
            }
        }
        if (view[4].color == ESCAPE){
         if (checktrapped(ESCAPE)==3){
            return{cell:scoreindex}
            }
        }
    if (checkforemptypattern()) {
    return{cell:4, color:OWN};
    }

    //Am I trapped? Different possible traps follow here
    if (view[4].color !== ESCAPE){
         if (checktrapped(OWN)==4){
            return{cell:4, color:ESCAPE}
            }
    }
    if (view[4].color !== ESCAPE){
         if (checktrapped(OWN)==3 && checktrapped(ESCAPE)==1){
            return{cell:4, color:ESCAPE}
            }
    }
    if (view[4].color !== ESCAPE){
         if (checktrapped(OWN)==2 && checktrapped(ESCAPE)==1){
            return{cell:4, color:ESCAPE}
            }
    }
    if (view[4].color !== ESCAPE){
         if (checktrapped(OWN)==2 && checktrapped(ESCAPE)==2){
            return{cell:4, color:ESCAPE}
            }
    }
    if (view[4].color !== ESCAPE){
         if (checktrapped(OWN)==1 && checktrapped(ESCAPE)==2){
            return{cell:4, color:ESCAPE}
            }
    }
    if (view[4].color !== ESCAPE){
         if (checktrapped(OWN)==1 && checktrapped(ESCAPE)==1){
            return{cell:4, color:ESCAPE}
            }
    }
    if (view[4].color !== ESCAPE){ //when the orthogonals are painted, some other guy was here before and movement traps are likely
         if (view[1].color == OWN || view[7].color == OWN || view[3].color == OWN || view[5].color == OWN){
            return{cell:4, color:ESCAPE}
            }
    }
    if (view[4].color !== ESCAPE){ //when the orthogonals are painted, some other guy was here before and movement traps are likely
         if (view[1].color == ESCAPE || view[7].color == ESCAPE || view[3].color == ESCAPE || view[5].color == ESCAPE){
            return{cell:4, color:ESCAPE}
            }
    }
}

//b) when on pattern check surroundings for escape route
if (checktrapped(ESCAPE)==3){
    return{cell:4, color:ESCAPE}
}
if (checktrapped(ESCAPE)==4){
    return{cell:4, color:ESCAPE}
}

//otherwise just move on
return{cell:scoreindex}

Пояснення

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

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

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

І останнє, але не менш важливе значення, бот малює кілька красивих абстрактних візерунків, залишаючись у спокої:

Оглядовий зразок бота Hoppemaur наприкінці гри


3
Ласкаво просимо на сайт! Виклики KoTH часто найпростіше зіткнутися, хоча і набагато рідше. Я думаю, що є три-чотири старших з контролерами, які ще можуть бути цікавими для спроб, навіть якщо вони офіційно закінчені. CodeBots 4 (боти, які вводять свій код один одному) та Prisoners Dillema 3 (чашка Петрі). Також «Битва за стипендію» та всесвітня пандемія. Я знаю, що PD був у Python, і я думаю, що CB був у Java, не пам'ятаю інших.
Draco18s

Дякую, @ Draco18s! Я можу перевірити ці старі виклики, але не до того, як мені вдалося створити колонію
мурашних

6

Формування

Дане представлення розміщується у сховищі github .

var marcher_count;var gatherer_count;var excess_gatherers;var tcell;var lh_cell;var rh_cell;var ant_off;var alt_cell;var cell_off;function debug(message)
{}
const MARCHER_A=1;const MARCHER_B=2;const GATHERER=3;const QUEEN=5;const S_END=[6,5,7,4,0,2,1,3];const S_FRONT=[7,5,6,0,4,1,3,2];const S_SIDE=[7,3,5,1,6,2,0,4];const S_GATHERER=[7,6,5,4,0,3,2,1];const SCAN_MOVES=[0,1,2,3,5,6,7,8];const CORNERS=[0,2,6,8];const EDGES=[1,3,5,7];const CCW=[[0,3,6,7,8,5,2,1],[1,0,3,6,7,8,5,2],[2,1,0,3,6,7,8,5],[3,6,7,8,5,2,1,0],[4,4,4,4,4,4,4,4],[5,2,1,0,3,6,7,8],[6,7,8,5,2,1,0,3],[7,8,5,2,1,0,3,6],[8,5,2,1,0,3,6,7]];const NEARS=[[6,5,3,5,4,2,3,2,1],[5,6,5,4,5,4,2,3,2],[3,5,6,2,4,5,1,2,3],[5,4,2,6,5,3,5,4,2],[4,5,4,5,6,5,4,5,4],[2,4,5,3,5,6,2,4,5],[3,2,1,5,4,2,6,5,3],[2,3,2,4,5,4,5,6,5],[1,2,3,2,4,5,3,5,6]];const SAN_ORD=[[1,3,6,2,5,7,8],[0,2,5,3,6,8,7],[5,1,0,8,7,3,6],[6,0,1,7,8,2,5],[],[2,8,7,1,0,6,3],[3,7,8,0,1,5,2],[8,6,3,5,2,0,1],[7,5,2,6,3,1,0]];const D_MARCH=1;const D_FOOD=2;const D_STALLED=3;const D_GATHERER=4;const U_REALIGN=5;const U_SENTINEL=6;const U_READY=7;const U_PANIC=8;const PUPS=[[0,1,2,3,4,5,6,7,8],[1,1,0,0,0,1,1,0,1],[2,0,2,0,4,2,2,0,2],[3,0,0,3,4,3,3,0,3],[4,0,4,4,4,4,0,0,4],[5,1,2,3,4,5,5,0,5],[6,1,2,3,0,5,5,0,6],[7,0,0,0,0,0,0,7,7],[8,1,2,3,4,5,6,7,8]];const PDOWNS=[[0,1,2,3,4,5,6,7,8],[1,1,0,3,4,5,5,0,1],[2,0,2,3,4,5,5,0,2],[3,3,3,3,3,3,3,3,3],[4,4,4,3,4,0,0,0,4],[5,5,5,3,0,5,5,0,5],[6,5,5,3,0,5,5,0,6],[7,0,0,3,0,0,0,7,7],[8,1,2,3,4,5,6,7,8]];const PSIDES=[[0,1,2,3,4,5,6,7,8],[1,1,0,3,4,1,1,0,1],[2,0,2,0,4,5,5,0,2],[3,3,0,3,3,3,3,3,3],[4,4,4,3,4,0,0,0,4],[5,1,5,3,0,5,5,0,5],[6,1,5,3,0,5,5,0,6],[7,0,0,3,0,0,0,7,7],[8,1,2,3,4,5,6,7,8]];const INIT_SEED=3734978372;const FINAL_SEED=2338395782;const SRECOLOR_PROB=0.7;const SONSTRIDE_PROB=0.5;const QFSPAWNP_MAX=0.05;const QFSPAWNP_MIN=0.00;const QFSPAWNP_DECAY=0.005;const QBSPAWNP_MAX=0.65;const QBSPAWNP_MIN=0.55;const QBSPAWNP_DECAY=0.01;const QFORMP_MAX=0.5;const QFORMP_MIN=0.3;const QFORMP_DECAY=0.01;const DISCOLORT=35;const ERASET=20;const SOBSTRUCT_FUZZ=6;const SSTRIDE_FUZZ=6;const OBSTRUCT_QWT=3;const SPREFWT=2;var state=null;function rand_init()
{state=INIT_SEED;for(var cell=0;cell<9;cell++)
{var v=view[cell];state^=v.color;state^=v.food<<3;if(v.ant!==null)
{state^=v.ant.friend<<4;state^=v.ant.type<<5;state^=v.ant.food<<8;}
ant_rand();}
state^=FINAL_SEED;if(state===0)state=1;}
function ant_rand()
{if(state===null)rand_init();state^=state<<13;state^=state>>>17;state^=state<<5;return state>>>0;}
function rand_choice(prob)
{return ant_rand()/4294967296<prob;}
function rand_sub(array,num)
{var return_array=array.slice();for(var i=0;i<num;i++)
{var rand_index=i+ant_rand()%(array.length-i);var x_val=return_array[rand_index];return_array[rand_index]=return_array[i];return_array[i]=x_val;}
return return_array.slice(0,num);}
function rand_perm(array)
{var return_array=array.slice();for(var i=0;i<array.length-1;i++)
{var rand_index=i+ant_rand()%(array.length-i)
var x_val=return_array[rand_index];return_array[rand_index]=return_array[i];return_array[i]=x_val;}
return return_array;}
function index_sort(arr)
{var index_array=[];for(var i=0;i<arr.length;i++)index_array.push(i);index_array.sort((a,b)=>(arr[a]===arr[b])?(a-b):(arr[a]-arr[b]));return index_array;}
function this_ant()
{return view[4].ant;}
function c_at(cell)
{return view[cell].color;}
function is_ally(cell)
{return view[cell].ant!==null&&view[cell].ant.friend===true;}
function is_enemy(cell)
{return view[cell].ant!==null&&view[cell].ant.friend===false;}
function is_harvestable(cell)
{return is_enemy(cell)&&view[cell].ant.type===QUEEN&&view[cell].ant.food>0;}
function lchk(c)
{if(is_ally(CCW[c][6])&&view[CCW[c][6]].ant.type===GATHERER)
if(is_ally(CCW[c][5])&&view[CCW[c][5]].ant.type!==GATHERER)return D_GATHERER;if(is_ally(CCW[c][7])&&view[CCW[c][7]].ant.type===GATHERER&&is_ally(CCW[c][1]))return D_GATHERER;if(is_ally(CCW[c][5])&&view[CCW[c][5]].ant.type===GATHERER)
if(is_ally(CCW[c][3])&&c_at(4)===D_MARCH)return D_STALLED;if(view[CCW[c][6]].food===1&&is_ally(CCW[c][5])&&view[CCW[c][5]].ant.type!==GATHERER)return D_FOOD;if(view[CCW[c][7]].food===1&&is_ally(CCW[c][1])&&c_at(CCW[c][1])===D_FOOD)return D_FOOD;if(view[CCW[c][5]].food===1&&is_ally(CCW[c][3])&&view[CCW[c][3]].ant.type!==QUEEN&&c_at(4)===D_MARCH)return U_REALIGN;return null;}
function lchk2(c)
{if(is_ally(CCW[c][6])&&view[CCW[c][6]].ant.type===GATHERER)
if(is_ally(CCW[c][5])&&view[CCW[c][5]].ant.type!==GATHERER)return D_GATHERER;if(is_ally(CCW[c][7])&&view[CCW[c][7]].ant.type===GATHERER&&is_ally(CCW[c][1]))return D_GATHERER;if(is_ally(CCW[c][5])&&view[CCW[c][5]].ant.type===GATHERER)
if(is_ally(CCW[c][3])&&c_at(4)===D_MARCH)return D_STALLED;if(is_ally(CCW[c][2])&&view[CCW[c][2]].ant.type===GATHERER)
if(is_ally(CCW[c][1])&&view[CCW[c][1]].ant.type!==GATHERER)return D_GATHERER;if(is_ally(CCW[c][3])&&view[CCW[c][3]].ant.type===GATHERER)
if(is_ally(CCW[c][5])&&c_at(CCW[c][5])===D_GATHERER)return D_GATHERER;if(is_ally(CCW[c][1])&&view[CCW[c][1]].ant.type===GATHERER)
if(is_ally(CCW[c][7])&&c_at(4)===D_MARCH)return D_STALLED;if(view[CCW[c][6]].food===1&&is_ally(CCW[c][5])&&view[CCW[c][5]].ant.type!==GATHERER)return D_FOOD;if(view[CCW[c][7]].food===1&&is_ally(CCW[c][1])&&c_at(CCW[c][1])===D_FOOD)return D_FOOD;if(view[CCW[c][5]].food===1&&is_ally(CCW[c][3])&&view[CCW[c][3]].ant.type!==QUEEN&&c_at(4)===D_MARCH)return U_REALIGN;if(view[CCW[c][2]].food===1&&is_ally(CCW[c][1])&&view[CCW[c][1]].ant.type!==GATHERER)return D_FOOD;if(view[CCW[c][3]].food===1&&is_ally(CCW[c][5])&&c_at(CCW[c][5])===D_FOOD)return{cell:4,color:D_FOOD};if(view[CCW[c][1]].food===1&&is_ally(CCW[c][7])&&view[CCW[c][7]].ant.type!==QUEEN&&c_at(4)===D_MARCH)return U_REALIGN;return null;}
function sigc(output,order,c)
{if(c_at(4)===output)
for(cell_off of order)
{var tcell=CCW[c][cell_off];if(!is_ally(tcell)&&c_at(tcell)!==D_MARCH)
{if(view[tcell].food!==0&&view[tcell].color===D_FOOD)
{for(alt_cell of SCAN_MOVES)
{var n_wt=NEARS[tcell][alt_cell];if(n_wt>3&&n_wt<6&&is_ally(alt_cell))
if(view[alt_cell].ant.type===QUEEN||view[alt_cell].ant.type===GATHERER)
continue;}}
return{cell:tcell,color:D_MARCH};}}
return{cell:4,color:output};}
function is_gatherer_marcher(cell)
{if(!is_ally(cell)||view[cell].ant.food>0||view[cell].ant.type!==GATHERER)return false;if(this_ant().type===QUEEN)return true;lh_cell=CCW[cell][1];rh_cell=CCW[cell][7];if(is_ally(lh_cell)&&view[lh_cell].ant.type===QUEEN)return!is_ally(rh_cell)
else if(is_ally(rh_cell)&&view[rh_cell].ant.type===QUEEN)return!is_ally(lh_cell)
else return false;}
function is_like(cell)
{if(c_at(cell)===U_PANIC)return false;if(is_ally(CCW[cell][1])&&c_at(CCW[cell][1])===U_PANIC)return false;if(is_ally(CCW[cell][7])&&c_at(CCW[cell][7])===U_PANIC)return false;if(CORNERS.includes(cell)&&is_ally(cell))
{switch(view[cell].ant.type)
{case MARCHER_A:return view[cell].ant.food===0&&this_ant().type!==MARCHER_B;case MARCHER_B:return view[cell].ant.food===0&&this_ant().type!==MARCHER_A;case GATHERER:return is_gatherer_marcher(cell)&&this_ant().type!==GATHERER;case QUEEN:return true;default:return false;}}
return false;}
function is_other(cell)
{if(c_at(cell)===U_PANIC)return false;if(EDGES.includes(cell)&&is_ally(cell))
{switch(view[cell].ant.type)
{case MARCHER_A:return view[cell].ant.food===0&&this_ant().type!==MARCHER_A;case MARCHER_B:return view[cell].ant.food===0&&this_ant().type!==MARCHER_B;case GATHERER:return this_ant().type===QUEEN
case QUEEN:return true;default:return false;}}
return false;}
function view_corner()
{var scores=[0,0,0,0];for(var i=0;i<4;i++)
for(var j=0;j<8;j++)
{scores[i]*=2;var tcell=CCW[CORNERS[i]][j];if(is_ally(tcell)&&(is_like(tcell)||is_other(tcell)))scores[i]++;}
if(scores[0]>scores[1]&&scores[0]>scores[2]&&scores[0]>scores[3])return CORNERS[0];else if(scores[1]>scores[2]&&scores[1]>scores[3])return CORNERS[1];else if(scores[2]>scores[3])return CORNERS[2];else return CORNERS[3];}
const ONE_EDGE=10;const ONE_CORNER=11;const EE_BENT=20;const EE_STRAIGHT=21;const EC_LEFT=22;const EC_RIGHT=23;const EC_SKEWED=24;const EC_SPAWN=25;const CC_EDGED=26;const CC_LINE=27;const THREE_MARCH=30;const THREE_STAND=31;const THREE_RECOVER=32;const THREE_UNSTAND=33;const THREE_BLOCK=34;const THREE_HANG=35;const THREE_UNHANG=36;const THREE_SIDE=37;const FOUR_Z=40;const FOUR_STAIRS=41;const FOUR_BENT=42;function neighbor_type(top_left)
{var corners=[];for(tcell of CORNERS)
if(is_ally(tcell)&&is_like(tcell))corners.push(tcell);var edges=[];for(tcell of EDGES)
if(is_ally(tcell)&&is_other(tcell))edges.push(tcell);if(corners.length===1&&edges.length===0)return ONE_CORNER;if(corners.length===0&&edges.length===1)return ONE_EDGE;if(corners.length===0&&edges.length===2)return(edges[1]===CCW[edges[0]][4])?EE_STRAIGHT:EE_BENT;if(corners.length===2&&edges.length===0)return(corners[1]===CCW[corners[0]][4])?CC_LINE:CC_EDGED;else if(corners.length===1&&edges.length===1)
{if(edges[0]===CCW[top_left][1])return EC_LEFT;if(edges[0]===CCW[top_left][3])return EC_SPAWN;if(edges[0]===CCW[top_left][5])return EC_SKEWED;if(edges[0]===CCW[top_left][7])return EC_RIGHT;return null;}
else if(corners.length===1&&edges.length===2)
{if(edges.includes(CCW[top_left][1])&&edges.includes(CCW[top_left][3]))return THREE_MARCH;if(edges.includes(CCW[top_left][3])&&edges.includes(CCW[top_left][7]))return THREE_STAND;if(edges.includes(CCW[top_left][1])&&edges.includes(CCW[top_left][5]))return THREE_RECOVER;if(edges.includes(CCW[top_left][5])&&edges.includes(CCW[top_left][7]))return THREE_UNSTAND;if(edges.includes(CCW[top_left][1])&&edges.includes(CCW[top_left][7]))return THREE_BLOCK;return null;}
else if(corners.length===2&&edges.length===1)
{if(corners.includes(CCW[top_left][4])&&edges.includes(CCW[top_left][3]))return THREE_HANG;if(corners.includes(CCW[top_left][4])&&edges.includes(CCW[top_left][1]))return THREE_UNHANG;if(corners.includes(CCW[top_left][2])&&edges.includes(CCW[top_left][1]))return THREE_SIDE;}
else if(corners.length===2&&edges.length===2)
{if(edges.includes(CCW[top_left][3])&&edges.includes(CCW[top_left][7])&&corners.includes(CCW[top_left][4]))
return FOUR_Z;if(edges.includes(CCW[top_left][1])&&edges.includes(CCW[top_left][3])&&corners.includes(CCW[top_left][4]))
return FOUR_STAIRS;if(edges.includes(CCW[top_left][1])&&edges.includes(CCW[top_left][3])&&corners.includes(CCW[top_left][2]))
return FOUR_BENT;return null;}
return null;}
function sok(cand)
{if(cand===4)return true;if(view[cand].food!==0&&this_ant().food!==0)return false;if(view[cand].ant!==null)return false;return true;}
function spref(cand)
{var okscore=0;if(cand===4)okscore-=9;if(this_ant().type===GATHERER)
{for(tcell of SCAN_MOVES)
if(NEARS[cand][tcell]>1)
if(is_ally(tcell)&&view[tcell].ant.type===QUEEN)okscore-=1;}
else
{if(this_ant().food===0&&view[cand].food!==0)
{for(tcell of SCAN_MOVES)
if(is_ally(tcell)&&view[tcell].ant.food===0)
{if([MARCHER_A,MARCHER_B].includes(view[tcell].ant.type))
{var has_common_enemy=false;for(var i=0;i<9;i++)
if(is_enemy(i)&&NEARS[tcell][i]>=4)has_common_enemy=true;if(!has_common_enemy)
{var wt=(view[tcell].ant.type===this_ant().type)?1:-1;if(NEARS[4][tcell]===5)okscore+=wt;if(NEARS[4][tcell]===4)okscore-=wt;if(NEARS[cand][tcell]===5)okscore-=wt;if(NEARS[cand][tcell]===4)okscore+=wt;}}}
if(okscore>0)okscore=0;}}
return okscore*SPREFWT;}
function ssep()
{var has_ally=false;var cands=[0,0,0,0,0,0,0,0,0];for(var i=0;i<9;i++)cands[i]+=spref(i);for(tcell of SCAN_MOVES)
{if(is_ally(tcell))
{has_ally=true;var wt=(is_like(tcell)||is_other(tcell))?3:1;for(var i=0;i<9;i++)cands[i]-=NEARS[tcell][i]*wt;}}
if(!has_ally)return null;var prox_order=index_sort(cands);for(var i=8;i>=0;i--)
{var i_cell=prox_order[i];if(sok(i_cell))return{cell:i_cell};}
return null;}
function sstep(col)
{if(c_at(4)===1)return{cell:4,color:col};var cands=[0,0,0,0,0,0,0,0,0];for(tcell of SCAN_MOVES)
if(c_at(tcell)===col)
for(var i=0;i<9;i++)cands[i]-=NEARS[tcell][i];for(var i=0;i<9;i++)cands[i]+=spref(i);var prox_order=index_sort(cands);for(var i=8;i>=0;i--)
{var i_cell=prox_order[i];if(sok(i_cell))return{cell:i_cell};}
return{cell:4,color:col};}
function smove()
{for(tcell of rand_perm(SCAN_MOVES))
if(sok(tcell))return{cell:tcell};return{cell:4};}
function sdec_alone()
{var try_sep=ssep();if(try_sep!==null)return try_sep;var c=U_PANIC;for(tcell of rand_sub(SCAN_MOVES,7))
if(c_at(tcell)>1&&c_at(tcell)!==c)
{c=c_at(tcell);break;}
return sstep(c);}
function sdec_erase()
{var try_sep=ssep();if(try_sep!==null)return try_sep;for(tcell of rand_perm(SCAN_MOVES))
if(c_at(tcell)!==1)return{cell:tcell,color:1};if(c_at(4)!==1)return{cell:4,color:1};return sdec_alone();}
function sdec_discolor()
{if(c_at(1)!==c_at(6)&&c_at(6)!==1)return{cell:1,color:c_at(6)};if(c_at(2)!==c_at(3))return{cell:3,color:c_at(2)};var proximities=[0,0,0,0,0,0,0,0,0];for(var i=0;i<9;i++)proximities[i]+=ant_rand()%SOBSTRUCT_FUZZ+spref(i);for(tcell of SCAN_MOVES)
if(is_ally(tcell))
for(var i=0;i<9;i++)proximities[i]+=NEARS[tcell][i];var prox_order=index_sort(proximities);for(var i=8;i>=0;i--)
if(sok(prox_order[i]))return{cell:prox_order[i]};return smove();}
function sdec_stride()
{if(rand_choice(SONSTRIDE_PROB))
{var stride_scores=[0,0,0,0,0,0,0,0,0];for(tcell of SCAN_MOVES)
{for(var i=0;i<9;i++)
if(c_at(tcell)!==c_at(i)&&c_at(i)!==1)stride_scores[i]+=NEARS[tcell][i];}
for(var i=0;i<9;i++)
stride_scores[i]+=ant_rand()%SSTRIDE_FUZZ+spref(i);var prox_order=index_sort(stride_scores);for(var i=8;i>=0;i--)
if(sok(prox_order[i]))return{cell:prox_order[i]};}
return smove();}
function sdec_obstruct_textured()
{var proximities=[0,0,0,0,0,0,0,0,0];for(tcell of SCAN_MOVES)
{if(is_enemy(tcell))
{var wt=(view[tcell].ant.type===QUEEN)?OBSTRUCT_QWT:1;for(var i=0;i<9;i++)proximities[i]+=NEARS[tcell][i]*wt;}}
for(var i=0;i<9;i++)proximities[i]+=ant_rand()%SOBSTRUCT_FUZZ;var prox_order;if(rand_choice(SRECOLOR_PROB))
{prox_order=index_sort(proximities);for(var i=8;i>0;i--)
{var i_cell=prox_order[i];for(var j=0;j<i;j++)
{var j_cell=prox_order[j];if(c_at(i_cell)!==c_at(j_cell))return{cell:i_cell,color:c_at(j_cell)};}}}
for(tcell of SCAN_MOVES)
if(is_ally(tcell))
for(var i=0;i<9;i++)proximities[i]+=NEARS[tcell][i];for(var i=0;i<9;i++)proximities[i]+=spref(i);prox_order=index_sort(proximities);for(var i=8;i>=0;i--)
if(sok(prox_order[i]))return{cell:prox_order[i]};return{cell:4,color:1};}
function sdec_obstruct_flat()
{var proximities=[0,0,0,0,0,0,0,0,0];for(tcell of SCAN_MOVES)
{if(is_enemy(tcell))
{var wt=(view[tcell].ant.type===QUEEN)?OBSTRUCT_QWT:1;for(var i=0;i<9;i++)proximities[i]+=NEARS[tcell][i]*wt;}}
for(var i=0;i<9;i++)proximities[i]+=ant_rand()%SOBSTRUCT_FUZZ;var prox_order;if(rand_choice(SRECOLOR_PROB))
{prox_order=index_sort(proximities);for(var i=8;i>0;i--)
{var i_cell=prox_order[i];if(c_at(i_cell)!==D_MARCH)return{cell:i_cell,color:D_MARCH};}}
for(tcell of SCAN_MOVES)
if(is_ally(tcell))
for(var i=0;i<9;i++)proximities[i]+=NEARS[tcell][i];for(var i=0;i<9;i++)proximities[i]+=spref(i);prox_order=index_sort(proximities);for(var i=8;i>=0;i--)
if(sok(prox_order[i]))return{cell:prox_order[i]};return{cell:4,color:1};}
function saboteur()
{var colored_neighbors=0;for(tcell of SCAN_MOVES)
if(c_at(tcell)>1)colored_neighbors++;if(colored_neighbors<=2)return sdec_alone();else
{var num_enemies=0;for(tcell of SCAN_MOVES)
if(is_enemy(tcell))num_enemies++;var diversity=0;var counts=[0,0,0,0,0,0,0,0,0];for(var i=0;i<9;i++)
{diversity+=5-counts[c_at(i)];counts[c_at(i)]++;}
if(num_enemies>0)
{if(diversity>=ERASET)return sdec_obstruct_textured();else return sdec_obstruct_flat();}
else
{if(diversity>=DISCOLORT)return sdec_discolor();else if(diversity>=ERASET)return sdec_stride();else return sdec_erase();}}}
function gwatch(cand)
{if(cand.cell===4)return cand;if(cand.hasOwnProperty("color"))return cand;if(view[cand.cell].food!==0&&this_ant().food!==0)return sigc(U_PANIC,S_SIDE,0);if(view[cand.cell].ant!==null)return sigc(U_PANIC,S_SIDE,0);return cand;}
function egwatch(cand)
{if(cand.cell===4)return cand;if(cand.hasOwnProperty("color"))return cand;if(view[cand.cell].food!==0&&this_ant().food!==0)return gwatch(sdec_erase());if(view[cand.cell].ant!==null)return gwatch(sdec_erase());return cand;}
function gdec_ee_bent(c)
{return{cell:CCW[c][4]};}
function gdec_ec_left(c)
{if(c_at(c)===D_FOOD&&c_at(CCW[c][1])===D_FOOD)return{cell:CCW[c][7]};if(c_at(c)===D_STALLED&&c_at(CCW[c][1])===D_STALLED)return sigc(U_READY,S_GATHERER,c);if(c_at(c)===D_MARCH&&c_at(CCW[c][1])===D_MARCH)return sigc(D_MARCH,S_GATHERER,c);return sigc(c_at(4),S_GATHERER,c);}
function gdec_ec_right(c)
{if([D_MARCH,D_FOOD].includes(c_at(c))&&[D_MARCH,D_FOOD].includes(c_at(CCW[c][7])))
return{cell:CCW[c][6]};if(is_ally(c)&&view[c].ant.type===QUEEN)
return{cell:CCW[c][1]};if(c_at(c)===D_STALLED&&c_at(CCW[c][7])===D_STALLED)
return sigc(U_READY,S_GATHERER,c);return sigc(c_at(4),S_GATHERER,c);}
function gdec_cc_edged(c)
{if(view[CCW[c][2]].ant.type!==QUEEN)return saboteur();return{cell:CCW[c][1]};}
function gdec_three_block(c)
{if(c_at(CCW[c][7])==D_FOOD)return{cell:CCW[c][6]};return{cell:CCW[c][2]};}
function gdec_three_unstand(c)
{if(view[CCW[c][5]].ant.type!==QUEEN)return saboteur();return{cell:CCW[c][4]};}
function gdec_four_bent(c)
{return{cell:CCW[c][4]};}
function early_gatherer()
{var qcell=null;var food_count=0;for(tcell of SCAN_MOVES)
{if(is_ally(tcell)&&view[tcell].ant.type===QUEEN)qcell=tcell;else if(is_enemy(tcell))return saboteur();}
if(qcell===null)return saboteur();if(c_at(qcell)===D_FOOD)return{cell:CCW[qcell][7]};if(this_ant().food===0)
{for(tcell of rand_perm(CORNERS))
if(view[tcell].food>0&&NEARS[tcell][qcell]===5)
{if(c_at(tcell)===D_FOOD)return{cell:tcell};else return{cell:tcell,color:D_FOOD};}
for(tcell of rand_perm(EDGES))
if(view[tcell].food>0)
{if(c_at(tcell)!==D_FOOD&&NEARS[tcell][qcell]===4)
return{cell:tcell,color:D_FOOD};}}
return{cell:CCW[qcell][1]};}
function gatherer_retrieve()
{if(c_at(4)===U_PANIC)return saboteur();var c=view_corner();switch(neighbor_type(c))
{case EC_LEFT:return gwatch({cell:CCW[c][2]});case THREE_BLOCK:{if(c_at(CCW[c][7])===D_FOOD)return gwatch({cell:CCW[c][6]});return gwatch({cell:CCW[c][2]});}
case FOUR_BENT:return gwatch(sigc(c_at(4),S_FRONT,c));default:return early_gatherer();}}
function gatherer_return()
{if(c_at(4)===U_PANIC)return saboteur();var c=view_corner();switch(neighbor_type(c))
{case EC_LEFT:return gwatch({cell:CCW[c][2]});case THREE_BLOCK:return gwatch({cell:CCW[c][2]});case FOUR_BENT:return gwatch({cell:CCW[c][4]});default:return early_gatherer();}}
function gatherer_formation()
{if(c_at(4)===U_PANIC)return saboteur();var c=view_corner();switch(neighbor_type(c))
{case EC_LEFT:return gwatch(gdec_ec_left(c));case EC_RIGHT:return gwatch(gdec_ec_right(c));case CC_EDGED:return gwatch(gdec_cc_edged(c));case EE_BENT:return gwatch(gdec_ee_bent(c));case THREE_BLOCK:return gwatch(gdec_three_block(c));case THREE_UNSTAND:return gwatch(gdec_three_unstand(c));case FOUR_BENT:return gwatch(gdec_four_bent(c));default:return egwatch(early_gatherer());}}
function gatherer_decision()
{var marcher_count=0;var gatherer_count=0;var queen_pos=null;for(tcell of SCAN_MOVES)
if(is_ally(tcell))
{if(view[tcell].ant.type===MARCHER_A||view[tcell].ant.type===MARCHER_B)marcher_count++;if(view[tcell].ant.type===GATHERER)gatherer_count++;if(view[tcell].ant.type===QUEEN)queen_pos=tcell;}
if(gatherer_count>0)return saboteur();if(this_ant().food>0&&marcher_count>0)return gwatch(gatherer_return());else if(queen_pos!==null&&marcher_count>0)return gwatch(gatherer_formation());else if(marcher_count>0)return gwatch(gatherer_retrieve());else if(queen_pos!==null)return egwatch(early_gatherer());else return saboteur();}
function mdec_one_corner(c)
{if(view[c].ant.type===QUEEN)
return sigc(c_at(4),S_SIDE,c);else return saboteur();}
function mdec_one_edge(c)
{if([U_REALIGN,D_MARCH].includes(c_at(CCW[c][1])))
{if(view[CCW[c][2]].food===1)return{cell:c};if(is_ally(CCW[c][2])&&view[CCW[c][2]].ant.type===GATHERER)return{cell:c};}
return saboteur();}
function mdec_ee_bent(c)
{if(view[CCW[c][1]].ant.type===GATHERER&&view[CCW[c][3]].ant.type===QUEEN)return saboteur();if(view[CCW[c][1]].ant.type===QUEEN&&view[CCW[c][3]].ant.type===GATHERER)return saboteur();var u_sig=c_at(CCW[c][1]);var d_sig=c_at(CCW[c][3]);if(is_ally(c)&&view[c].ant.type===GATHERER)return sigc(c_at(4),S_SIDE,CCW[c][4]);var provisional=lchk(c);if(provisional!==null)
{if(provisional===U_REALIGN)return sigc(U_SENTINEL,S_END,c);return sigc(provisional,S_END,c);}
if(u_sig===D_STALLED)
{if([D_STALLED,U_READY,D_GATHERER].includes(d_sig)&&[D_STALLED,U_READY].includes(c_at(4)))
return sigc(D_STALLED,S_SIDE,c);if(d_sig===U_REALIGN&&c_at(4)===D_STALLED)
return sigc(D_STALLED,S_SIDE,c);}
if(view[CCW[c][1]].ant.type===QUEEN)
{var provisional=lchk(CCW[c][4]);if(provisional!==null)return sigc(provisional,S_END,CCW[c][4]);if(u_sig===D_GATHERER&&d_sig===U_REALIGN&&c_at(4)===D_GATHERER)
return sigc(D_GATHERER,S_END,CCW[c][4]);}
if(u_sig===U_SENTINEL)
{if(d_sig===U_REALIGN&&[D_MARCH,U_SENTINEL].includes(c_at(4)))return sigc(U_SENTINEL,S_SIDE,c);if(d_sig===D_STALLED&&[U_SENTINEL,D_STALLED].includes(c_at(4)))return sigc(U_SENTINEL,S_SIDE,c);if(d_sig===D_MARCH&&[U_SENTINEL,D_MARCH].includes(c_at(4)))return sigc(D_MARCH,S_SIDE,c);}
if(u_sig===D_GATHERER&&d_sig===D_STALLED&&c_at(4)===D_GATHERER)return sigc(D_STALLED,S_SIDE,c);return{cell:CCW[c][2]};}
function mdec_ee_straight(c)
{return sigc(U_REALIGN,S_SIDE,c);}
function mdec_ec_left(c)
{if(view[CCW[c][1]].ant.type===GATHERER&&view[c].ant.type===QUEEN)return saboteur();if(view[CCW[c][1]].ant.type===QUEEN&&view[c].ant.type===GATHERER)return saboteur();if(is_other(CCW[c][1])&&view[c].ant.type===QUEEN)return{cell:CCW[c][3]};var d_sig=PDOWNS[c_at(c)][c_at(CCW[c][1])];if(is_ally(CCW[c][4])&&view[CCW[c][4]].ant.type===GATHERER&&d_sig===D_STALLED&&c_at(4)===D_STALLED)
return sigc(D_STALLED,S_END,c);var provisional=lchk(CCW[c][4]);if(provisional!==null)
{if(provisional===U_REALIGN)return sigc(U_SENTINEL,S_END,CCW[c][4]);return sigc(provisional,S_END,CCW[c][4]);}
if(d_sig===U_REALIGN)
{if(c_at(4)===D_MARCH)return sigc(U_SENTINEL,S_END,CCW[c][4]);if(c_at(4)===U_SENTINEL)
{if(c_at(c)===D_MARCH)return{cell:CCW[c][2]};return sigc(U_SENTINEL,S_END,CCW[c][4]);}}
if(d_sig===D_STALLED)
{if([D_MARCH,D_STALLED].includes(c_at(4)))return sigc(D_STALLED,S_END,CCW[c][4]);if(c_at(4)===U_SENTINEL)return sigc(U_SENTINEL,S_END,CCW[c][4]);}
if(d_sig===D_GATHERER)
{if(c_at(4)===D_FOOD)return sigc(D_GATHERER,S_END,CCW[c][4]);if(c_at(4)===D_GATHERER)return sigc(D_STALLED,S_END,CCW[c][4]);}
if(d_sig===U_READY)
{if(c_at(4)===D_STALLED)
{if(c_at(CCW[c][2])!==D_MARCH)return{cell:CCW[c][2],color:D_MARCH};return sigc(D_MARCH,S_END,CCW[c][4]);}
if(c_at(4)===U_SENTINEL)return sigc(D_MARCH,S_END,CCW[c][4]);}
return{cell:CCW[c][2]};}
function mdec_ec_right(c)
{if(view[c].ant.type===GATHERER&&view[CCW[c][7]].ant.type===QUEEN)
if(is_ally(CCW[c][4])&&view[CCW[c][4]].ant.type!==this_ant().type)return{cell:CCW[c][5]};var d_sig=PDOWNS[c_at(c)][c_at(CCW[c][7])];var provisional=lchk(CCW[c][4]);if(provisional!==null)
{if(provisional===U_REALIGN)return sigc(U_SENTINEL,S_END,CCW[c][4]);return sigc(provisional,S_END,CCW[c][4]);}
if(d_sig===D_MARCH)
{if(c_at(4)===D_MARCH)return sigc(D_MARCH,S_END,CCW[c][4]);if([D_FOOD,D_GATHERER,U_READY].includes(c_at(4)))return sigc(D_MARCH,S_END,CCW[c][4]);}
if(d_sig===D_FOOD)
{if([U_SENTINEL,D_STALLED].includes(c_at(4)))return sigc(D_STALLED,S_END,CCW[c][4]);if([D_FOOD,D_GATHERER,U_READY].includes(c_at(4)))return sigc(D_STALLED,S_END,CCW[c][4]);}
if(d_sig===D_GATHERER)
{if([D_FOOD,D_GATHERER,U_READY].includes(c_at(4)))return sigc(D_MARCH,S_END,CCW[c][4]);if([U_SENTINEL,D_STALLED].includes(c_at(4)))return sigc(D_STALLED,S_END,CCW[c][4]);}
if(d_sig===D_STALLED)
{if(c_at(4)===D_STALLED)return sigc(D_STALLED,S_END,CCW[c][4]);if([D_FOOD,D_GATHERER,U_READY].includes(c_at(4)))return sigc(D_STALLED,S_END,CCW[c][4]);}
if(d_sig===U_READY)
{if(c_at(4)===D_STALLED)return sigc(D_MARCH,S_END,CCW[c][4]);if([D_FOOD,D_GATHERER,U_READY].includes(c_at(4)))return sigc(D_MARCH,S_END,CCW[c][4]);}
if(d_sig===U_REALIGN)
{if(c_at(4)===U_SENTINEL)return{cell:CCW[c][6]};if([D_FOOD,D_GATHERER,U_READY].includes(c_at(4)))return sigc(D_STALLED,S_END,CCW[c][4]);}
return sigc(d_sig,S_END,CCW[c][4]);}
function mdec_ec_spawn(c)
{if(view[c].ant.type===QUEEN&&c_at(c)===D_MARCH&&c_at(CCW[c][3])===D_STALLED)
if(c_at(4)===D_STALLED)return sigc(D_STALLED,S_SIDE,c);return saboteur();}
function mdec_three_march(c)
{var d_sig=PDOWNS[c_at(c)][c_at(CCW[c][1])];var u_sig=c_at(CCW[c][3]);var provisional=lchk2(c);if(provisional!==null)return sigc(provisional,S_FRONT,c);if(u_sig===U_SENTINEL)
{if(d_sig===D_GATHERER&&[D_GATHERER,D_STALLED].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,c);if(d_sig===D_STALLED&&[D_MARCH,D_STALLED].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,c);}
if(u_sig===U_REALIGN)
{if(d_sig===U_REALIGN&&c_at(4)===U_REALIGN)
if(c_at(c)===U_SENTINEL)
{if(c_at(CCW[c][7])===D_MARCH)return sigc(U_REALIGN,S_FRONT,c);return{cell:CCW[c][2]};}
if(d_sig===D_FOOD&&[D_MARCH,D_FOOD].includes(c_at(4)))return sigc(D_FOOD,S_FRONT,c);if(d_sig===U_READY&&c_at(4)===D_STALLED)return sigc(D_MARCH,S_FRONT,c);if(d_sig===D_STALLED&&[D_MARCH,D_STALLED].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,c);if(d_sig===D_GATHERER&&[D_GATHERER,D_STALLED].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,c);if(d_sig===D_MARCH&&c_at(4)===D_STALLED)return sigc(D_STALLED,S_FRONT,c);}
if(u_sig===D_MARCH)
{if(d_sig===D_FOOD&&c_at(4)===D_FOOD)return sigc(D_FOOD,S_FRONT,c);if(d_sig===U_REALIGN&&c_at(4)===D_MARCH)
if(c_at(c)===U_SENTINEL)return sigc(U_REALIGN,S_FRONT,c);if(d_sig===U_READY&&c_at(4)===U_READY)return sigc(D_MARCH,S_FRONT,c);}
if(u_sig===D_STALLED)
{if(d_sig===U_READY&&c_at(4)===D_STALLED)return sigc(U_READY,S_FRONT,c);if(d_sig===D_STALLED&&[D_STALLED,D_MARCH].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,c);if(d_sig===D_GATHERER&&c_at(4)===D_GATHERER)return sigc(D_STALLED,S_FRONT,c);if(d_sig===D_MARCH&&c_at(4)===D_STALLED)return sigc(D_STALLED,S_FRONT,c);if(d_sig===U_REALIGN&&[D_STALLED,D_MARCH].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,c);}
if(u_sig===D_GATHERER)
{if(d_sig===D_STALLED&&c_at(4)===D_GATHERER)
if(view[CCW[c][3]].ant.type===QUEEN)return sigc(D_STALLED,S_FRONT,c);if(d_sig===D_GATHERER&&c_at(4)===D_GATHERER)return sigc(D_GATHERER,S_FRONT,c);if(d_sig===D_FOOD&&c_at(4)===D_GATHERER)return sigc(D_FOOD,S_FRONT,c);}
if(u_sig===D_FOOD)
{if(d_sig===D_FOOD&&c_at(4)===D_FOOD)return sigc(D_FOOD,S_FRONT,c);if(d_sig===D_GATHERER&&c_at(4)===D_GATHERER)return sigc(D_GATHERER,S_FRONT,c);}
return{cell:CCW[c][2]};}
function mdec_three_stand(c)
{var provisional=lchk2(c);if(provisional!==null)return sigc(provisional,S_SIDE,c);var u_sig=c_at(CCW[c][3]);var d_sig=PSIDES[c_at(c)][c_at(CCW[c][7])];if(u_sig===U_REALIGN)
{if([D_MARCH,D_STALLED].includes(d_sig)&&c_at(4)===D_MARCH)return sigc(U_REALIGN,S_SIDE,CCW[c][4]);if(c_at(4)===U_REALIGN)return sigc(U_REALIGN,S_SIDE,CCW[c][4]);}
if(u_sig===D_MARCH&&d_sig===U_REALIGN&&c_at(4)===D_MARCH)return sigc(U_REALIGN,S_SIDE,CCW[c][4]);if(u_sig===D_STALLED&&[D_STALLED,U_REALIGN].includes(d_sig)&&c_at(4)===D_STALLED)
return sigc(D_STALLED,S_SIDE,CCW[c][4]);return sigc(D_MARCH,S_SIDE,CCW[c][4]);}
function mdec_three_unstand(c)
{if(view[CCW[c][5]].ant.type===QUEEN)
{var provisional=lchk(c);if(provisional!==null)return sigc(provisional,S_FRONT,c);var d_sig=PUPS[c_at(c)][c_at(CCW[c][7])];return sigc(d_sig,S_FRONT,c);}
else
{var provisional=lchk(CCW[c][4]);if(provisional!==null)return sigc(provisional,S_FRONT,CCW[c][4]);var u_sig=c_at(CCW[c][5]);var d_sig=PDOWNS[c_at(c)][c_at(CCW[c][7])];if(u_sig===D_MARCH)
{if(d_sig===U_READY&&c_at(4)===U_READY)return sigc(D_MARCH,S_FRONT,CCW[c][4]);if(d_sig===D_FOOD&&c_at(4)===D_MARCH)return sigc(U_REALIGN,S_FRONT,CCW[c][4]);if([D_FOOD,D_GATHERER].includes(c_at(4)))return sigc(D_MARCH,S_FRONT,CCW[c][4]);}
if(u_sig===D_FOOD)
{if(d_sig===D_FOOD&&c_at(4)===D_MARCH)return sigc(U_REALIGN,S_FRONT,CCW[c][4]);if([D_FOOD,D_GATHERER].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,CCW[c][4]);}
if(u_sig===D_GATHERER)
{if(d_sig===D_FOOD&&c_at(4)===D_MARCH)return sigc(U_REALIGN,S_FRONT,CCW[c][4]);if([D_FOOD,D_GATHERER].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,CCW[c][4]);}
if(u_sig===D_STALLED)
{if(d_sig===U_READY)
{if(c_at(4)===D_STALLED)return sigc(U_READY,S_FRONT,CCW[c][4]);if(c_at(4)===D_GATHERER)return sigc(D_STALLED,S_FRONT,CCW[c][4]);}
if(d_sig===D_FOOD)
{if(c_at(4)===D_MARCH)return sigc(U_REALIGN,S_FRONT,CCW[c][4]);if(c_at(4)===D_GATHERER)return sigc(D_STALLED,S_FRONT,CCW[c][4]);}
if(d_sig===D_STALLED)
{if(c_at(4)===D_STALLED)return sigc(D_STALLED,S_FRONT,CCW[c][4]);if(c_at(4)===D_GATHERER)return sigc(D_STALLED,S_FRONT,CCW[c][4]);}
if(d_sig===D_GATHERER&&c_at(4)===D_GATHERER)return sigc(D_STALLED,S_FRONT,CCW[c][4]);if([D_MARCH,U_REALIGN].includes(d_sig)&&c_at(4)===D_GATHERER)
return sigc(D_STALLED,S_FRONT,CCW[c][4]);if(c_at(4)===D_FOOD)return sigc(D_STALLED,S_FRONT,CCW[c][4]);}
if(u_sig===U_REALIGN)
{if(d_sig===D_FOOD&&c_at(4)===D_MARCH)return sigc(U_REALIGN,S_FRONT,CCW[c][4]);if([D_FOOD,D_GATHERER].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,CCW[c][4]);}
if(u_sig===U_SENTINEL)
{if(d_sig===D_FOOD)
{if(c_at(4)===U_REALIGN)return sigc(D_STALLED,S_FRONT,CCW[c][4]);if(c_at(4)===D_MARCH)return sigc(U_REALIGN,S_FRONT,CCW[c][4]);}
if(d_sig===D_GATHERER&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_FRONT,CCW[c][4]);if(d_sig===D_MARCH&&c_at(4)===U_SENTINEL)return sigc(D_MARCH,S_FRONT,CCW[c][4]);if(d_sig===D_STALLED&&c_at(4)===U_SENTINEL)return sigc(D_STALLED,S_FRONT,CCW[c][4]);if(d_sig===U_READY&&c_at(4)===D_STALLED)return sigc(U_READY,S_FRONT,CCW[c][4]);if([D_FOOD,D_GATHERER].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,CCW[c][4]);}
if(u_sig===U_READY)
{if(d_sig===D_FOOD&&c_at(4)===D_MARCH)return sigc(U_REALIGN,S_FRONT,CCW[c][4]);if([D_FOOD,D_GATHERER].includes(c_at(4)))return sigc(D_MARCH,S_FRONT,CCW[c][4]);}
return sigc(c_at(4),S_FRONT,CCW[c][4]);}}
function mdec_three_recover(c)
{return sigc(U_SENTINEL,S_FRONT,c);}
function mdec_three_hang(c)
{return sigc(c_at(4),S_SIDE,CCW[c][4]);}
function mdec_three_unhang(c)
{return sigc(c_at(4),S_SIDE,c);}
function mdec_four_z(c)
{var provisional=lchk2(CCW[c][4]);if(provisional!==null)return sigc(provisional,S_SIDE,CCW[c][4]);var u_sig=PSIDES[c_at(c)][c_at(CCW[c][7])];var d_sig=PSIDES[c_at(CCW[c][4])][c_at(CCW[c][3])];if(u_sig===D_FOOD)
{if([D_FOOD,D_STALLED,U_REALIGN].includes(d_sig)&&c_at(4)===U_REALIGN)
return sigc(U_REALIGN,S_SIDE,CCW[c][4]);if(d_sig===D_GATHERER&&[U_REALIGN,D_GATHERER].includes(c_at(4)))
return sigc(U_REALIGN,S_SIDE,CCW[c][4]);}
if(u_sig===D_STALLED)
{if(d_sig===U_REALIGN)return sigc(U_REALIGN,S_SIDE,CCW[c][4]);if(d_sig===D_FOOD&&c_at(4)===U_REALIGN)return sigc(U_REALIGN,S_SIDE,CCW[c][4]);}
if(u_sig===D_GATHERER)
{if(d_sig===U_REALIGN&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,CCW[c][4]);if(d_sig===D_FOOD&&[U_REALIGN,D_GATHERER].includes(c_at(4)))
return sigc(U_REALIGN,S_SIDE,CCW[c][4]);}
if(u_sig===U_REALIGN)
{if(d_sig===D_FOOD&&c_at(4)===U_REALIGN)return sigc(U_REALIGN,S_SIDE,CCW[c][4]);if(d_sig===D_STALLED)return sigc(U_REALIGN,S_SIDE,CCW[c][4]);if(d_sig===D_GATHERER&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,CCW[c][4]);if(d_sig===U_READY&&c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,CCW[c][4]);}
if(u_sig===U_READY&&d_sig===U_REALIGN&&c_at(4)===U_REALIGN)
return sigc(D_MARCH,S_SIDE,CCW[c][4]);return sigc(D_MARCH,S_SIDE,CCW[c][4]);}
function mdec_four_stairs(c)
{var provisional=lchk2(c);if(provisional!==null)return sigc(provisional,S_SIDE,c);var u_sig=PSIDES[c_at(c)][c_at(CCW[c][1])];var d_sig=PSIDES[c_at(CCW[c][4])][c_at(CCW[c][3])];if(u_sig===D_MARCH)
{if(d_sig===D_FOOD)
{if(c_at(4)===D_MARCH)return sigc(D_FOOD,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);}
if(d_sig===U_READY)
{if(c_at(4)===U_READY)return sigc(D_MARCH,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);}
if(d_sig===D_STALLED)
{if(c_at(4)===D_MARCH)return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(d_sig===D_GATHERER&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);if([D_MARCH,U_REALIGN].includes(d_sig)&&c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);}
if(u_sig===D_FOOD)
{if(d_sig===D_MARCH)
{if(c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);if(c_at(4)===D_MARCH)return sigc(D_FOOD,S_SIDE,c);}
if(d_sig===U_READY)
{if(c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);if(c_at(4)===D_STALLED)return sigc(D_STALLED,S_SIDE,c);}
if(d_sig===D_GATHERER&&[U_REALIGN,D_GATHERER].includes(c_at(4)))return sigc(D_FOOD,S_SIDE,c);if([U_REALIGN,D_STALLED].includes(d_sig)&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(u_sig===D_STALLED)
{if(d_sig===D_STALLED)
{if(c_at(4)===D_STALLED)return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===D_MARCH)return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);}
if(d_sig===D_MARCH)
{if(c_at(4)===D_MARCH)return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(d_sig===D_GATHERER)
{if(c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);if([D_STALLED,D_GATHERER].includes(c_at(4)))return sigc(D_STALLED,S_SIDE,c);}
if(d_sig===U_READY)
{if(c_at(4)===D_STALLED)return sigc(U_READY,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);}
if(d_sig===U_REALIGN&&[U_REALIGN,D_MARCH].includes(c_at(4)))return sigc(D_STALLED,S_SIDE,c);if(d_sig===D_FOOD&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(u_sig===D_GATHERER)
{if(d_sig===D_STALLED)
{if([D_STALLED,D_GATHERER].includes(c_at(4)))return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(d_sig===U_READY)
{if(c_at(4)===D_STALLED)return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(d_sig===D_FOOD&&[D_GATHERER,U_REALIGN].includes(c_at(4)))return sigc(D_FOOD,S_SIDE,c);if([D_MARCH,U_REALIGN].includes(d_sig)&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);if(d_sig===D_GATHERER&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(u_sig===U_REALIGN)
{if(d_sig===U_REALIGN)
{if(c_at(4)===D_MARCH)return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(d_sig===D_STALLED)
{if(c_at(4)===D_MARCH)return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(d_sig===D_MARCH&&c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);if([D_FOOD,D_GATHERER,U_READY].includes(d_sig)&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(u_sig===U_READY)
{if(d_sig===D_MARCH)
{if(c_at(4)===U_READY)return sigc(D_MARCH,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(U_READY,S_SIDE,c);}
if([D_FOOD,D_GATHERER].includes(d_sig))
{if(c_at(4)===D_STALLED)return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(U_READY,S_SIDE,c);}
if(d_sig===D_STALLED&&c_at(4)===D_STALLED)return sigc(U_READY,S_SIDE,c);if(d_sig===U_REALIGN&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);if(d_sig===U_READY&&c_at(4)===U_REALIGN)return sigc(U_READY,S_SIDE,c);}
return sigc(c_at(4),S_SIDE,c);}
function mwatch(cand)
{if(cand.cell===4)return cand;if(cand.hasOwnProperty("color"))return cand;if(view[cand.cell].food!==0)return sigc(D_FOOD,S_SIDE,0);if(is_harvestable(cand.cell))return sigc(D_FOOD,S_SIDE,0);if(view[cand.cell].ant!==null)return sigc(U_PANIC,S_SIDE,0);return cand;}
function marcher_decision()
{if(c_at(4)===U_PANIC||this_ant().food>0)return saboteur();var gatherer_count=0;var enemy_count=0;for(tcell of SCAN_MOVES)
{if(is_ally(tcell)&&view[tcell].ant.type===GATHERER)gatherer_count++;else if(is_enemy(tcell)&&!is_harvestable(tcell))enemy_count++;}
if(gatherer_count>1||enemy_count>0)return saboteur();var colored_neighbors=0;for(tcell of SCAN_MOVES)
if(c_at(tcell)>1)colored_neighbors++;if(colored_neighbors>5)return saboteur();var c=view_corner();switch(neighbor_type(c))
{case ONE_CORNER:return mwatch(mdec_one_corner(c));case ONE_EDGE:return mwatch(mdec_one_edge(c));case EE_BENT:return mwatch(mdec_ee_bent(c));case EE_STRAIGHT:return mwatch(mdec_ee_straight(c));case EC_LEFT:return mwatch(mdec_ec_left(c));case EC_RIGHT:return mwatch(mdec_ec_right(c));case EC_SPAWN:return mwatch(mdec_ec_spawn(c));case THREE_MARCH:return mwatch(mdec_three_march(c));case THREE_STAND:return mwatch(mdec_three_stand(c));case THREE_RECOVER:return mwatch(mdec_three_recover(c));case THREE_UNSTAND:return mwatch(mdec_three_unstand(c));case THREE_HANG:return mwatch(mdec_three_hang(c));case THREE_UNHANG:return mwatch(mdec_three_unhang(c));case FOUR_Z:return mwatch(mdec_four_z(c));case FOUR_STAIRS:return mwatch(mdec_four_stairs(c));default:return saboteur();}}
function opening_queen()
{for(tcell of rand_perm(SCAN_MOVES))
if(view[tcell].food===1)return{cell:tcell};var has_ally=false;var proxs=[0,0,0,0,0,0,0,0,0];for(tcell of SCAN_MOVES)
{if(view[tcell].ant!==null)
{has_ally=true;for(var i=0;i<9;i++)proxs[i]-=NEARS[tcell][i];}}
if(has_ally)
{var prox_order=index_sort(proxs);for(var i=8;i>=0;i--)
{var i_cell=prox_order[i];if(view[i_cell].ant===null&&view[i_cell].food===0)return{cell:i_cell};}}
if(this_ant().food>0)
{var num_ants=0;for(tcell of SCAN_MOVES)
if(view[tcell].ant!==null)num_ants++;if(num_ants===0)
{var is_clear=true;var num_black_corners=0;var black_corner=null;for(var tcell=0;tcell<9;tcell++)
{if(CORNERS.includes(tcell))
{if(c_at(tcell)===8)
{num_black_corners++;black_corner=tcell;}
else if(c_at(tcell)!==1)is_clear=false;}
else if(c_at(tcell)!==1)is_clear=false;}
if(num_black_corners===1&&is_clear)return{cell:CCW[black_corner][7],type:GATHERER};}}
if(c_at(4)!==8)return{cell:4,color:8};var cands=[0,0,0,0,9,0,0,0,0];for(tcell of SCAN_MOVES)
if(c_at(tcell)===8)
for(var i=0;i<9;i++)cands[i]-=NEARS[tcell][i];var cand_order=index_sort(cands);for(var i=8;i>=0;i--)
{var i_cell=cand_order[i];if(view[i_cell].ant===null&&view[i_cell].food===0)return{cell:i_cell};}
return{cell:4,color:8};}
function early_queen()
{var gcell=null;var ally_count=0;for(tcell of rand_perm(SCAN_MOVES))
{if(is_ally(tcell))
{ally_count++;if(view[tcell].ant.type===GATHERER&&EDGES.includes(tcell))gcell=tcell;}}
if(gcell===null)return opening_queen();for(tcell of rand_perm(CORNERS))
if(view[tcell].food>0&&NEARS[tcell][gcell]===5)
{if(c_at(tcell)===D_FOOD)return{cell:tcell};else return{cell:tcell,color:D_FOOD};}
for(tcell of rand_perm(EDGES))
if(view[tcell].food>0)
{if(c_at(tcell)!==D_FOOD&&NEARS[tcell][gcell]===4)
return{cell:tcell,color:D_FOOD};}
if(c_at(4)===D_FOOD)
{if(c_at(CCW[gcell][2])===D_FOOD&&view[CCW[gcell][2]].food===0)
return{cell:CCW[gcell][2],color:D_MARCH};return{cell:4,color:D_MARCH};}
if(c_at(CCW[gcell][6])===D_FOOD&&view[CCW[gcell][6]].food===0)
return{cell:CCW[gcell][6],color:D_MARCH};if(EDGES.includes(gcell)&&this_ant().food>2&&ally_count===1)
{var num_clear_cells=0;var num_down_food=0;var is_valid=true;for(var tcell=0;tcell<9;tcell++)
{if(c_at(tcell)===D_FOOD)
{num_down_food++;if(tcell!==4&&tcell!==gcell)is_valid=false;}
if(c_at(tcell)===D_MARCH)num_clear_cells++;}
if(is_valid&&num_down_food===1&&num_clear_cells===8)
{var food_factor=QFORMP_MAX-QFORMP_MIN
var food_coefficient=QFORMP_DECAY/food_factor
var actual_prob=food_factor/(food_coefficient*(this_ant().food-3)+1)+QFORMP_MIN;if(rand_choice(actual_prob))return{cell:CCW[gcell][1],type:rand_choice(.5)?MARCHER_A:MARCHER_B};else return{cell:gcell,color:D_MARCH};}}
return{cell:CCW[gcell][7]};}
function qwatch(cand)
{if(cand.hasOwnProperty("type")&&this_ant().food===0)return sigc(U_PANIC,S_SIDE,0);if(cand.hasOwnProperty("type")&&view[cand.cell].food!==0)return sigc(U_PANIC,S_SIDE,0);if(cand.cell===4)return cand;if(cand.hasOwnProperty("color"))return cand;if(is_enemy(cand.cell))return sigc(U_PANIC,S_SIDE,0);if(is_ally(cand.cell))return sigc(c_at(4),S_SIDE,0);return cand;}
function eqwatch(cand)
{if(cand.hasOwnProperty("type")&&this_ant().food===0)return qwatch(opening_queen());if(cand.hasOwnProperty("type")&&view[cand.cell].food!==0)return qwatch(opening_queen());if(cand.cell===4)return cand;if(cand.hasOwnProperty("color"))return cand;if(is_enemy(cand.cell))return qwatch(opening_queen());if(is_ally(cand.cell))return qwatch(opening_queen());return cand;}
function qdec_ee_straight(c)
{return sigc(c_at(4),S_SIDE,c);}
function qdec_ee_bent(c)
{return{cell:CCW[c][2]};}
function qdec_ec_skewed(c)
{if(view[CCW[c][5]].ant.type!==GATHERER)return opening_queen();if(this_ant().food>0&&view[c].ant.type===MARCHER_A)return{cell:CCW[c][7],type:MARCHER_B};if(this_ant().food>0&&view[c].ant.type===MARCHER_B)return{cell:CCW[c][7],type:MARCHER_A};return opening_queen();}
function qdec_ec_spawn(c)
{if(view[CCW[c][3]].ant.type!==GATHERER)return opening_queen();if(this_ant().food>0&&view[c].ant.type===MARCHER_A)return{cell:CCW[c][1],type:MARCHER_B};if(this_ant().food>0&&view[c].ant.type===MARCHER_B)return{cell:CCW[c][1],type:MARCHER_A};return opening_queen();}
function qdec_cc_edged(c)
{if(view[c].ant.type!==GATHERER)return opening_queen();if(this_ant().food>0&&view[CCW[c][2]].ant.type===MARCHER_A)return{cell:CCW[c][1],type:MARCHER_B};if(this_ant().food>0&&view[CCW[c][2]].ant.type===MARCHER_B)return{cell:CCW[c][1],type:MARCHER_A};return opening_queen();}
function qdec_three_march(c)
{var u_sig=PUPS[c_at(c)][c_at(CCW[c][1])];if(u_sig===D_STALLED)
{if(c_at(CCW[c][3])===D_MARCH&&[D_MARCH,D_GATHERER].includes(c_at(4)))
return sigc(D_STALLED,S_FRONT,c);if(c_at(CCW[c][3])===U_READY&&c_at(4)===D_STALLED)return sigc(U_READY,S_FRONT,c);}
if(u_sig===D_MARCH&&c_at(CCW[c][3])===U_READY&&c_at(4)===U_READY)
return sigc(D_MARCH,S_FRONT,c);if(u_sig===U_READY&&c_at(CCW[c][3])===U_REALIGN&&c_at(4)===U_READY)
if(c_at(CCW[c][1])===D_MARCH)return sigc(D_MARCH,S_FRONT,c);return sigc(c_at(4),S_FRONT,c);}
function qdec_three_stand(c)
{var u_sig=PUPS[c_at(c)][c_at(CCW[c][7])];if(u_sig===D_STALLED)
{if(c_at(CCW[c][3])===D_MARCH&&c_at(4)===D_GATHERER)return sigc(D_STALLED,S_FRONT,c);if(c_at(CCW[c][3])===U_READY&&c_at(4)===D_STALLED)return sigc(U_READY,S_FRONT,c);}
if(u_sig===D_MARCH&&c_at(CCW[c][3])===U_READY&&c_at(4)===U_READY)
return sigc(D_MARCH,S_FRONT,c);if(u_sig===U_READY&&c_at(CCW[c][3])===U_REALIGN&&c_at(4)===U_READY)
if(c_at(CCW[c][1])===D_MARCH)return sigc(D_MARCH,S_FRONT,c);return sigc(c_at(4),S_FRONT,c);}
function qdec_three_recover(c)
{var u_sig=PUPS[c_at(c)][c_at(CCW[c][1])];if(u_sig===D_FOOD)return sigc(D_FOOD,S_FRONT,c);if(this_ant().food>0&&[D_STALLED,U_READY].includes(u_sig))
{var food_factor=QFSPAWNP_MAX-QFSPAWNP_MIN
var food_coefficient=QFSPAWNP_DECAY/food_factor
var actual_prob=food_factor/(food_coefficient*(this_ant().food-1)+1)+QFSPAWNP_MIN;if(rand_choice(actual_prob))return{cell:CCW[c][3]};}
var provisional=lchk(c)
if(provisional!==null)return sigc(provisional,S_FRONT,c);return sigc(c_at(4),S_FRONT,c);}
function qdec_three_unstand(c)
{var u_sig=PUPS[c_at(c)][c_at(CCW[c][7])];if(this_ant().food>0&&u_sig===D_STALLED&&c_at(CCW[c][5])===D_MARCH&&c_at(4)===D_STALLED)
{var food_factor=QBSPAWNP_MAX-QBSPAWNP_MIN
var food_coefficient=QBSPAWNP_DECAY/food_factor
var actual_prob=food_factor/(food_coefficient*(this_ant().food-1)+1)+QBSPAWNP_MIN;if(rand_choice(actual_prob))return{cell:CCW[c][3]};}
if(u_sig===D_STALLED&&c_at(CCW[c][5])===U_READY&&c_at(4)===D_STALLED)
return sigc(U_READY,S_FRONT,c);return sigc(u_sig,S_FRONT,c);}
function qdec_three_block(c)
{var u_sig=PUPS[c_at(c)][c_at(CCW[c][1])];return sigc(u_sig,S_FRONT,c);}
function qdec_three_side(c)
{var u_sig=PUPS[c_at(CCW[c][1])][c_at(CCW[c][2])];return sigc(u_sig,S_FRONT,CCW[c][2]);}
function queen_wait()
{var c=view_corner();switch(neighbor_type(c))
{case ONE_EDGE:{if(this_ant().food>1)return{cell:CCW[c][3],type:GATHERER};}
break;case EC_LEFT:{var u_sig=PUPS[c_at(c)][c_at(CCW[c][1])];if(u_sig===D_GATHERER)return sigc(D_GATHERER,S_FRONT,c);if(u_sig===U_REALIGN&&[U_REALIGN,U_SENTINEL].includes(c_at(c)))
if([U_REALIGN,U_SENTINEL].includes(c_at(CCW[c][1])))
return eqwatch(early_queen());var provisional=lchk(c);if(provisional!==null)return sigc(provisional,S_FRONT,c);if(this_ant().food>1)
{if(c_at(CCW[c][3])!==D_MARCH)return{cell:CCW[c][3],color:D_MARCH};return{cell:CCW[c][3],type:GATHERER};}}
break;case EC_RIGHT:{var u_sig=PUPS[c_at(c)][c_at(CCW[c][7])];if(u_sig===D_GATHERER)return sigc(D_GATHERER,S_FRONT,c);if(u_sig===U_REALIGN&&[U_REALIGN,U_SENTINEL].includes(c_at(c)))
if([U_REALIGN,U_SENTINEL].includes(c_at(CCW[c][7])))
return eqwatch(early_queen());var provisional=lchk(c);if(provisional!==null)return sigc(provisional,S_FRONT,c);if(this_ant().food>1)
{if(c_at(CCW[c][5])!==D_MARCH)return{cell:CCW[c][5],color:D_MARCH};return{cell:CCW[c][5],type:GATHERER};}}
break;}
if(c_at(4)!==U_PANIC)return sigc(U_PANIC,S_SIDE,c);else return opening_queen();}
function queen_march()
{var c=view_corner();switch(neighbor_type(c))
{case EE_STRAIGHT:return qwatch(qdec_ee_straight(c));case EE_BENT:return qwatch(qdec_ee_bent(c));case EC_SKEWED:return qwatch(qdec_ec_skewed(c));case EC_SPAWN:return qwatch(qdec_ec_spawn(c));case CC_EDGED:return qwatch(qdec_cc_edged(c));case THREE_MARCH:return qwatch(qdec_three_march(c));case THREE_STAND:return qwatch(qdec_three_stand(c));case THREE_RECOVER:return qwatch(qdec_three_recover(c));case THREE_UNSTAND:return qwatch(qdec_three_unstand(c));case THREE_BLOCK:return qwatch(qdec_three_block(c));case THREE_SIDE:return qwatch(qdec_three_side(c));default:return eqwatch(early_queen());}}
function queen_decision()
{marcher_count=0;gatherer_count=0;excess_gatherers=0;for(tcell of SCAN_MOVES)
{if(is_ally(tcell))
{if(view[tcell].ant.type===MARCHER_A||view[tcell].ant.type===MARCHER_B)marcher_count++;if(view[tcell].ant.type===GATHERER)
{if(EDGES.includes(tcell)||is_gatherer_marcher(tcell))gatherer_count++;else excess_gatherers++;}}
else if(is_enemy(tcell))return opening_queen();}
if(marcher_count>0&&gatherer_count===1&&excess_gatherers===0)return qwatch(queen_march());else if(marcher_count>0&&gatherer_count===0&&excess_gatherers===0)return qwatch(queen_wait());else if(gatherer_count===1&&excess_gatherers===0)return eqwatch(early_queen());else return opening_queen();}
function main_decide()
{switch(this_ant().type)
{case QUEEN:return queen_decision();case GATHERER:return gatherer_decision();case MARCHER_A:case MARCHER_B:return marcher_decision();default:return sanitize(saboteur());}}
return main_decide();

Огляд

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

У цьому поданні крім королеви використовуються три типи працівників:

  • Тип 1: Марш формування, А фаза
  • Тип 2: Марш формування, B фаза
  • Тип 3: Збирач
  • Тип 4: Зарезервовано для подальшого використання

Мурахи створюються по діагоналі повної ширини таким чином:

    A
    BA
     BA
      BA
       BA
        BA
         BA
          QG

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

Рання фаза

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

Загальна поведінка

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

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

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

Збір продуктів харчування

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

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

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

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

Вимкнено лінію

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

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

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

Екстри

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

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

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

Робити

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

Примітки до випуску

1.0: Перша версія, виставлена ​​для подання, початковий випуск

1.0.1: Виконано логічні скорочення, сумісні з більшістю контролерів

1.1: Ущільнила купу матеріалів, покращила логіку щодо випадків помилок

1.1.1: Виправлення для вирішення проблеми дискваліфікації

1.2: Видалено подальші тупики, диверсанти тепер відремонтовані

1.3: Знижена швидкість нересту королеви, давала диверсантам настрій

1.3.1: Далі знизили нерест королеви та виправили дискваліфікуючу помилку

1.4: Налаштування параметрів


5

Мурашки пароплави

/*Ants will try to move diagonally in the following fashion:
 * 2
 * 51
 *
 *Type 1 and queen are the two core ants
 */


switch (view[4].ant.type) {

  case 1: //Guiding ant
    //Look for queen, try to move diagonally
    if (view[7].ant && view[7].ant.friend && view[7].ant.type === 5 && !view[8].ant) return {cell: 8};
    else if (view[5].ant && view[5].ant.friend && view[5].ant.type === 5 && !view[2].ant) return {cell: 2};
    else if (view[3].ant && view[3].ant.friend && view[3].ant.type === 5 && !view[6].ant) return {cell: 6};
    else if (view[1].ant && view[1].ant.friend && view[1].ant.type === 5 && !view[0].ant) return {cell: 0};
    else return {cell: 4};
  case 2: //Other wing
    //Look for queen, try to move diagonally. If there is food, rotate the other way to start rotating procedure
    if (view[7].ant && view[7].ant.friend && view[7].ant.type === 5 && !view[6].ant) {
      if (view[6].food) {
        if (!view[8].ant) return {cell: 8};
        else return {cell: 4};
      } else return {cell: 6};
    } else if (view[5].ant && view[5].ant.friend && view[5].ant.type === 5 && !view[8].ant) {
      if (view[8].food) {
        if (!view[2].ant) return {cell: 2};
        else return {cell: 4};
      } else return {cell: 8};
    } else if (view[3].ant && view[3].ant.friend && view[3].ant.type === 5 && !view[0].ant) {
      if (view[0].food) {
        if (!view[6].ant) return {cell: 6};
        else return {cell: 4};
      } else return {cell: 0};
    } else if (view[1].ant && view[1].ant.friend && view[1].ant.type === 5 && !view[2].ant) {
      if (view[2].food) {
        if (!view[0].ant) return {cell: 0};
        else return {cell: 4};
      } else return {cell: 2};
    } else return {cell: 4};
  case 5: //Queen ant

    //If forever alone
    if (!view[1].ant && !view[3].ant && !view[5].ant && !view[7].ant) {
      if (view[4].color === 2) { //If on colored square, try to move
        if (view[0].color === 2 && !view[8].ant) return {cell: 8};
        else if (view[2].color === 2 && !view[6].ant) return {cell: 6};
        else if (view[6].color === 2 && !view[2].ant) return {cell: 2};
        else if (view[8].color === 2 && !view[0].ant) return {cell: 0};
        //Can't find color, or path is blocked? try diagonals regardless of color
        else if (!view[0].ant) return {cell: 0};
        else if (!view[2].ant) return {cell: 2};
        else if (!view[6].ant) return {cell: 6};
        else if (!view[8].ant) return {cell: 8};
        //Everything else failed? Stay put.
        else return {cell: 4};
      } else { //If not on colored square, look for food, or set current color to 2.
        if (view[4].ant.food >= 1) { //Try to make Guiding ant
          if (!view[1].ant && !view[1].food) return {cell: 1, type: 1};
          else if (!view[3].ant && !view[3].food) return {cell: 3, type: 1};
          else if (!view[5].ant && !view[5].food) return {cell: 5, type: 1};
          else if (!view[7].ant && !view[7].food) return {cell: 7, type: 1};
        }
        for (var i = 0; i < 9; i++) { //Look for food
          if (view[i].food) return {cell: i};
        }
        return {cell: 4, color:2};
      }
    } else { //Queen has partner
      //Make other wing
      if (view[4].ant.food >= 1) {
        if (view[1].ant && view[1].ant.friend && view[1].ant.type === 1 && !view[3].ant && !view[3].food && !view[5].ant) return {cell: 3, type: 2};
        else if (view[3].ant && view[3].ant.friend && view[3].ant.type === 1 && !view[7].ant && !view[7].food && !view[1].ant) return {cell: 7, type: 2};
        else if (view[5].ant && view[5].ant.friend && view[5].ant.type === 1 && !view[1].ant && !view[1].food && !view[7].ant) return {cell: 1, type: 2};
        else if (view[7].ant && view[7].ant.friend && view[7].ant.type === 1 && !view[5].ant && !view[5].food && !view[3].ant) return {cell: 5, type: 2};
      }

      //If food is orthogonal to Queen, stay put
      if (view[1].food || view[3].food || view[5].food || view[7].food) return {cell: 4};

      //Look for guiding type 1 ant, try to move diagonally
      else if (view[7].ant && view[7].ant.friend && view[7].ant.type === 1 && !view[6].ant) return {cell: 6};
      else if (view[5].ant && view[5].ant.friend && view[5].ant.type === 1 && !view[8].ant) return {cell: 8};
      else if (view[3].ant && view[3].ant.friend && view[3].ant.type === 1 && !view[0].ant) return {cell: 0};
      else if (view[1].ant && view[1].ant.friend && view[1].ant.type === 1 && !view[2].ant) return {cell: 2};
    }
  default: return {cell: 4};
}

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

Фаза 1: Корабці з їжею

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

Фаза 2: 2 мурашки

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

Фаза 3: парування

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

Обертовий

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

Примітка: якщо з якихось причин (можливо, зіткнення з іншим мурахом?) Мураха типу 2 народилася до мурашки типу 1, то це обертання призведе до того, що мураха типу 2 намагатиметься перейти на мурашник типу 1. Щоб вирішити це, мураха типу 2 замість цього залишить себе позаду, а королева дозволить зробити ще один мурашник 2 типу.


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

@Докладіть частину про зміну напрямку - це мене стосується. Якщо я зможу знайти спосіб змінити напрямок, не втрачаючи жодного з своїх працівників, то ця ідея обов'язково спрацює. Якщо ні, то я можу спробувати мати колір в якості маркера, коли слід залишати за собою робітника, і щоб королева змінила напрямок.
К Чжан

@trichoplax Чи знаєте ви насіння, яке було використано під час дискваліфікації, а може бути і номер переміщення? Було б дуже корисно знати обставини, що призвели до цього питання.
К Чжан

@trichoplax Nevermind, я щойно пішов і додав кілька додаткових перевірок. Це (сподіваємось) більше не будемо дискваліфіковані.
K Чжан

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

5

Медуза

function clean(move) {
    if (move["color"] == undefined) {
        if (view[move["cell"]].ant != null) {
            move = {
                cell: 4
            }
        }
        if (move["type"] == undefined) {
            if (view[4].ant.type == 5 && move["cell"] != 4 && view[move["cell"]].color > 2) {
                move["color"] = 1
            }
            if (view[move["cell"]].food == 1 && view[4].ant.type < 5 && view[4].ant.food > 0) {
                move = {
                    cell: 4
                }
            }
        } else if (view[4].ant.type != 5 || view[4].ant.food == 0 || view[move["cell"]].food != 0) {
            move = {
                cell: 4
            }
        }
    }
    return move
}

function coord(cell) {
    var x = (cell % 3) - 1
    var y = 1 - (cell - (cell % 3)) / 3
    return {
        x: x,
        y: y
    }
}

function getcell(x, y) {
    return (x + 1) + (1 - y) * 3
}

var diags = [0, 2, 8, 6]

var colorcounts = [0, 0, 0, 0, 0, 0, 0, 0, 0];
for (var i = 0; i < 9; i++) {
    colorcounts[view[i].color]++
}

var queen = -1
for (var i = 0; i < 9; i++) {
    if (view[i].ant != null && view[i].ant.friend == true && view[i].ant.type == 5) {
        queen = i
    }
}

var guard = -1
for (var i = 0; i < 9; i++) {
    if (view[i].ant != null && view[i].ant.friend == true && view[i].ant.type == 1) {
        guard = i
    }
}

var forager = -1
for (var i = 0; i < 9; i++) {
    if (view[i].ant != null && view[i].ant.friend == true && view[i].ant.type == 2) {
        forager = i
    }
}

var black = -1
for (var i = 0; i < 9; i++) {
    if (view[i].color == 8) {
        black = i
    }
}

var yellow = -1
for (var i = 0; i < 9; i++) {
    if (view[i].color == 2) {
        yellow = i
    }
}


if (view[4].ant.type == 5) {
    if (forager >= 0 && view[forager].color == 8) {
        return clean({
            cell: forager,
            color: 2
        })
    }

    if (guard == -1) {
        if (view[4].color == 3) {
            if (view[4].ant.food > 1) {
                return clean({
                    cell: 0,
                    type: 2
                })
            }
            return clean({
                cell: 0,
                type: 1
            })
        }
        if (view[4].ant.food >= 3) {
            return clean({
                cell: 4,
                color: 3
            })
        }
        if (view[4].color == 1) {
            return clean({
                cell: 4,
                color: 2
            })
        }
        for (var i = 0; i < 9; i++) {
            if (view[i].food == 1) {
                return clean({
                    cell: i
                })
            }
        }
        for (var i = 0; i < 4; i++) {
            if (view[diags[i]].color != 2 && view[diags[(i + 2) % 4]].color == 2) {
                return clean({
                    cell: diags[i]
                })
            }
        }
        return clean({
            cell: 0
        })
    }

    var state = 3
    var max = 0
    for (var i = 3; i <= 4; i++) {
        if (colorcounts[i] > max) {
            max = colorcounts[i]
            state = i
        }
    }

    if (state == 3) {
        if (black >= 0 && forager == -1) {
            return clean({
                cell: black,
                type: 2
            })
        }
        if (forager >= 0 && view[forager].color != 2) {
            return clean({
                cell: 0,
                color: 8
            })
        }
        if (colorcounts[3] == 9) {
            return clean({
                cell: 4,
                color: 4
            })
        }
    }
    if (state == 4) {
        if (colorcounts[4] == 9) {
            return clean({
                cell: 4,
                color: 3
            })
        }
    }
    return clean({
        cell: 4
    })
}
if (view[4].ant.type == 1) {
    var dest = 0
    var destmap = [1, 0, 1, 1, 4, 1, 7, 8, 7]
    dest = destmap[queen]
    if (view[queen].color != view[dest].color && (view[queen].color == view[4].color || view[4].color == view[dest].color)) {
        if (queen < 4 && view[dest].color > 2 && view[dest].color < 5) {
            return clean({
                cell: queen,
                color: view[dest].color
            })
        }
        return clean({
            cell: dest,
            color: view[queen].color
        })
    }
    return clean({
        cell: dest
    })
}
if (view[4].ant.type == 2) {
    if (queen >= 0 && view[4].color == 8) {
        return clean({
            cell: 4
        })
    }
    var state = 3
    var max = 0
    for (var i = 5; i <= 7; i++) {
        if (colorcounts[i] > max) {
            max = colorcounts[i]
            state = i
        }
    }
    var flowx = 0
    var flowy = 0
    for (var i = 0; i < 9; i++) {
        for (var j = i + 1; j < 9; j++) {
            var loci = coord(i)
            var locj = coord(j)
            var dx = locj.x - loci.x
            var dy = locj.y - loci.y
            var cyc = 0
            if (view[i].color >= 5 && view[i].color <= 7 && view[j].color >= 5 && view[j].color <= 7) {
                var cyc = ((view[j].color - view[i].color) % 3 + 3) % 3
                if (cyc == 2) {
                    cyc = -1
                }
            } else if (view[i].color >= 5 && view[i].color <= 7) {
                cyc = 0.1
            } else {
                cyc = -0.1
            }
            flowx += cyc * dx / (dx * dx + dy * dy)
            flowy += cyc * dy / (dx * dx + dy * dy)

        }
    }
    if (flowx * flowx > flowy * flowy) {
        flowy = 0
    } else {
        flowx = 0
    }
    if (flowx < 0) {
        flowx = -1
    }
    if (flowy < 0) {
        flowy = -1
    }
    if (flowx > 0) {
        flowx = 1
    }
    if (flowy > 0) {
        flowy = 1
    }
    if (queen >= 0) {
        var locq = coord(queen)
        flowx = -locq.x
        flowy = -locq.y
        state = 5
    }
    if (view[4].ant.food > 0) {
        if (guard >= 0) {
            var destmap = [1, 0, 1, 1, 4, 1, 7, 8, 7]
            return clean({
                cell: destmap[guard]
            })
        }
        dest = getcell(-flowx, -flowy)
        if (dest != 7) {
            dest = 1
        }
        if (view[dest].color >= 5 && view[dest].color <= 7) {
            return clean({
                cell: dest
            })
        }
        if (view[dest - 1].color >= 5 && view[dest - 1].color <= 7) {
            return clean({
                cell: dest - 1
            })
        }
        return clean({
            cell: 4
        })
    }
    if (view[4].color >= 5 && view[4].color <= 7) {
        state = view[4].color
    }
    var nextc = ((state - 4) % 3 + 5)
    var prevc = ((state - 3) % 3 + 5)
    var centerdest
    centerdest = getcell(flowx, flowy)
    if (view[centerdest].color != state && view[centerdest].color != nextc) {
        return clean({
            cell: centerdest,
            color: nextc
        })
    }
    for (var dest = 1; dest < 9; dest++) {
        var locd = coord(dest)
        var net = locd.x * flowx + locd.y * flowy
        if (net > 0 && view[dest].color != view[centerdest].color) {
            return clean({
                cell: dest,
                color: view[centerdest].color
            })
        }
    }
    for (var dest = 0; dest < 9; dest++) {
        if (view[dest].food == 1) {
            if (view[dest].color >= 5 && view[dest].color <= 7) {
                return clean({
                    cell: dest
                })
            }
            return clean({
                cell: dest,
                color: state
            })
        }
    }
    if (centerdest == 4 && view[0].color >= 5 && view[0].color <= 7) {
        return clean({
            cell: 0
        })
    }
    if (centerdest > 0 && view[centerdest - 1].color >= 5 && view[centerdest - 1].color <= 7) {
        return clean({
            cell: centerdest - 1
        })
    }
    return clean({
        cell: centerdest
    })
}

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

Медуза в дії

Фаза 1: Початкові інвестиції

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

Фаза 2: Колонізуйте

Тут три види мурашок грають різні ролі:

Королева

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

Охорона

Охоронець живе все життя поруч із королевою, обходячи її випадковим чином.

Охоронець відіграє ключову роль у підтримці держави королеви. Він намагається виправити будь-які помилки в області 3x3 королеви. Якщо в даній області є два дійсних альтернативних кольору, який із двох кольорів переходить у «виправлений» стан, є відносно випадковим. Однак після досягнення консенсусу королева перевертає свій квадрат до протилежного кольору, повторно ініціюючи процес. Саме це викликає коливання стану королеви, і це робиться дуже стійким до помилок.

Охоронець також виконує функції воротаря "палацу" королеви. Коли фуражник бачить охоронець, він може рухатися поруч із королевою, навіть якщо королева знаходиться поза зоною зору.

Кормозбирачі

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

Типовий шлях фуражу:

Шлях фуражу

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


Цікаво, чи зможе мій слід-гумка змогти накручувати корми. Добре стирає широкі шляхи, як ви бачите, коли це йде проти чорної діри.
pppery

@ppperry Іноді це робиться.
PhiNotPi

Чи можна було б зробити так, щоб робітники поверталися ефективніше, лише рухаючись циклом r-> b-> g-> r?
CalculatorFeline

@trichoplax Я не здогадувався, що це щось не було дозволено робити. Будь-який виправлений зараз.
PhiNotPi

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

5

Броунівський джиг

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

Перший блок коду у відповіді - це той, який автоматично включається в гру:

// Full version that won't be disqualified for moving onto another ant

// Move to food if visible
for (var i=0; i<9; i+=1) {
    if (view[i].food) {
        return {cell:i}
    }
}

// Otherwise move to one of the diagonal cells if not occupied
for (var i=0; i<9; i+=2) {
    if (!view[i].ant) {
        return {cell:i}
    }
}

// Otherwise one of the vertical or horizontal cells if not occupied
for (var i=1; i<9; i+=2) {
    if (!view[i].ant) {
        return {cell:i}
    }
}

// Otherwise don't move at all
return {cell:4}

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

// Basic version for an intuitive understanding

// Move to food if visible
for (var i=0; i<9; i+=1) {
    if (view[i].food) {
        return {cell:i}
    }
}

// Otherwise move "up and left", which will be a random direction
return {cell:0}

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

Для прикладу створення прямолінійного руху, незважаючи на випадкову орієнтацію введення, див. Романеско-роуд .


Чому потрібно маркувати кольори?
Соломон Учко

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

Я бачу, має сенс.
Соломон Учко

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

4

La Reine Bleue

var Queen = 5;
var QueenTrail = [];
var EnemyAnts = [];
var EnemyColors = [];
var QueenTrailColor = 7;
var QueenTrailColor2 = 8;
var QueensPosition = -1; //Future use...

var rotations =   
[ 0,1,2,
  3,4,5,
  6,7,8,

  6,3,0,
  7,4,1,
  8,5,2,

  8,7,6,
  5,4,3,
  2,1,0,

  2,5,8,
  1,4,7,
  0,3,6];

var moves = [];
getMoves();
return findBestMove();

function getMoves()
{
    var matchIdx = -1;
    //Initialization of current state
    for(ii = 0; ii < 9; ii++)
    {
        if(ii != 4)
        {
            if(view[ii].color == QueenTrailColor)
            {
                QueenTrail.push(ii);
            }
            else if(view[ii].color == QueenTrailColor2)
            {
                QueenTrail.push(ii);
            }
            else if(view[ii].color != 1)
            {
                EnemyColors.push(ii);
            }
        }

        if(ii != 4 && view[ii].ant != null)
        {
            if(view[ii].ant.friend)
            {
                if(view[ii].ant.type == Queen)
                {
                    QueensPosition = ii * ii;
                }
            }
            else
            {
                EnemyAnts.push(ii);
            }
        }
    }

    switch (view[4].ant.type) 
    {
        case Queen:
        {        
            //first get the food
            for (var ii = 0; ii < 9; ii++) 
            {
                if (view[ii].food > 0 && view[ii].ant == null) 
                {
                    moves.push(getCell(ii)) ;
                }
            }
            if(EnemyAnts.length == 0)
            {
                lm(AA(-QueenTrailColor),AA(4), {cell:4, color:QueenTrailColor});
            }

            if(QueenTrail.length >= 5 || EnemyAnts.length > 0)
            {
                lm(AA(-QueenTrailColor), AA(0,1,2),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,1,3),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,1,5),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,1,6),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,1,7),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,1,8),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,2,3),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,2,6),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,2,7),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,3,7),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,3,8),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,5,7),{cell:0});
                lm(AA(-QueenTrailColor), AA(1,2,7),{cell:1});
                lm(AA(-QueenTrailColor), AA(1,3,5),{cell:1});
                lm(AA(-QueenTrailColor), AA(0,1),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,2),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,3),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,5),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,7),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,8),{cell:0});
                lm(AA(-QueenTrailColor), AA(1,3),{cell:1});
                lm(AA(-QueenTrailColor), AA(1,7),{cell:1});
            }
            if(QueenTrail.length == 4)
            {
                lmQT(AA(0,1,2,3),{cell:7});
                lmQT(AA(0,1,2,5),{cell:7});
                lmQT(AA(0,1,2,6),{cell:7});
                lmQT(AA(0,1,2,7),{cell:5});
                lmQT(AA(0,1,2,8),{cell:7});
                lmQT(AA(0,1,3,5),{cell:7});
                lmQT(AA(0,1,3,7),{cell:8});
                lmQT(AA(0,1,3,8),{cell:2});
                lmQT(AA(0,1,5,6),{cell:8});
                lmQT(AA(0,1,5,7),{cell:6});
                lmQT(AA(0,1,5,8),{cell:3});
                lmQT(AA(0,1,6,7),{cell:8});
                lmQT(AA(0,1,6,8),{cell:2});
                lmQT(AA(0,1,7,8),{cell:2});
                lmQT(AA(0,2,3,7),{cell:8});
                lmQT(AA(0,2,3,8),{cell:6});
                lmQT(AA(0,2,6,8),{cell:1});
                lmQT(AA(0,3,5,7),{cell:2});
                lmQT(AA(0,3,5,8),{cell:2});
                lmQT(AA(1,3,5,7),{cell:0});
            }
            if(QueenTrail.length == 1)
            {                
                lmQT(AA(0), {cell:8});
                lmQT(AA(1), {cell:7});
            }
            else if(QueenTrail.length == 0)
            {
                moves.push(getCell(1));
            }

            if(QueenTrail.length == 0) // starting out or someone is messing with us
            {
                moves.push( getCellColor(1, QueenTrailColor));
            }    
            else if (QueenTrail.length >= 5) //queen is stuck? move her randomly until we get a straight trail
            {    
                moves.push( getCellColor(1, QueenTrailColor2));
            }
            else if (QueenTrail.length >= 3)
            {
                lmQT(AA(0,1,2),{cell:3});
                lmQT(AA(0,1,3),{cell:7});
                lmQT(AA(0,1,5),{cell:3});
                lmQT(AA(0,1,6),{cell:7});
                lmQT(AA(0,1,7),{cell:3});
                lmQT(AA(0,1,8),{cell:3});
                lmQT(AA(0,2,3),{cell:6});
                lmQT(AA(0,2,6),{cell:3});
                lmQT(AA(0,2,7),{cell:1});
                lmQT(AA(0,3,7),{cell:8});
                lmQT(AA(0,3,8),{cell:5});
                lmQT(AA(0,5,7),{cell:6});
                lmQT(AA(1,2,7),{cell:0});
                lmQT(AA(1,3,5),{cell:6});
            }
            else if(QueenTrail.length == 2)
            {
                lmQT(AA(0,1),{cell:7});
                lmQT(AA(0,2),getCellColor(1, QueenTrailColor));
                lmQT(AA(0,3),{cell:6});
                lmQT(AA(0,5),{cell:1});
                lmQT(AA(0,7),{cell:3});
                lmQT(AA(0,8),{cell:3});
                lmQT(AA(1,3),{cell:6});
                lmQT(AA(1,7),{cell:0});
            }
            else if(QueenTrail.length == 1) //we are either going in a straight line or trapped?
            {    
                if(view[4].ant.food > 0)
                {
                    lmQT(AA(0), getCell(0));
                    //clear out the area for the ants
                    if(EnemyColors.length > 0)
                    {
                        moves.push( getCellColor(EnemyColors[0],1));
                    }
                }
                lmQT(AA(0), getCell(8));
                lmQT(AA(1), getCell(7));
                lmQT(AA(2), getCell(6));
            }
            break;
        }
    }
    moves.push( getCell(4));
}

function leftOfPos(x)
{
    if (x == 0)
    {
        return 3;
    }
    else if (x == 1)
    {
        return 0;
    }
    else if (x == 2)
    {
        return 1;
    }
    else if (x == 3)
    {
        return 6;
    }
    else if (x == 5)
    {
        return 2;
    }
    else if (x == 6)
    {
        return 7;
    }
    else if (x == 7)
    {
        return 8;
    }
    else if (x == 8)
    {
        return 5;
    }
}

function findBestMove() 
{
    var keeper = 0;
    for(var ii = 0; ii < moves.length ; ii++)
    {
        if(moves[ii].cell < 0 || moves[ii].cell > 8 || (moves[ii].cell != 4 && (moves[ii].color == null || moves[ii].color == 0) && view[moves[ii].cell].ant != null) || (view[moves[ii].cell].food > 0 && (view[4].ant.food > 0 && view[4].ant.type < 5))) 
        {
            continue;
        }
        else if(moves[ii].type != null && (view[moves[ii].cell].ant != null || view[moves[ii].cell].food > 0 || view[0].color == 1)) //semi random here. 
        {
            continue;
        }
        else
        {
            keeper = ii;
            break;
        }
    }
    return moves[keeper];
}

function lm(matchingColors, coords, matchCell)
{
    var matchTarget = coords.length ;
    var matchCount = [0,0,0,0];
    var returnVal = -1;
    for(var ii = 0; ii < coords.length; ii++)
    {        
        for(var jj = 0; jj < 4; jj++)
        {
            var actualIndex = rotations[coords[ii] + (jj * 9)];
            var foundMatch = false;
            for(var kk = 0; kk < matchingColors.length; kk++)
            {
                var matchingColor = matchingColors[kk];

                if(matchingColor >= 1 && matchingColor <= 8 && view[actualIndex].color == matchingColor)
                {
                    foundMatch = true;
                    break;
                }    
                else if(matchingColor < 0 && view[actualIndex].color != -matchingColor)
                {
                    foundMatch = true;
                    break;
                }
            }
            if(foundMatch)
            {
                matchCount[jj] = matchCount[jj] + 1;
                if(matchCount[jj] == matchTarget)
                {
                    matchCell.cell = rotations[matchCell.cell + (jj * 9)];
                    moves.push(matchCell);
                    returnVal = jj;
                }
            }
        }
    }
    return returnVal;
}

function lmQT(coords, matchCell)
{
    return lm(AA(QueenTrailColor, QueenTrailColor2), coords,matchCell);
}

function AA()
{
    return arguments;
}

function getCell(x)
{
    return {cell:x};
}

function getCellColor(x, y)
{
    return {cell:x, color:y};
}

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

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


3
Ласкаво просимо на сайт! :)
DJMcMayhem

Добре бути тут!
Якийсь бігун хлопець

4

FireFlyMkII

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

    // maps current view's cells' indecies to the rotated cell's location's indecies for each direction
    var rotate = 
            [[0,1,2,3,4,5,6,7,8],
             [2,5,8,1,4,7,0,3,6],
             [8,7,6,5,4,3,2,1,0],
             [6,3,0,7,4,1,8,5,2]];

    // the colours that form the pattern of the trail back to the queen
    var TRAIL_COLOR_A = 8;
    var TRAIL_COLOR_B = 2;
    var TRAIL_COLOR_C = 5;
    var trailColours = [TRAIL_COLOR_A,TRAIL_COLOR_B,TRAIL_COLOR_C];
    var trailColoursLookUp = [-1,-1,1,-1,-1,2,-1,-1,0];

    var ORIENTATION_MARKER = 8;

    // Queens Modes
    var QUEEN_MODE_HUNTING_MOVING = 6;
    var QUEEN_MODE_HUNTING_PAINTING = 1;
    var QUEEN_MODE_RESETTING = 5;
    var QUEEN_MODE_RESETTING_SPAWNING = 3;
    var QUEEN_MODE_COUNTING_EVEN = 7;
    var QUEEN_MODE_COUNTING_ODD = 4;
    var QUEEN_MODE_NESTING = 2;

    // the number of non-blank (i.e. not colour 1 ) colours to used to encode the queen's worker spawn counter. Min of 1. Max of 7
    var SPAWN_COUNTER_NON_BLANK_COLOURS_COUNT = 7;


    // the maximum number that can be encoded using the queen's worker spawn counter
    var SPAWN_COUNTER_MAX = SPAWN_COUNTER_NON_BLANK_COLOURS_COUNT*SPAWN_COUNTER_NON_BLANK_COLOURS_COUNT*SPAWN_COUNTER_NON_BLANK_COLOURS_COUNT -1;//SPAWN_COUNTER_NON_BLANK_COLOURS_COUNT * SPAWN_COUNTER_USED_CELLS_COUNT_MAX;

    // the minimum game ticks between spawning a worker. Min of 0, Max of SPAWN_COUNTER_MAX
    var TICKS_BETWEEN_FOOD_RETURN_MAX = 50;

    // No Operation... i.e. stay put do nothing
    var NO_OP = {cell:4};

    var ANT_TYPE_WORKER = 1; 
    var ANT_TYPE_QUEEN = 5;

    var orientationMarkerRotation = -1;

    var i=0;
    var j=0;

    // returns true of the provided colour is a trail colour
function isTrailColour(colour)
    {
        return colour === trailColours[0] || colour === trailColours[1] || colour === trailColours[2];
    }

    // returns the colour of the colour in the trail away from queen
function nextTrailColor(currentTrailColour)
    {
        return trailColours[(trailColoursLookUp[currentTrailColour]+1)%3];
    }

    // returns the colour of the colour in the trail toward from queen
function prevTrailColor(currentTrailColour)
    {
        return trailColours[(3+trailColoursLookUp[currentTrailColour]-1)%3];
    }

    // RNG
function randomNumberGenerator(seed)
    {
        return (1103515245 * seed + 12345) % 2147483647;
    }

    // returns a positive random integer based on the provided ant's view and seed
function randomInt(view,seed)
    {
        for (var i=0;i<9;i++)
        {
            if (view[i].ant !=null)
            {
                seed=randomNumberGenerator(seed+view[i].ant.food);
                seed=randomNumberGenerator(seed+view[i].ant.type);
                seed=randomNumberGenerator(seed+(view[i].ant.friend?1:0));
            }
            seed=randomNumberGenerator(seed+view[i].color);
            seed=randomNumberGenerator(seed+view[i].food);
        }
        return seed<0?-seed:seed;
    }

    // SHUFFLE *NOT* IMPLEMENTED 
function shuffleIndecies(view,seed,range)
    {
        var indecies = new Array(range);

        for (var i=0;i<range;i++)
        {
            indecies[i]=i;
        }

        return indecies;
    }

function processOrientation(view)
    {
        // count orientation markers
        var orientationMarkerCount = 0;
        for (var i=0; i<rotate.length;i++)
        {
            if (view[rotate[i][1]].color === ORIENTATION_MARKER)
            {
                orientationMarkerCount++;
                orientationMarkerRotation = i;
            }
        }

        // corruption detected
        if (orientationMarkerCount >1)
        {
            return {cell:4, color: QUEEN_MODE_RESETTING};
        }

        // place the orientation marker
        if (orientationMarkerCount === 0)
        {
            return {cell:1, color: ORIENTATION_MARKER};
        }
        return null;
    }

function incrementSpawnCounter(view)
    {
        var action = processOrientation(view);
        if (action != null)
        {
            return action;
        }

        var newCount = decodeThreeCellsToInt(view) + 1;

        var MSD = view[rotate[orientationMarkerRotation][3]].color-1;
        var NSD = view[rotate[orientationMarkerRotation][7]].color-1;
        var LSD = view[rotate[orientationMarkerRotation][5]].color-1;

        var MSDisEven =(MSD & 1) ===0;
        var NSDisEven =(NSD & 1) ===0; 

        var MSDdelta =  Math.floor(newCount / 49) - MSD;
        var NSDdelta =  (MSDisEven ? Math.floor(Math.floor(newCount%49)/7) :( 6 - Math.floor(Math.floor(newCount%49)/7))) - NSD;
        var LSDdelta =  (((MSDisEven && NSDisEven) || (!MSDisEven && !NSDisEven)) ? Math.floor(newCount%7) :( 6 - Math.floor(newCount%7))) - LSD;

        // check for roll over 
        if (MSDdelta > 6)
        {
            return {cell:rotate[orientationMarkerRotation][3], color:1};
        }

        // Most Significant Digit (cell) update
        if (MSDdelta != 0)
        {
            return {cell:rotate[orientationMarkerRotation][3], color:(MSD+MSDdelta)+1};
        }

        // Next Significant Digit (cell) update
        if (NSDdelta != 0)
        {
            return {cell:rotate[orientationMarkerRotation][7], color:(NSD+NSDdelta)+1};
        }

        // Least Significant Digit (cell) update
        if (LSDdelta != 0)
        {
            return {cell:rotate[orientationMarkerRotation][5], color:(LSD+LSDdelta)+1};
        }

        return null;
    }

function decodeThreeCellsToInt(view)
    {
        var MSD = view[rotate[orientationMarkerRotation][3]].color-1;
        var NSD = view[rotate[orientationMarkerRotation][7]].color-1;
        var LSD = view[rotate[orientationMarkerRotation][5]].color-1;

        var MSDisEven =(MSD & 1) ===0;
        var NSDisEven =(NSD & 1) ===0; 
        return MSD * 49 + 
               (MSDisEven?NSD:6-NSD) * 7 + 
               ((MSDisEven && NSDisEven) || (!MSDisEven && !NSDisEven)?LSD:6-LSD);
    }
    // Performs a paint command to reset the queen's worker spawn counter to 0.
    // NOTE that is may take multiple calls on sequential game ticks to complete the reset.
    // returns null if the counter is reset
function resetSpawnCounter(view)
    {
        var orientationMarkerCount = 0;
        for (i=1; i<9; i+=2) 
        {
            if (view[i].color === ORIENTATION_MARKER && orientationMarkerCount ===0)
            {
                orientationMarkerCount++;
            }
            else if (view[i].color!=1)
            {
                return {cell:i, color:1};
            }
        }

        // place the orientation marker
        if (orientationMarkerCount === 0)
        {
            return {cell:1, color: ORIENTATION_MARKER};
        }

        return null;
    }


function spawnNewWorker(view,type,defaultAction)
    {
        // ensure that we do not try and create a worker when having no food
        if (view[4].ant.food > 0)
        {
            // now try to spawn an ant
            if (view[1].ant===null && view[1].food===0)
            {
                return {cell:1, type:type};
            }

            // previous spawn cell was blocked, try another 
            if (view[3].ant===null && view[3].food===0)
            {
                return {cell:3, type:type};
            }

            // previous spawn cell was blocked, try another
            if (view[5].ant===null && view[5].food===0)
            {
                return {cell:5, type:type};
            }

            // previous spawn cell was blocked, try another
            if (view[7].ant===null && view[7].food===0)
            {
                return {cell:7, type:type};
            }
        }
        return defaultAction;
    }

function isCellTrailToQueen(cell,currentAntCellColour)
    {
        // is cell containing our queen, or is the cell the next cell colour on the trail back
        return (cell.ant!=null && cell.ant.friend && cell.ant.type === ANT_TYPE_QUEEN) ||
                cell.color === prevTrailColor(currentAntCellColour);
    }

    // entry point into ant logic
function getAction(view)
    {
        var random = 1;
        var food = view[4].ant.food;
        var currentCellColour = view[4].color;

/////////////////////////////////////// QUEEN ///////////////////////////////////////
        if (view[4].ant.type === ANT_TYPE_QUEEN)
        {
            // move to visible food. this queen is greedy!
            for (i=0; i<9; i++) 
            {
                if (view[i].food>0) {
                    return {cell:i};
                }
            }

            // see if we have spawned a worker in the last few turns
            var workerSpawned = false;
            // look in orthogonal cells
            for (i=1; i<9; i+=2) 
            {
                // ant detected and is friendly and of worker type
                if (view[i].ant !=null && view[i].ant.friend && view[i].ant.type===ANT_TYPE_WORKER)
                {
                    workerSpawned=true;
                    break;
                }
            }

            var queenMode = currentCellColour;
            var foodModRemainder=1;

            var minFoodLatch = 0;
            if (food>=500) minFoodLatch = 500;
            else if (food>=400) minFoodLatch = 400;
            else if (food>=300) minFoodLatch = 300;
            else if (food>=200) minFoodLatch = 200;
            else if (food>=100) minFoodLatch = 100;
            else if (food>=50) minFoodLatch = 50;
            else if (food>=20) minFoodLatch = 30;
            else if (food>=20) minFoodLatch = 20;
            else if (food>=10) minFoodLatch = 10;
            else if (food>=5) minFoodLatch = 5;

            switch (queenMode)
            {
                case QUEEN_MODE_HUNTING_MOVING:
                {


                    // move to the cell mirror of the trail cell
                    for (i=0; i<9; i++) {
                        if (view[i].ant===null && view[i].color===QUEEN_MODE_HUNTING_MOVING) {
                            if (view[8-i].ant==null)
                            {
                                return {cell:8-i};
                            }
                        }
                    }

                    // Otherwise move to one of the diagonal cells if not occupied
                    for (i=0; i<9; i+=2) 
                    {
                        if (view[i].ant===null) 
                        {
                            return {cell:i};
                        }
                    }

                    // Otherwise move to one of the vertical or horizontal cells if not occupied
                    for (i=1; i<9; i+=2)
                    {
                        if (view[i].ant===null)
                        {
                            return {cell:i};
                        }
                    }
                    return {cell:4};
                }
                case QUEEN_MODE_HUNTING_PAINTING:
                {
                    // no food found, change to move mode
                    if (food ===0)
                    {
                        // Queenie places a trail
                        return {cell:4, color:QUEEN_MODE_HUNTING_MOVING};
                    }
                    // found food, now change to nesting mode
                    else
                    {
                        return {cell:4, color:QUEEN_MODE_NESTING};
                    }
                }

                // initialise colony
                case QUEEN_MODE_NESTING:
                {
                    // we have spawned a worker so change to counting mode
                    if (workerSpawned===true)
                    {
                        return {cell:4, color:QUEEN_MODE_COUNTING_ODD};
                    }

                    var action = processOrientation(view);
                    if (action != null)
                    {
                        return action;
                    }

                    // ensure that we have the initial band constructed around the queen
                    for (i=0; i<9; i+=2) 
                    {
                        if (i!=4 && view[i].color!=TRAIL_COLOR_A)
                        {
                            return {cell:i, color:TRAIL_COLOR_A};
                        }
                    }

                    // ensure that the counter cells are reset.
                    action = resetSpawnCounter(view);
                    if (action != null)
                    {
                        return action;
                    }

                    // spawn initial worker
                    return spawnNewWorker(view, ANT_TYPE_WORKER, NO_OP);
                }


                case QUEEN_MODE_RESETTING_SPAWNING:

                    // spawn the worker if we have not spawned a worker in the last few turns 
                    if (!workerSpawned===true)
                    {
                        return spawnNewWorker(view, ANT_TYPE_WORKER, NO_OP);
                    }
                    // must have spawned a worker previously, so reset the counter 
                    var action = resetSpawnCounter(view);

                    // still in process of resetting counter...
                    if (action != null)
                    {
                        return action;
                    }


                    // spawn counter as been reset. We will set the queen back to counting mode;
                    return {cell:4, color:food%2===0?QUEEN_MODE_COUNTING_EVEN:QUEEN_MODE_COUNTING_ODD};

                case QUEEN_MODE_RESETTING:

                    action = resetSpawnCounter(view);

                    // still in process of resetting counter...
                    if (action != null)
                    {
                        return action;
                    }


                    // spawn counter as been reset. We will set the queen back to counting mode;
                    return {cell:4, color:food%2===0?QUEEN_MODE_COUNTING_ODD:QUEEN_MODE_COUNTING_EVEN};

                case QUEEN_MODE_COUNTING_ODD:
                    foodModRemainder = 2;
                case QUEEN_MODE_COUNTING_EVEN:
                {
                    foodModRemainder--;

                    action = processOrientation(view);
                    if (action != null)
                    {
                        return action;
                    }

                    var spawnCounter = decodeThreeCellsToInt(view);

                    // repair any damage to the initial band constructed around the queen
                    for (i=0; i<9; i+=2) 
                    {
                        if (i!=4 && view[i].color!=TRAIL_COLOR_A)
                        {
                            return {cell:i, color:TRAIL_COLOR_A};
                        }
                    }

//                    // spawn interval time threshold as been reached and we have food to convert into workers...
//                    if (spawnCounter>=TICKS_BETWEEN_FOOD_RETURN_MAX && food>minFoodLatch)
//                    {
//                        // change to reset spawn counter mode to spawn a new worker
//                        return new Paint(4,QUEEN_MODE_RESETTING_SPAWNING);
//                    }
//
//                    // Check to see if a worker has just returned some food.
//                    if (food>0 && food%2 == foodModRemainder)
//                    {
//                        // change to reset spawn counter mode
//                        return new Paint(4,QUEEN_MODE_RESETTING);
//                    }

                    // Check to see if a worker has just returned some food.
                    if (food>0 && food%2 === foodModRemainder)
                    {

                        // spawn interval time threshold as been reached and we have food to convert into workers...
                        if (spawnCounter>=TICKS_BETWEEN_FOOD_RETURN_MAX && food>0)
                        {
                            // change to reset spawn counter mode to spawn a new worker
                            return {cell:4, color:QUEEN_MODE_RESETTING_SPAWNING};
                        }

                        // change to reset spawn counter mode
                        return {cell:4, color:QUEEN_MODE_RESETTING};
                    }


                    if (spawnCounter < SPAWN_COUNTER_MAX)
                    {
                        // simply increment the counter
                        return incrementSpawnCounter(view);
                    }

                    return NO_OP;
                }
                default:
                {
                }

            }


        }

/////////////////////////////////////// WORKER ///////////////////////////////////////



        var expectedNextPathColourToEdge = nextTrailColor(currentCellColour);

        // worker is looking for food
        if (food===0)
        {
/////////////////////////////////// WORKER HUNTNING //////////////////////////////////    

            // determine whether we are a recently spawned worker
            for (var i=1;i<9;i+=2)
            {
                // are we orthogonal to the queen?
                if (view[i].ant!=null && view[i].ant.friend && view[i].ant.type === ANT_TYPE_QUEEN)
                {
                    // test to see whether queen's counter has reset so worker is free to move
                    if (view[i].color === QUEEN_MODE_COUNTING_ODD || view[i].color === QUEEN_MODE_COUNTING_EVEN )
                    {
                        for (var j=1;j<9;j+=2)
                        {
                            if (view[j].ant===null && view[j].color===TRAIL_COLOR_A)
                            {
                                return {cell:j};
                            }
                        }
                    }
                    // wait until queen's counter has reset
                    return NO_OP;
                }
            }

//            // this is to try and unstick stuck ants... not overly well i might add
//            if (randomInt(view,1)%20==1)
//            {
//                // attempt to pick an empty random trail-cell
//                for (i = randomInt(view,666)%9;i>0;i--)
//                {
//                    if (view[i].ant==null && view[i].food==0 && isTrailColour(view[i].color))
//                    {
//                        return new Move(i);
//                    }
//                }
//            }

            // see if there is any food off band that is enclosed or almost enclosed by trail cells
            // if so, then move to claim the food
            if (view[1].food>0 &&
                isTrailColour(view[0].color) &&
                isTrailColour(view[2].color))
            {
                return {cell:1};
            }

            if (view[3].food>0 &&
                isTrailColour(view[0].color) &&
                isTrailColour(view[6].color))
            {
                return {cell:3};
            }

            if (view[5].food>0 &&
                isTrailColour(view[2].color) &&
                isTrailColour(view[8].color))
            {
                return {cell:5};
            }

            if (view[7].food>0 &&
                isTrailColour(view[6].color) &&
                isTrailColour(view[8].color))
            {
                return {cell:7};
            }


            // if not on trail, attempt see if cells surrounding are trail colours... and set our own cell accordingly
            if (!isTrailColour(currentCellColour))
            {
                for (var priority = 0; priority <4;priority++)
                {
                    // repeat for each rotation
                    var indecies = shuffleIndecies(view,random,4);
                    for (var j=0;j<4;j++)
                    {
                        var r = indecies[j];
                        switch(priority)
                        {
                        // C??  ...
                        // ???  .P.
                        // P?N  ...
                        case 0:
                            if (isTrailColour(view[rotate[r][0]].color) && 
                                nextTrailColor(view[rotate[r][0]].color) === view[rotate[r][8]].color &&
                                prevTrailColor(view[rotate[r][0]].color) === view[rotate[r][6]].color)
                            {
                                return {cell:4, color: view[rotate[r][6]].color};
                            }
                            break;

                        // ??C  ...
                        // ???  .C.
                        // N?C  ...
                        case 1:
                            if (isTrailColour(view[rotate[r][2]].color) && 
                                view[rotate[r][2]].color === view[rotate[r][8]].color &&
                                nextTrailColor(view[rotate[r][2]].color) === view[rotate[r][6]].color)
                            {
                                return {cell:4, color: view[rotate[r][2]].color};
                            }
                            break;

                        // C??  ...
                        // ???  .C.
                        // P??  ...
                        case 2:
                            if (isTrailColour(view[rotate[r][0]].color) && 
                                prevTrailColor(view[rotate[r][0]].color) === view[rotate[r][6]].color)
                            {
                                return {cell:4, color: view[rotate[r][0]].color};
                            }
                            break;

                        // C??  ...
                        // ???  .C.
                        // ???  ...
                        case 3:
                            if (isTrailColour(view[rotate[r][0]].color))
                            {
                                return {cell:4, color: view[rotate[r][0]].color};
                            }
                            break;
                        }
                    }
                }
                // we are completely lost! lets perform a random walk and hopefully find the surface again
                return {cell:view[2].ant === null?2:4};
            }

            // decide worker action...
            for (var priority = 0; priority <13;priority++)
            {
                // repeat for each rotation
                var indecies = shuffleIndecies(view,random,4);
                for (var j=0;j<4;j++)
                {
                    var r = indecies[j];
                    var cellToMoveTo =-1;

                    switch(priority)
                    {
                    /////// AVOID MOVING/PAINTING LOCK-STEP ///////

                    // X?X  ...
                    // ?C?  .M.
                    // C?W  ...

                    case 0:
                        if (view[rotate[r][6]].color === currentCellColour &&
                            !isTrailColour(view[rotate[r][0]].color) &&
                            !isTrailColour(view[rotate[r][2]].color) &&
                            view[rotate[r][8]].ant!=null &&
                            view[rotate[r][8]].ant.type!=ANT_TYPE_QUEEN) 
                        {
                            // step back on path back to queen
                            return NO_OP;
                        }
                        break;

                    /////// REPAIR PATH ///////

                    // P?X  ..C
                    // ?C?  ...
                    // C??  ...

                    case 1:
                        if (view[rotate[r][2]].color != currentCellColour &&
                            view[rotate[r][6]].color === currentCellColour &&
                            isCellTrailToQueen(view[rotate[r][0]],currentCellColour))
                        {
                            return {cell:rotate[r][2], color:currentCellColour};
                        }
                        break;

                    // ??C  N..
                    // ?C?  ...
                    // X?P  ...

                    case 2:
                        if (view[rotate[r][2]].color === currentCellColour &&
                            isCellTrailToQueen(view[rotate[r][8]],currentCellColour) &&
                            !isTrailColour(view[rotate[r][6]].color) &&
                            view[rotate[r][0]].color != expectedNextPathColourToEdge)
                        {
                            return {cell:rotate[r][0], color:expectedNextPathColourToEdge};
                        }
                        break;

                    // C?N  ...
                    // ?C?  .N.
                    // ??P  ...

                    case 3:
                        if (view[rotate[r][0]].color === currentCellColour &&
                            view[rotate[r][2]].color === expectedNextPathColourToEdge &&
                            isCellTrailToQueen(view[rotate[r][8]],currentCellColour))
                        {
                            return {cell:4, color:expectedNextPathColourToEdge};
                        }
                        break;

                    // C??  N..
                    // ?C?  ...
                    // ??P  ...

                    case 4:
                        if (view[rotate[r][0]].color === currentCellColour &&
                            isCellTrailToQueen(view[rotate[r][8]],currentCellColour))
                        {
                            return {cell:rotate[r][0], color:expectedNextPathColourToEdge};
                        }
                        break;

                    /////// MOVING ///////

                    // C?P  ...
                    // ?C?  ...
                    // ??C  N..
                    case 5:
                        if (view[rotate[r][0]].color === currentCellColour &&
                            view[rotate[r][8]].color === currentCellColour &&
                            isCellTrailToQueen(view[rotate[r][2]],currentCellColour) &&
                            view[rotate[r][6]].color != expectedNextPathColourToEdge)
                        {

                            if (view[rotate[r][6]].ant === null)
                            {
                                return {cell:rotate[r][6], color:expectedNextPathColourToEdge};
                            }
                            else
                            {
                                return NO_OP;
                            }
                        }
                        break;

                    // C?P  ...
                    // ?C?  ...
                    // N?C  M..
                    case 6:
                        if (view[rotate[r][0]].color === currentCellColour &&
                            view[rotate[r][8]].color === currentCellColour &&
                            isCellTrailToQueen(view[rotate[r][2]],currentCellColour) &&
                            view[rotate[r][6]].color === expectedNextPathColourToEdge)
                        {
                            cellToMoveTo = rotate[r][6];
                        }
                        break;

                    // Special case. we need to first paint the cell prior to moving other wise will cause corruption
                    // C??  ...
                    // ?C?  ...
                    // C??  ..N
                    case 7:
                        if (view[rotate[r][0]].color === currentCellColour &&
                            view[rotate[r][6]].color === currentCellColour &&
                            view[rotate[r][8]].color != expectedNextPathColourToEdge)
                        {
                            if (view[rotate[r][8]].ant === null)
                            {
                                return {cell:rotate[r][8], color:expectedNextPathColourToEdge};
                            }
                            else
                            {
                                return NO_OP;
                            }
                        }
                        break;
                    // C??  ...
                    // ?C?  ...
                    // C?N  ..M
                    case 8:
                        if (view[rotate[r][0]].color === currentCellColour &&
                            view[rotate[r][6]].color === currentCellColour &&
                            view[rotate[r][8]].color === expectedNextPathColourToEdge)
                        {
                            cellToMoveTo = rotate[r][8];
                        }
                        break;
                    // C??  ..M
                    // ?C?  ...
                    // P?C  ...
                    case 9:
                        if (view[rotate[r][0]].color === currentCellColour &&
                            view[rotate[r][8]].color === currentCellColour &&
                            isCellTrailToQueen(view[rotate[r][6]],currentCellColour))
                        {
                            cellToMoveTo = rotate[r][2];
                        }
                        break;
                    // C??  ...
                    // ?C?  ...
                    // P??  ..M
                    case 10:
                        if (view[rotate[r][0]].color === currentCellColour &&
                            isCellTrailToQueen(view[rotate[r][6]],currentCellColour))
                        {
                            cellToMoveTo = rotate[r][8];
                        }
                        break;
                    // C??  ...
                    // ?C?  ...
                    // ???  M..
                    case 11:
                        if (view[rotate[r][0]].color === currentCellColour)

                        {
                            cellToMoveTo = rotate[r][6];
                        }
                        break;
                    // ???  ...
                    // ?C?  ...
                    // P??  ?.M
                    case 12:
                        if (isCellTrailToQueen(view[rotate[r][6]],currentCellColour))

                        {
                            cellToMoveTo = rotate[r][8];
                        }
                        break;
                    }
                    if (cellToMoveTo>-1)
                    {
                        return {cell:view[cellToMoveTo].ant === null?cellToMoveTo:4};
                    }
                }
            }
            return NO_OP;
        }

        // worker is transporting food
        else
        {
            // worker deadlock avoidance
            for (i=0; i<9; i+=2)
            {
                // does the cell have another ant in it?
                if (i!=4 && view[i].ant!=null && !(view[i].ant.type===ANT_TYPE_QUEEN && view[i].ant.friend))
                {
                    // attempt to pick an empty random trail-cell
                    for (i = randomInt(view,54321)%9;i>0;i--)
                    {
                        if (view[i].ant===null && view[i].food===0 && isTrailColour(view[i].color))
                        {
                            return {cell:i};
                        }
                    }

                    // no luck... just pick a random empty cell
                    for (i = randomInt(view,12345)%9;i>0;i--)
                    {
                        if (view[i].ant===null && view[i].food===0)
                        {
                            return {cell:i};
                        }
                    }

                    // deadlock unavoidable!
                    return NO_OP;
                }
            }

//            // this is to try and unstick stuck ants
//            if (randomInt(view,1)%20==1)
//            {
//                // attempt to pick an empty random trail-cell
//                for (i = randomInt(view,666)%9;i>0;i--)
//                {
//                    if (view[i].ant==null && view[i].food==0 && isTrailColour(view[i].color))
//                    {
//                        return new Move(i);
//                    }
//                }
//            }


            // decide move action
            for (var priority = 0; priority <14;priority++)
            {
                // repeat for each rotation
                var indecies = shuffleIndecies(view,random,4);
                for (var j=0;j<4;j++)
                {
                    var r = indecies[j];
                    var cellToMoveTo =-1;

                    switch(priority)
                    {
                        // PRE-EMPTIVE PATH PRUNING

                    // C?X  X..
                    // ?X?  ...
                    // P?X  ...
                    case 0:
                        if (isTrailColour(view[rotate[r][0]].color) &&
                            !isTrailColour(view[rotate[r][2]].color) &&
                            !isTrailColour(view[rotate[r][4]].color) && 
                            prevTrailColor(view[rotate[r][0]].color) === view[rotate[r][5]].color)
                        {
                            return {cell:rotate[r][0], color: 1};
                        }
                        break;


                        // ?C?  ...
                        // CXN  X..
                        // ?X?  ...
                        case 1:
                            if (!isTrailColour(view[rotate[r][4]].color) &&
                                !isTrailColour(view[rotate[r][7]].color) &&
                                isTrailColour(view[rotate[r][1]].color) && 
                                view[rotate[r][3]].color === view[rotate[r][1]].color &&
                                nextTrailColor(view[rotate[r][1]].color) === view[rotate[r][5]].color)
                            {
                                return {cell:rotate[r][3], color: 1};
                            }
                            break;

                        // ?C?  ...
                        // PXC  ..X
                        // ?X?  ...
                        case 2:
                            if (!isTrailColour(view[rotate[r][4]].color) &&
                                !isTrailColour(view[rotate[r][7]].color) &&
                                isTrailColour(view[rotate[r][1]].color) && 
                                view[rotate[r][5]].color === view[rotate[r][1]].color &&
                                prevTrailColor(view[rotate[r][1]].color) === view[rotate[r][3]].color)
                            {
                                return {cell:rotate[r][5], color: 1};
                            }
                            break;

                        // N??  ..N
                        // ?C?  ...
                        // C?C  ...

                        case 3:
                            if (isTrailColour(view[rotate[r][4]].color) &&
                                view[rotate[r][2]].color != expectedNextPathColourToEdge &&
                                view[rotate[r][8]].color === currentCellColour &&
                                view[rotate[r][0]].color === expectedNextPathColourToEdge &&
                                view[rotate[r][6]].color === currentCellColour)
                            {
                                return {cell:rotate[r][2], color:expectedNextPathColourToEdge};
                            }
                            break;

                        // N??  ...
                        // ?X?  .C.
                        // C?C  ...


                        case 4:
                            if (!isTrailColour(view[rotate[r][4]].color) && 
                                isTrailColour(view[rotate[r][8]].color) &&
                                view[rotate[r][0]].color === nextTrailColor(view[rotate[r][8]].color) &&
                                view[rotate[r][8]].color === view[rotate[r][6]].color)
                            {
                                return {cell:rotate[r][4], color:view[rotate[r][8]].color};
                            }
                            break;

                        // N??  ..C
                        // ?C?  ...
                        // C?P  ...

                        case 5:
                            if (isTrailColour(view[rotate[r][4]].color) && 
                                view[rotate[r][0]].color === expectedNextPathColourToEdge &&
                                view[rotate[r][6]].color === currentCellColour &&
                                isCellTrailToQueen(view[rotate[r][8]],currentCellColour) &&
                                view[rotate[r][2]].color != currentCellColour)
                            {
                                return {cell:rotate[r][2], color:currentCellColour};
                            }
                            break;

                        // C?N  ...
                        // ?X?  .N.
                        // ??P  ...

                        case 6:
                            if (!isTrailColour(view[rotate[r][4]].color) && 
                                isTrailColour(view[rotate[r][0]].color) && 
                                view[rotate[r][2]].color === nextTrailColor(view[rotate[r][0]].color) &&
                                view[rotate[r][8]].color === prevTrailColor(view[rotate[r][0]].color))
                            {
                                return {cell:rotate[r][4], color:nextTrailColor(view[rotate[r][0]].color)};
                            }
                            break;

                        // ??P  ...
                        // ?X?  .N.
                        // ??N  ...

                        case 7:
                            if (!isTrailColour(view[rotate[r][4]].color) && 
                                isTrailColour(view[rotate[r][2]].color) &&
                                nextTrailColor(view[rotate[r][2]].color) === view[rotate[r][8]].color)
                            {
                                return {cell:4, color:view[rotate[r][8]].color};
                            }
                            break;



                        // if we are on the corner of a trail band... move opposite to the apex
                        case 8:
                            if (isTrailColour(currentCellColour) &&
                                view[rotate[r][0]].color === currentCellColour &&
                                view[rotate[r][2]].color === currentCellColour)
                            {
                                if (randomInt(view,currentCellColour)%2 === 0)
                                {
                                    cellToMoveTo = rotate[r][0];
                                }
                                else
                                {
                                    cellToMoveTo = rotate[r][2];
                                }
                            }
                            break;

                        // if we on the opposite corner of a trail band... move back toward the apex
                        case 9:
                            if (isTrailColour(currentCellColour) &&
                                isCellTrailToQueen(view[rotate[r][0]],currentCellColour) &&
                                isCellTrailToQueen(view[rotate[r][2]],currentCellColour))
                            {
                                if (randomInt(view,currentCellColour)%2 === 0)
                                {
                                    cellToMoveTo = rotate[r][0];
                                }
                                else
                                {
                                    cellToMoveTo = rotate[r][2];
                                }
                            }
                            break;
                        // if an adjacent cell is a trail to the queen, move that way
                        case 10:
                            if (isTrailColour(currentCellColour) &&
                                isCellTrailToQueen(view[rotate[r][0]],currentCellColour))
                            {
                                cellToMoveTo = rotate[r][0];
                            }
                            break;
                        // if we are not on a trail and an adjacent cell is a trail, then move that way
                        case 11:
                            if (!isTrailColour(currentCellColour) && isTrailColour(view[rotate[r][8]].color))
                            {
                                cellToMoveTo = rotate[r][8];
                            }
                            break;
                        // are we on a cell between trail cells? if so then move onto a cell on the trail.
                        case 12:
                            if (isTrailColour(view[rotate[r][1]].color))
                            {
                                cellToMoveTo = rotate[r][1];
                            }
                            break;
                        // are we on a terminal trail cell? if so then move onto a cell on the trail.
                        case 13:
                            if (isTrailColour(view[rotate[r][0]].color))
                            {
                                cellToMoveTo = rotate[r][0];
                            }
                            break;

                    }
                    if (cellToMoveTo>-1)
                    {
                        if (view[cellToMoveTo].ant != null || view[cellToMoveTo].food > 0) continue;
                        return {cell:cellToMoveTo};
                    }
                }
            }
            return NO_OP;
        }

    }

    return getAction(view);

ВЕРСІЯ 1

версія1

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

Там назва запису - від ворога "світлячка" в класичній головоломці BolderDash, повторюваний три кольоровий візерунок нагадує ворога.

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

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

ВЕРСІЯ 2

версія 2

Ця версія є кращою, тепер вона обертається на 45 градусів, що дозволяє проводити більше розвідки на кожен крок робітника.

Він також зараз збирає їжу (якщо вона не відволікається)

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

Однак, залишившись один, він в середньому складає 700 продуктів харчування, зібраних з 300 працівниками.

ВЕРСІЯ 2.1

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

ВЕРСІЯ 2.1.1

виправлено виправлення в 2.1 (я фактично не використовував поле "їжа" для мурашиного виду, і тому посилався на нульовий об'єкт.

ВЕРСІЯ 2.1.1.1

/ мене ховає голова в руки

Я знайшов просту помилку в індексації в нерестовій функції, яка означала, що королева може спробувати нереститися над собою ... зайве говорити, що це підстава для дискваліфікації. тепер виправлено.

ВЕРСІЯ 2.2

Виправлена ​​помилка вставлення копії, яка знову ввела незаконний помилку нересту

ВЕРСІЯ 2.2.1

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


Ваша стратегія також страждає від тієї ж слабкості, що і Зігурат: крадіжка. Я міг налаштувати ідентифікаційний код Zigurrat у вампірі, щоб націлити Firefly приблизно за 90 секунд. Буквально це було б "скопіювати 60 рядків коду, вставити 60 рядків коду, змінити 3 числа". Хоча я вважаю за краще зробити код Zigurrat-ID модульним ("зробіть це з цими кольорами").
Draco18s

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

@ Draco18s Так, у цієї початкової версії є кілька недоліків :-), але це місце з чого почати. Щодо загрози крадіжки: я планую зробити мурашник-охоронець, який шукатиме королеву злодія ворога та "вкраде" назад. Це повинно мінімізувати втрати.
Moogie

О, я розумію, що це місце для початку, просто роблячи деякі спостереження. :)
Draco18s

1
Це зроблено? Чи можу я зайти і розбити місце вампіровою битою? : D
Draco18s

4

Мандельбрант

Усі мої відповіді містять подібну логіку низького рівня у формі Рамки форматичних функцій.

"ЛОГІЧНІ ПОЧАТКИ ВИСОКОГО РОЗВИТКУ ТУТ" позначає кінець коду Рамки.

ПОПЕРЕДЖЕННЯ: Цей запис є переважно демонстрацією того, що можливо в наборі правил Формічні функції. Це не було ретельно перевірено на не порожній карті. Хоча він пережив дві цілі ігри, я не сподіваюся, що він буде довго залишатися кваліфікованим.

// FORMIC FRAMEWORK \\
//  Version 7.0.4   \\
const QUEEN = 5;
const HERE = view[4];
const ME = HERE.ant;
const ORTHOGONALS = [1, 3, 5, 7];
const DIAGONALS = [0, 2, 6, 8];
const DIAGONALS_ORTHOGONALS = [0, 2, 6, 8, 1, 3, 5, 7];
const DIRECTIONS = [0, 1, 2, 3, 5, 6, 7, 8];
const CLOCKWISE_DIRECTIONS = [0, 1, 2, 5, 8, 7, 6, 3];
const ROTATIONS = [
    [0, 1, 2,
     3, 4, 5,
     6, 7, 8],

    [6, 3, 0,
     7, 4, 1,
     8, 5, 2],

    [8, 7, 6,
     5, 4, 3,
     2, 1, 0],

    [2, 5, 8,
     1, 4, 7,
     0, 3, 6]
];
const NEIGHBORS = [
    [1, 4, 3],
    [2, 5, 4, 3, 0],
    [5, 4, 1],
    [0, 1, 4, 7, 6],
    [0, 1, 2, 5, 8, 7, 6, 3],
    [8, 7, 4, 1, 2],
    [3, 4, 7],
    [6, 3, 4, 5, 8],
    [7, 4, 5]
];
const HORIZONTAL_FLIP = [2, 1, 0, 5, 4, 3, 8, 7, 6];
const VERTICAL_FLIP = [6, 7, 8, 3, 4, 5, 0, 1, 2];

const DEBUG_MODE = false;
const log = DEBUG_MODE ? console.log : () => { };

function cells(...indices) {
    return indices.map(i => view[i]);
}
function colors(...indices) {
    return cells(...indices).map(c => c.color);
}
function ants(...indices) {
    return cells(...indices).map(c => c.ant);
}

function isColor(color, index) {
    return view[index].color === color;
}
function isAnyColor(colors, index) {
    return colors.includes(view[index].color);
}
function hasFood(index) {
    return view[index].food === 1;
}
function hasAnt(qualifies, index) {
    const a = view[index].ant;
    return a && (!(qualifies instanceof Function) || qualifies(a));
}
function hasFriend(type, index) {
    return hasAnt(a => a.friend && (!type || a.type === type), index);
}
const hasAnyFriend = bind(hasFriend, null);
function bind(f, ...args) {
    return f.bind(null, ...args);
}

function noTransform() {
    return { revert() { }, detransformAction() { } };
}
function indexTransform(indices) {
    const revertedIndices = new Array(9);
    for (let i = 0; i < 9; ++i) {
        revertedIndices[indices[i]] = i;
    }

    view = indices.map(index => view[index]);

    return { revert() { view = revertedIndices.map(index => view[index]); }, detransformAction(action) { action.cell = indices[action.cell]; } };
}

const rotationTransformers = [noTransform, ...ROTATIONS.slice(1).map(r => bind(indexTransform, r))];

function bestTransformers(transformers, scorer) {
    let bestScore = 0;
    const bestIndices = [];
    const bestTransformers = [];
    for (let i = 0; i < transformers.length; ++i) {
        const t = transformers[i];
        const {revert} = t();
        const score = scorer();
        revert();
        if (score > bestScore) {
            bestScore = score;
            bestIndices.length = 0;
            bestTransformers.length = 0;
        }
        if (score >= bestScore) {
            bestIndices.push(i);
            bestTransformers.push(t);
        }
    }

    return {score: bestScore, indices: bestIndices, transformers: bestTransformers};
}
function* withBestTransformation(transformers, scorer, continuation) {
    const best = bestTransformers(transformers, scorer);
    if (best.score > 0) {
        const {revert, detransformAction} = best.transformers[0]();
        for (const output of continuation(best)) {
            if (isAction(output)) {
                detransformAction(output);
            }
            yield output;
        }
        revert();
    }
}
const withBestRotation = bind(withBestTransformation, rotationTransformers);

const wait = {cell: 4};
function move(index) {
    return index >= 0 && index < 9 && view[index].ant === null && (view[index].food === 0 || ME.food === 0 || ME.type === QUEEN) ? { cell: index } : null;
}
function moveMany(...indices) {
    return indices.map(move);
}
function paint(color, index) {
    return index >= 0 && index < 9 && color >= 1 && color <= 8 && view[index].color !== color ? { cell: index, color } : null;
}
function paintMany(colors, ...indices) {
    return pairMap(indices, colors, paint);
}
function spawn(type, index) {
    return index >= 0 && index < 9 && view[index].ant === null && view[index].food === 0 && ME.food > 0 && ME.type === QUEEN && type >= 1 && type <= 4 ? { cell: index, type } : null;
}
function spawnMany(types, ...indices) {
    return pairMap(indices, types, spawn);
}
function pairMap(mainArr, sideArr, func) {
    return mainArr.map((v, i) => func(sideArr[i % sideArr.length], v));
}

function isAction(value) {
    return value instanceof Object && value.cell !== undefined; // TODO: Make this more strict.
}

log('=== start logic ===');
for (const output of main()) {
    if (isAction(output)) {
        log('=== end logic ===');
        return output;
    }
}

throw 'Decision was omitted.';

function* main() {
    // HIGH-LEVEL LOGIC STARTS HERE \\

    // TARGET SIZE:  2^21 pixels -- SUPPORTED
    // STRETCH GOAL: 2497 x 996
    // MAX POSSIBLE: 2500 x 1000

    // How long the painting triplet will go on for until they begin returning to the shifting station.
    // This value should not exceed 997 for the painter to work in all cases, or 2497 if you don't care about being positioned vertically.
    // It also shouldn't be too low. The exact lowest value is unclear, but it's likely to be in the teens.
    const LENGTH = 6 * 11;

    // Which function will be used for painting in the pixels.
    const getPictureColorAt = mandelbrot;

    function notReallyRainbow(index) {
        return index % 6 + 2;
    }
    function fromColorString(index) {
        // Input your own color string ({ a, b, c, d, e, f, g, h } => { 8, 7, 6, 4, 5, 3, 2, 1 }).
        const colorString = '';

        return [8, 7, 6, 4, 5, 3, 2, 1][colorString.charCodeAt(index % colorString.length) - 'a'.charCodeAt(0)];
    }
    function mandelbrot(index) {
        const ESCAPE = 2 ** 2, MAX_I = 8 * 10 - 1;
        const x0 = (index % LENGTH) / (LENGTH - 1) * 3 - 2, y0 = Math.floor(index / LENGTH) / (Math.floor(LENGTH * 2 / 3) - 1) * 2 - 1;
        let x = 0, y = 0;
        for (let i = 0; i < MAX_I; ++i) {
            [x, y] = [x * x - y * y + x0, 2 * x * y + y0];
            if (x * x + y * y > ESCAPE) {
                return (i + 1) % 8 + 1;
            }
        }
        return 8;
    }

    // WARNING! Beyond likely lies awful code.
    // There are no more tunable parameters.
    // Continue reading at your own risk.

    const L1_OVERFLOW = 4096;
    const FILL_ORDER_INDEX = [1, 2, 3, 6];
    const FILL_ORDER_DIGIT = [0, 1, 2, 4];

    function parseNumber(...indices) {
        return indices.reduceRight((a, index) => (a << 3) + (index !== -1 ? view[index].color - 1 : 0), 0);
    }

    function colorAtDigit(n, d) {
        return ((n >>> (d * 3)) & 7) + 1;
    }

    function paintPictureFragment(number) {
        log(`initialized painter with ${number}`);
        return paint(getPictureColorAt(number), 0);
    }

    const COPIER = 1;
    const COUNTER = 2;
    const MAJOR = 3;
    const MINOR = 4;

    function* moveWait(index) {
        yield move(index);
        yield wait;
    }

    log(`type: ${ME.type}`);
    switch (ME.type) {
        case COPIER: {
            yield* withBestRotation(() => Math.max(hasFriend(QUEEN, 7) + hasFriend(MINOR, 3), hasFriend(QUEEN, 6) + hasFriend(MINOR, 0)) - 1, bind(moveWait, 5));
            yield* withBestRotation(bind(hasFriend, COUNTER, 7), function*() {
                if ([6, 3].findIndex(hasAnyFriend) === -1) {
                    yield paint(8, 3);
                    yield move(3);
                }
                yield wait;
            });
            yield* withBestRotation(bind(hasFriend, COUNTER, 8), function*() {
                const targetIndex = FILL_ORDER_INDEX[view[4].color - 3];
                yield paint(view[5].color, targetIndex);
                yield wait;
            });
            yield wait;
        }

        case COUNTER: {
            yield* withBestRotation(() => hasFriend(COPIER, 2) + hasFriend(MAJOR, 0) - 1, bind(moveWait, 5));
            yield* withBestRotation(bind(hasFriend, COPIER, 1), function*() {
                if (hasFriend(MAJOR, 0)) {
                    yield paint(view[6].color, 8);
                }
                yield wait;
            });
            yield* withBestRotation(bind(hasFriend, COPIER, 0), function*() {
                if (!hasAnyFriend(6)) {
                    const progress = view[0].color;
                    if (progress === 8) {
                        const number = parseNumber(8, 7, 4, 1) + 1;
                        yield* paintMany([1, 2, 3].map(bind(colorAtDigit, number)), 3, 5, 2);
                        yield paint(7, 0);
                    } else if (progress === 7) {
                        const number = parseNumber(8) + 1;
                        yield paint(colorAtDigit(number, 0), 4);
                        yield paint(6, 0);
                    } else {
                        const number = parseNumber(...progress === 6 ? [4, 3] : [7, 8], 5, 2) * LENGTH;
                        if (progress > 2) {
                            if (progress === 6) {
                                yield* paintMany(colors(4, 3), 7, 8);
                            } else if (progress === 5) {
                                yield* paintMany([5, 6].map(bind(colorAtDigit, number)), 3, 4);
                            }
                            yield paint(colorAtDigit(number, FILL_ORDER_DIGIT[progress - 3]), 1);
                            if (progress !== 3) {
                                yield paint(progress - 1, 0);
                            } else {
                                yield paint(number + 1 === L1_OVERFLOW ? 2 : 1, 0);
                            }
                        } else {
                            yield paint(colorAtDigit(number, 3), 1);
                            yield wait;
                        }
                    }
                }
            });
            yield wait;
        }

        case MAJOR: {
            yield* withBestRotation(bind(hasFriend, COPIER, 5), bind(moveWait, 6));
            yield* withBestRotation(() => hasFriend(QUEEN, 7) + hasFriend(MINOR, 1) - 1, bind(moveWait, 5));
            yield* withBestRotation(bind(hasFriend, QUEEN, 2), bind(moveWait, 1));
            yield* withBestRotation(bind(hasFriend, MINOR, 2), function*() {
                const number = parseNumber(-1, -1, -1, -1, 3, 4, 5) + (isColor(2, 1) ? L1_OVERFLOW : 0);
                yield* paintMany([4, 5, 6].map(bind(colorAtDigit, number)), 6, 7, 8);
                yield move(7);
            });
            yield wait;
        }

        case MINOR: {
            yield* withBestRotation(() => hasFriend(COPIER, 8) + hasFriend(QUEEN, 6) - hasFriend(MAJOR, 3) - 1, bind(moveWait, 7));
            yield* withBestRotation(() => hasFriend(MAJOR, 6) + hasFriend(QUEEN, 7) - 1, bind(moveWait, 3));
            yield* withBestRotation(() => hasFriend(MAJOR, 8) - hasFriend(QUEEN, 7), bind(moveWait, 5));
            yield* withBestRotation(() => hasFriend(MAJOR, 7) + hasFriend(QUEEN, 5) - 1, bind(moveWait, 1));
            yield* withBestRotation(bind(hasFriend, QUEEN, 6), function*() {
                if (hasFriend(MAJOR, 3)) {
                    yield wait;
                }
                const number = parseNumber(0, 1) + 1;
                yield* paintMany([0, 1].map(bind(colorAtDigit, number)), 3, 4);
                yield move(7);
            });
            yield wait;
        }

        case QUEEN: {
            if (DIRECTIONS.some(hasAnyFriend)) {
                yield* withBestRotation(bind(hasFriend, COPIER, 5), function*() {
                    if (!hasFriend(COUNTER, 7)) {
                        yield spawn(COUNTER, 8);
                    }
                    if (!hasFriend(MAJOR, 3) && !hasFriend(MAJOR, 0)) {
                        yield spawn(MAJOR, 6);
                    }
                    yield spawn(MINOR, 0);
                    if (!hasFriend(MAJOR, 0) && hasFriend(MAJOR, 3)) {
                        yield move(7);
                    }
                    yield wait;
                });

                yield* withBestRotation(bind(hasFriend, MAJOR, 2), function*() {
                    if (!hasFriend(MINOR, 8)) {
                        yield move(1);
                    }
                    yield wait;
                });

                yield* withBestRotation(bind(hasFriend, MINOR, 0), function*() {
                    if (hasFriend(MAJOR, 3)) {
                        yield move(1);
                        yield wait;
                    } else if (hasFriend(MAJOR, 6)) {
                        yield wait;
                    }
                });

                yield* withBestRotation(bind(hasFriend, MAJOR, 7), function*() {
                    if (!hasFriend(COPIER, 6)) {
                        yield paintPictureFragment(parseNumber(1, 2, 3, 5, 6, 7, 8));
                    }
                    yield wait;
                });

                yield* withBestRotation(bind(hasFriend, MINOR, 5), function*() {
                    if (isColor(3, 4)) {
                        const number = parseNumber(1, 2, 3, 5) + 1;
                        yield* paintMany([colorAtDigit(number, 2), number + 1 === L1_OVERFLOW ? 2 : 1, colorAtDigit(number, 3)], 6, 7, 8);
                        yield move(7);
                        yield wait;
                    } else {
                        const number = parseNumber(1, 2, 3, 5, 6, 7, 8);
                        yield paintPictureFragment(number);
                        if ((number + 1) % LENGTH === 0) {
                            yield move(8);
                            yield wait;
                        }
                        yield paint(3, 4);
                    }

                    throw 'illogical failure 1';
                });

                throw 'illogical failure 2';
            }

            yield* moveMany(...DIAGONALS_ORTHOGONALS.filter(hasFood));

            if (ME.food >= 4) {
                yield* spawnMany([COPIER], ...ORTHOGONALS);
            }

            yield* moveMany(...DIAGONALS_ORTHOGONALS.filter(bind(isColor, 1)), ...DIAGONALS_ORTHOGONALS); // TODO: Watch out for accidental entrapment.
            yield wait;
        }
    }
}

Галерея

Мандельброт повний Мандельброт крихітний


Пояснення

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

Фаза 1

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

2 фаза

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

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

3 мурашки (королева та 2 робітники) йдуть у петлю для малювання. Вони несуть 21-бітове (7 комірок * 3 біта на комірку) ціле число у вигляді кольорів з ними, які можна використовувати для індексації до будь-якого потрібного зображення. За замовчуванням це зображення є набором Мандельброта. Додатково, лівий верхній осередок зарезервований для малювання пікселя, а центральна комірка зарезервована для речей, що не входять у рамки цього пояснення. Триплет не потребує кольорів, щоб керувати ними, оскільки вони визначають орієнтацію, знаходячи один одного. Кожен цикл вони зміщують ціле число на 1 клітинку вниз, переконуючись, що збільшують його на 1 кожен раз, коли вони це роблять. Цикл закінчується, коли доходить кінець намальованої лінії, яка налаштована символомLENGTHпостійний. У цей момент починається скоординований танець з 3 мурахами, який призводить до того, що тріо закінчується незграбною конфігурацією. Королева також переміщується вправо на одну клітинку під час танцю. Вони подорожують вгору, поки не зустрінуться з парою мурах, що чекає на них.

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

Цикл завершується, коли три мурахи повертаються до пари і знову починають виконувати 5-мурашиний танець.


Цей запис містить параметри. Наразі налаштовано малювати крихітний набір Мандельброта. Ви можете налаштувати LENGTHзображення, поміняти функцію малювання зображення або навіть прокатати свою. Веселіться!

Рекомендований контролер: dzaima's .


Журнал змін

Версія 1.0

  • початковий випуск

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

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

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

1
Чому? ЧОМУ? Навіщо ти це робив? Крікі, на цей виклик були відповіді, коли я був на трьох різних роботах.
Draco18s

1
@ Draco18s Я просто дуже люблю цей виклик: P
Аліон

3

Одинокий вовк

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

// == Shared low-level helpers for all solutions ==

var QUEEN = 5;

var WHITE = 1;
var COL_MIN = WHITE;
var COL_LIM = 9;

var CENTRE = 4;

var NOP = {cell: CENTRE};

var DIR_FORWARDS = false;
var DIR_REVERSE = true;
var SIDE_RIGHT = true;
var SIDE_LEFT = false;

function sanity_check(movement) {
  var me = view[CENTRE].ant;
  if(!movement || movement.cell < 0 || movement.cell > 8) {
    return false;
  }
  if(movement.type) {
    if(movement.color) {
      return false;
    }
    if(movement.type < 1 || movement.type > 4) {
      return false;
    }
    if(view[movement.cell].ant || view[movement.cell].food) {
      return false;
    }
    if(me.type !== QUEEN || me.food < 1) {
      return false;
    }
    return true;
  }
  if(movement.color) {
    if(movement.color < COL_MIN || movement.color >= COL_LIM) {
      return false;
    }
    if(view[movement.cell].color === movement.color) {
      return false;
    }
    return true;
  }
  if(view[movement.cell].ant) {
    return false;
  }
  if(view[movement.cell].food + me.food > 1 && me.type !== QUEEN) {
    return false;
  }
  return true;
}

function as_array(o) {
  if(Array.isArray(o)) {
    return o;
  }
  return [o];
}

function best_of(movements) {
  var m;
  for(var i = 0; i < movements.length; ++ i) {
    if(typeof(movements[i]) === 'function') {
      m = movements[i]();
    } else {
      m = movements[i];
    }
    if(sanity_check(m)) {
      return m;
    }
  }
  return null;
}

function play_safe(movement) {
  // Avoid disqualification: no-op if moves are invalid
  return best_of(as_array(movement)) || NOP;
}

var RAND_SEED = (() => {
  var s = 0;
  for(var i = 0; i < 9; ++ i) {
    s += view[i].color * (i + 1);
    s += view[i].ant ? i * i : 0;
    s += view[i].food ? i * i * i : 0;
  }
  return s % 29;
})();

var ROTATIONS = [
  [0, 1, 2, 3, 4, 5, 6, 7, 8],
  [6, 3, 0, 7, 4, 1, 8, 5, 2],
  [8, 7, 6, 5, 4, 3, 2, 1, 0],
  [2, 5, 8, 1, 4, 7, 0, 3, 6],
];

function try_all(fns, limit, wrapperFn, checkFn) {
  var m;
  fns = as_array(fns);
  for(var i = 0; i < fns.length; ++ i) {
    if(typeof(fns[i]) !== 'function') {
      if(checkFn(m = fns[i])) {
        return m;
      }
      continue;
    }
    for(var j = 0; j < limit; ++ j) {
      if(checkFn(m = wrapperFn(fns[i], j))) {
        return m;
      }
    }
  }
  return null;
}

function identify_rotation(testFns) {
  // testFns MUST be functions, not constants
  return try_all(
    testFns,
    4,
    (fn, r) => fn(ROTATIONS[r]) ? ROTATIONS[r] : null,
    (r) => r
  );
}

function near(a, b) {
  return (
    Math.abs(a % 3 - b % 3) < 2 &&
    Math.abs(Math.floor(a / 3) - Math.floor(b / 3)) < 2
  );
}

function try_all_angles(solverFns) {
  return try_all(
    solverFns,
    4,
    (fn, r) => fn(ROTATIONS[r]),
    sanity_check
  );
}

function try_all_cells(solverFns, skipCentre) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i === CENTRE && skipCentre) ? null : fn(i)),
    sanity_check
  );
}

function try_all_cells_near(p, solverFns) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i !== p && near(p, i)) ? fn(i) : null),
    sanity_check
  );
}

function ant_type_at(i, friend) {
  return (view[i].ant && view[i].ant.friend === friend) ? view[i].ant.type : 0;
}

function friend_at(i) {
  return ant_type_at(i, true);
}

function foe_at(i) {
  return ant_type_at(i, false);
}

function foe_near(p) {
  for(var i = 0; i < 9; ++ i) {
    if(foe_at(i) && near(i, p)) {
      return true;
    }
  }
  return false;
}

function move_agent(agents) {
  var me = view[CENTRE].ant;
  var buddies = [0, 0, 0, 0, 0, 0];
  for(var i = 0; i < 9; ++ i) {
    ++ buddies[friend_at(i)];
  }

  for(var i = 0; i < agents.length; i += 2) {
    if(agents[i] === me.type) {
      return agents[i+1](me, buddies);
    }
  }
  return null;
}

function grab_nearby_food() {
  return try_all_cells((i) => (view[i].food ? {cell: i} : null), true);
}

function go_anywhere() {
  return try_all_cells((i) => ({cell: i}), true);
}

function colours_excluding(cols) {
  var r = [];
  for(var i = COL_MIN; i < COL_LIM; ++ i) {
    if(cols.indexOf(i) === -1) {
      r.push(i);
    }
  }
  return r;
}

function generate_band(start, width) {
  var r = [];
  for(var i = 0; i < width; ++ i) {
    r.push(start + i);
  }
  return r;
}

function colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function(c) {
      return colours[(colours.indexOf(c) + 1) % colours.length];
    }
  };
}

function random_colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function() {
      return colours[RAND_SEED % colours.length];
    }
  };
}

function fast_diagonal(colourBand) {
  var m = try_all_angles([
    // Avoid nearby checked areas
    (rot) => {
      if(
        !colourBand.contains(view[rot[0]].color) &&
        colourBand.contains(view[rot[5]].color) &&
        colourBand.contains(view[rot[7]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // Go in a straight diagonal line if possible
    (rot) => {
      if(
        !colourBand.contains(view[rot[0]].color) &&
        colourBand.contains(view[rot[8]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // When in doubt, pick randomly but avoid doubling-back
    (rot) => (colourBand.contains(view[rot[0]].color) ? null : {cell: rot[0]}),

    // Double-back when absolutely necessary
    (rot) => ({cell: rot[0]})
  ]);

  // Lay a colour track so that we can avoid doubling-back
  // (and mess up our foes as much as possible)
  if(!colourBand.contains(view[CENTRE].color)) {
    var prevCol = m ? view[8-m.cell].color : WHITE;
    return {cell: CENTRE, color: colourBand.next(prevCol)};
  }

  return m;
}

function follow_edge(obstacleFn, side) {
  // Since we don't know which direction we came from, this can cause us to get
  // stuck on islands, but the random orientation helps to ensure we don't get
  // stuck forever.

  var order = ((side === SIDE_LEFT)
    ? [0, 3, 6, 7, 8, 5, 2, 1, 0]
    : [0, 1, 2, 5, 8, 7, 6, 3, 0]
  );
  return try_all(
    [obstacleFn],
    order.length - 1,
    (fn, i) => (fn(order[i+1]) && !fn(order[i])) ? {cell: order[i]} : null,
    sanity_check
  );
}

function start_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => ((
      !protectedCols.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 2 : 0]].color) &&
      !colourBand.contains(view[rot[1]].color)
    )
      ? {cell: rot[right ? 5 : 3], color: colourBand.next(WHITE)}
      : null)
  ]);
}

function lay_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => {
      var ahead = rot[right ? 2 : 0];
      var behind = rot[right ? 8 : 6];
      if(
        colourBand.contains(view[behind].color) &&
        !protectedCols.contains(view[ahead].color) &&
        !colourBand.contains(view[ahead].color) &&
        !colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        return {cell: ahead, color: colourBand.next(view[behind].color)};
      }
    }
  ]);
}

function follow_dotted_path(colourBand, side, direction) {
  var forwards = (direction === DIR_REVERSE) ? 7 : 1;
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    // Cell on our side? advance
    (rot) => {
      if(
        colourBand.contains(view[rot[right ? 5 : 3]].color) &&
        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[0]].color) &&
        !colourBand.contains(view[rot[2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    },

    // Cell ahead and behind? advance
    (rot) => {
      var passedCol = view[rot[right ? 8 : 6]].color;
      var nextCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(passedCol) &&
        nextCol === colourBand.next(passedCol) &&

        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[right ? 0 : 2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    }
  ]);
}

function escape_dotted_path(colourBand, side, newColourBand) {
  var right = (side === SIDE_RIGHT);
  if(!newColourBand) {
    newColourBand = colourBand;
  }

  return try_all_angles([
    // Escape from beside the line
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        !colourBand.contains(view[rot[right ? 8 : 6]].color) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[rot[7]].color) ||
        colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        // not oriented, or in a corner
        return null;
      }
      return best_of([
        {cell: rot[right ? 0 : 2], color: newColourBand.next(approachingCol)},
        {cell: rot[right ? 3 : 5]},
        {cell: rot[right ? 0 : 2]},
        {cell: rot[right ? 6 : 8]},
        {cell: rot[right ? 2 : 0]},
        {cell: rot[right ? 8 : 6]},
        {cell: rot[right ? 5 : 3]}
      ]);
    },

    // Escape from inside the line
    (rot) => {
      if(
        !colourBand.contains(view[rot[7]].color) ||
        !colourBand.contains(view[rot[1]].color) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      return best_of([
        {cell: rot[3]},
        {cell: rot[5]},
        {cell: rot[0]},
        {cell: rot[2]},
        {cell: rot[6]},
        {cell: rot[8]}
      ]);
    }
  ]);
}

function latch_to_dotted_path(colourBand, side) {
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(approachingCol) &&
        view[rot[right ? 8 : 6]].color === colourBand.next(approachingCol) &&
        !colourBand.contains(view[rot[right ? 5 : 3]].color)
      ) {
        // We're on the wrong side; go inside the line
        return {cell: rot[right ? 5 : 3]};
      }
    },

    // Inside the line? pick a side
    (rot) => {
      var passedCol = view[rot[7]].color;
      var approachingCol = view[rot[1]].color;
      if(
        !colourBand.contains(passedCol) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      if((approachingCol === colourBand.next(passedCol)) === right) {
        return best_of([{cell: rot[3]}, {cell: rot[6]}, {cell: rot[0]}]);
      } else {
        return best_of([{cell: rot[5]}, {cell: rot[2]}, {cell: rot[8]}]);
      }
    }
  ]);
}


// == High-level logic begins here ==


var COLOURS = random_colour_band(colours_excluding([1]));
return play_safe([
  grab_nearby_food,
  fast_diagonal.bind(null, COLOURS),
  go_anywhere,
  {cell: 1, color: COLOURS.next()}
]);

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

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

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

Я обіцяю зараз припинити спам на цю проблему з відповідями ...


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


Це не спам, коли всі вони чіткі та цікаві стратегії!
трихоплакс

Який запис є диверсантом Чорної Діри?
Draco18s

@ Draco18s не впевнений, що це таке, але він швидко малює діагональні сині лінії, які, здається, змушують Чорну Діру перестати нерестувати робітників раніше, ніж вона хоче, і змушує її стати дуже подовженою, завдяки чому всі робітники вічно беруться, щоб дістатися куди завгодно. Є також ракета з білою фарбою, яка заподіює горе всім, і це справді може побігти, якщо потрапить на чорну діру.
Дейв

@Dave Бот, який це робить, - Antdom Walking Artist
pppery

3

Пірс

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

var ORTHOGONALS = [1,3,5,7];
var CORNERS = [0,2,6,8];
var CENTER = 4;

var QUEEN = 5;

var no_op = {cell:CENTER};
var me = view[4].ant;

var ants;
var food;
var friendlies;
var unfriendlies;
var colors;

var j = 0;
var i = 0;
var cell = 0;
var rotation;

var out;

init_arrays();
var seed = rSeed();

var response;

var adjacents_all = {0:[1,3,4],1:[0,2,3,4,5],2:[1,4,5],3:[0,1,4,6,7],4:[0,1,2,3,4,5,6,7,8],5:[1,2,4,7,8],6:[3,4,7],7:[3,4,5,6,8],8:[4,5,7]};
var adjacents_ortho = {0:[1,3],1:[0,2,4],2:[1,5],3:[0,4,6],4:[1,3,5,7],5:[2,4,8],6:[3,7],7:[4,6,8],8:[5,7]};
var adjacents_diag = {0:[4],1:[3,5],2:[4],3:[1,7],4:[0,2,6,8],5:[1,7],6:[4],7:[3,5],8:[4]};

function valid_move(move) {
  if(!move || move.cell == undefined || move.cell < 0 || move.cell > 8) {return false;}
  if(move.type) {
    if(move.color) {return false;}
    if(move.type < 1 || move.type > 4) {return false;}
    if(view[move.cell].ant || view[move.cell].food) {return false;}
    if(me.type != QUEEN || me.food < 1) {return false;}
    return true;
  }
  if(move.color) {
    if(move.color < 1 || move.color > 8) {return false;}
    return true;
  }
  if(view[move.cell].ant){return false;}
  if(view[move.cell].food && me.food&& me.type != 5) {return false;}
  return true;
}

function steal_then_road(){
  if(count(unfriendlies, 5)>=1 && me.food==0){
    //steal from a queen with more than 30 food
    if(ants[unfriendlies.indexOf(5)].food>30){
      for(i=0;i<adjacents_ortho[unfriendlies.indexOf(5)].length;i++){
        if(ants[adjacents_ortho[unfriendlies.indexOf(5)][i]]==null){
          return {cell:adjacents_ortho[unfriendlies.indexOf(5)][i]};
        }
      }
    }
  }
  return road();
}

function try_corners(){
  for(i=0;i<4;i++){
    if(view[CORNERS[i]].ant==null){
      return {cell:CORNERS[i]};
    }
  }
}

function try_ortho(){
  for(i=0;i<4;i++){
    if(view[ORTHOGONALS[i]].ant==null){
      return {cell:ORTHOGONALS[i]};
    }
  }
}

function corner_then_ortho(){
  if(try_corners()){return try_corners();}
  if(try_ortho()){return try_ortho();}
  return {cell:4}; //PANIC!
}

function ortho_then_corner(){
  if(try_ortho()){return try_ortho();}
  if(try_corners()){return try_corners();}
  return {cell:4}; //PANIC!
}

function road(color){
  if (colors[color] != color) {
      return {cell:CENTER,color:color};
    }
  for (i = 0; i < 9; i++) {
    if (colors[i] == color && ants[8 - i] == null && i != color) {
      return {cell:8-i};
    }
  }
}

function color_self(color){
  return {cell:4,color:color};
}

function make_valid_move(move){
  if(valid_move(move)){return move;}
  return no_op;
  //return{cell:seed%9,color:seed%7+1};
}

function count(array, element) {
  out = 0;
  for (j = 0; j < array.length; j++) {
    if (array[j] == element) {
      out++;
    }
  }
  return out;
}

function target_ant(ant_type, location) {
  for (i = 0; i < 4; i++) {
    if (ants[location] != null) {
      if (ants[location].type == ant_type) {
        return i;
      }
    }
    ants = rot_left(ants);
    friendlies = rot_left(friendlies);
    unfriendlies = rot_left(unfriendlies);
    food = rot_left(food);
    colors = rot_left(colors);
  }
}


function target_color(color, location) {
  for (i = 0; i < 4; i++) {
    if (colors[location] != null) {
      if (colors[location].type == color) {
        return i;
      }
    }
    ants = rot_left(ants);
    friendlies = rot_left(friendlies);
    unfriendlies = rot_left(unfriendlies);
    food = rot_left(food);
    colors = rot_left(colors);
  }
}

function init_arrays() {
    ants = new Array(9);
  for (cell = 0; cell < 9; cell++) {ants[cell] = view[cell].ant;}


  food = new Array(9);
  for (cell = 0; cell < 9; cell++) {food[cell] = view[cell].food;}

  colors = new Array(9);
  for (cell = 0; cell < 9; cell++) {colors[cell] = view[cell].color;}

  friendlies = new Array(9);
  for (cell = 0; cell < 9; cell++) {
    if (ants[cell] != null) {
      if (ants[cell].friend) {friendlies[cell] = ants[cell].type;}
    }
  }

  unfriendlies = new Array(9);
  for (cell = 0; cell < 9; cell++) {
    if (ants[cell] != null) {
      if (!ants[cell].friend) {unfriendlies[cell] = ants[cell].type;}
    }
  }
}

function rot_n_pos(pos, n) {
  for (i = 0; i < n; i++) {
    pos = [2, 5, 8, 1, 4, 7, 0, 3, 6][pos];
  }
  return pos;
}

function rot_left(a) {
  return [a[2], a[5], a[8], a[1], a[4], a[7], a[0], a[3], a[6]];
} 

function rot_right(a) {
  return [a[6], a[3], a[0], a[7], a[4], a[1], a[8], a[5], a[2]];
}

function rSeed(){
  out=23;
  for(i=0;i<9;i++){
    if(food[i]){
      out+=17;
    }
    out += 3 * colors[i];
    if(ants[i]){
      out *= 19;
    }
  }
  return out;
}

function get_response(){
  if (me.type == 5) { //Queen Case:
    return type5();
  }
  else if (me.type == 1) {
    return type1();
  }
  else if (me.type == 2) {
    return type2();
  }
  else if (me.type == 3) {
    return type3();
  }
  else if(me.type == 4){
    return type4();
}

function type5(){
  if (me.food == 0 && count(friendlies, 1) == 0 && count(friendlies, 2) == 0) {
    if (count(food, 1) > 0) {
      for (j = 0; j < 9; j++) {
        if (food[j]) {
          return {cell: j};
        }
      }
    }
    // travel up
    // color own cell if not 4

    if(road()){return road();}

    //move
    for (i = 0; i < 9; i++) {
      if (ants[i] == null && i != 4) {
        return {cell:i};
      }
    }
    return corner_then_ortho();
  }
  if (me.food >= 1 && count(friendlies, 1) == 0 && count(friendlies, 2) == 0) {
    if (ants[5] == null) {
      return {cell:5,type:1};
    }
    if (ants[1] == null) {
      return {cell:5, type:1};
    }
    if (ants[3] == null) {
      return {cell:5,type:1};
    }
    if (ants[7] == null) {
      return {cell:5, type:1};
    }
    return color_self(5);
  }
  if (me.food == 0 && count(friendlies, 1) == 1 && count(friendlies, 2) == 0) {
    if (friendlies.indexOf(1) % 2 == 0) {
      return ortho_then_corner();//PANIC!!! TODO: FIX
    }
    rotation = target_ant(1, 1);
    if (ants[0] == null) {
      return {cell: rot_n_pos(0, rotation)};
    } else {
      return corner_then_ortho;
    }
  }
  if (me.food >= 1 && count(friendlies, 1) == 1 && count(friendlies, 2) == 0) {
    if (friendlies.indexOf(1) % 2 == 0) {
      return corner_then_ortho(); //PANIC!!! TODO: FIX
    }
    rotation = target_ant(1, 1);
    if (ants[3] == null) {
      return {cell: rot_n_pos(3, rotation),type: 2};
    }
    if (ants[0] == null) {
      return { cell: rot_n_pos(0, rotation)};
    }
    return {cell: 4};
  }
  if (count(friendlies, 1) == 1 && count(friendlies, 2) == 1) {
    if (friendlies.indexOf(1) % 2 == 0) {
      return ortho_then_corner();
    }
    rotation = target_ant(1, 1);
    if(food[0] || food[8]){
      return no_op;
    }
    if(ants[5]!=null){
      if(ants[5].type == 2 && ants[2]==null){
        return {cell: rot_n_pos(2, rotation)};
      }
    }
    return corner_then_ortho();
  }
  return corner_then_ortho();
}

function type1(){
//right flank
  if (count(friendlies, 5) == 0 && count(friendlies, 2) == 0 && count(friendlies,1) == 1) {
    //no friends = destruction
    return steal_then_road();
  }
  if (count(friendlies, 5) == 1 && count(friendlies, 2) == 0 && count(friendlies,1) == 1) {
    if (friendlies.indexOf(5) % 2 == 0) {
      return ortho_then_corner();
    }
    rotation = target_ant(5, 3);
    if (ants[0] == null) {
      return {cell: rot_n_pos(0, rotation)};
    }
    return corner_then_ortho(); // PANIC!! TODO: FIX
  }
  if (count(friendlies, 5) == 1 && count(friendlies, 2) == 1 && count(friendlies,1) == 1) {
    if (friendlies.indexOf(5) % 2 == 0) {
      return ortho_then_corner(); 
    }
    rotation = target_ant(5, 3);
    if(friendlies[8] !=null){
      if(friendlies[8].type==2){
        if (ants[0] == null){
          return {cell: rot_n_pos(0, rotation)};
        }
      }
    }
    if (ants[0] != null) {
      if (ants[0].type == 2 && ants[6] == null) {
        return {cell: rot_n_pos(6, rotation)};
      }
    }
    if (ants[6] != null) {
      if (ants[6].type == 2 && ants[0] == null) {
        return {cell: rot_n_pos(0, rotation)};
      }
    }
    return corner_then_ortho();
  }
  return corner_then_ortho();
}

function type2(){
  //left flank
  if (count(friendlies, 5) == 0 && count(friendlies, 1) == 0  && count(friendlies,2) == 1) {
    return steal_then_road();
  }
  if (count(friendlies, 5) == 1 && count(friendlies, 1) == 0  && count(friendlies,2) == 1) {
    if (friendlies.indexOf(5) % 2 == 0) {
      return ortho_then_corner();
    }
    rotation = target_ant(5, 1);
    if (ants[0] == null) {
      return {cell: rot_n_pos(2, rotation)};
    }
    return corner_then_ortho();
    }
    if (count(friendlies, 5) == 1 && count(friendlies, 2) == 1) {
      return {cell: 4,color:2};
    }
    }
  return corner_then_ortho();
}

function type3(){}
function type4(){}

response = get_response();

return make_valid_move(response);

Стратегія.

Фаза 1: Королева сутичка.

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

Фаза 2: сутичка королеви-партнера.

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

Фаза 3: Пірс.

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

Сильні сторони:

Формування трьох мурашок не створює слідів, не загортається і рухається зі швидкістю світла, отримуючи в середньому 0,1% * 3 = 0,003 їжі за ходу. Це в середньому дорівнює 0,003 * 30000 = 90 продуктів харчування на гру, які зазвичай отримують.

Слабкі сторони:

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

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


Ці мурахи практично такі ж, як і мої парові мурашки , але, здається, гірше піддаються моїм тестуванням. Єдина відмінність, яку я помічаю, полягає в тому, що ваші мурахи обертаються, коли королева бачить їжу, до якої інші працівники не можуть дістатися, в той час як моя обертається, коли працівник 2-го типу потрапить на їжу. Я думаю, що твої мурахи також повинні добре працювати, але я не розумію, чому це часом піддається.
K Чжан

@KZhang Цікаво. На моєму тестуванні Пірс в цілому краще, за винятком випадків, коли він завдає серйозного втручання мурашок (Wildfire? Чорна діра?), Коли він створює десятки нових мурашок без потреби. Підкаже лише час і тестування.
fireflame241

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

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

3

Єдина королева

var C = 5;

for(var i = 0; i<9; i++)
{
  if(view[i].food === 1)
    return {cell:i};
}


if(view[4].color != 5 && !view[0].ant && !view[1].ant && !view[2].ant && !view[3].ant && !view[5].ant && !view[6].ant && !view[7].ant && !view[8].ant)
  return {cell:4, color:C};

if(!view[0].ant && 
   view[0].color != C && view[8].color === C && view[1].color != C && view[3].color != C && view[2].color != C && view[6].color != C)

      return {cell:0};

if(!view[2].ant && 
   view[2].color != C && view[6].color === C && view[1].color != C && view[5].color != C  && view[0].color != C && view[8].color != C)

     return {cell:2};

if(!view[6].ant && 
   view[6].color != C && view[2].color ===  C && view[3].color != C && view[7].color != C  && view[0].color != C && view[8].color != C)

     return {cell:6};

if(!view[8].ant && 
   view[8].color != C && view[0].color === C && view[5].color != C && view[7].color != C  && view[2].color != C && view[6].color != C)

     return {cell:8};


if(!view[0].ant)
  return {cell:0};

if(!view[2].ant)
  return {cell:2};

if(!view[6].ant)
  return {cell:6};

if(!view[8].ant)
  return {cell:8};


return {cell:4};

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

Схоже, стратегія схожа на Самотнього Вовка (ненавмисне).


3

Провідник

Розкладіть банду!

Explorer - це команда з 5 чоловік, яка має на меті поширитись настільки далеко, наскільки це можливо, при цьому все-таки повернути їжу королеві.

Королева

Сама королева використовує 3-ступінчасті установки.

1 етап.

На початку матчу це дикий штрих для їжі, як і будь-яка добра королева. Він просто прямує по прямій лінії по діагоналі, поки не знайде їжу, а потім випадковим чином змінить напрямок. Коли в ньому є 4 їжі, він переходить на 2 етап.

2 етап

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

3 етап

Усі етапи 3 - це сидіти нерухомо до кінця матчу, гарантуючи, що чотири плитки навколо нього встановлені правильно.

Робітники

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

var me = view[4].ant
var turf = view[4].color

var queenHolder = 2 // "Queen Holder", the turf colour for the queen in stage 3.
var queenBuild = 7 // "Queen Build", the turf colour for the queen in stage 2.
var antTrail = [3, 4, 5, 6] // Various colours of the ant's trails.
var orth = [1, 3, 5, 7] // Orthogonal Directions.
var rotates = [[1,3,5,7],[3,7,1,5],[5,1,7,3],[7,5,3,1]] // These are the orthogonal directions rotated so 0 is the first position, used in the queen build stage.
var outside = [1,2,3,5,6,7,8] // Every tile but the center one.
var diag = [0,2,6,8] // Diagonal Directions.

// Define a move function to avoid throwing an error.
function move(dir){
    if(view[dir].ant)   // If we're going to move onto an ant.
        dir = 4 // Don't move anywhere.
    if(view[dir].food && me.type < 5 && me.food > 0)    // If we're going to over-eat.
        dir = 4 // Don't move anywhere.
    return {cell: dir}  // Build the move output.
}

if(me.type == 5){ // If we're the queen.
    var invDiag = [8,6,2,0] // Inverse of diagonals, using the indexing of diag. So 0 becomes 8, and such.
    if(turf == 1 || turf == 8){
        // Stage 1.
        // Find enough food to start a hive.
        for(var i=0; i < view.length; i++){ // Check every tile in view
            if(view[i].food){   // Is it food?
                return move(i)  // Move to it.
            }
        }
        if(me.food > 3) // Do we have 4 food?
            return {cell:4, color:queenBuild}   // Move to stage 2.
        if(turf == 1)   // Are we on a white tile?
            return {cell:4, color:8}    // Set the tile to black.
        for(var i=0; i < diag.length; i++)  // Check all diagonals.
            if(view[diag[i]].color == 8)    // Is it black?
                return move(invDiag[i]) // Move in the opposite direction. This creates a straight diagonal line.
        return move(2)  // When in doubt, move randomly diagonally.
    }else if(turf == queenBuild){
        // Stage 2.
        // Spawn ants around, and set up their movement paths.
        if(me.food < 1) // Have we used all our food?
            return {cell:4, color:queenHolder}  // Move to stage 3.

        var firstHolder = -1; // Stores which way we're facing.
        for(var i=0; i < orth.length; i++){ // Check orthogonals.
            if(view[orth[i]].color == antTrail[0]){ // Is it the first trail colour?
                firstHolder = i // THIS WAY UP
                break;
            }
        }
        if(firstHolder==-1) // No way is up?
            return {cell:1, color:antTrail[0]} // Set a random direction to up.

        var orthRot = rotates[firstHolder]  // Get the rotated orthogonal set for the current up direction.
        for(var i=0; i < orthRot.length; i++){  // For each of them.
            if(!view[orthRot[i]].ant)   // Is there an ant on this tile yet?
                return {cell:orthRot[i], type:(i+1)}    // If not, place one down with the correct type.
            if(view[orthRot[i]].color!=antTrail[i]) // Otherwise, is the turf set correctly yet?
                return {cell:orthRot[i], color:antTrail[i]} // If not, set the turf.
        }
        return {cell:4, color:queenHolder}; // After all's said and done, move to stage 3. Probably won't happen this way.
    }else if(turf == queenHolder){
        // Stage 3.
        // Sit still, ensure rails exist around.

        var firstHolder = -1;   // Same behavoir of which way is up from stage 2.
        for(var i=0; i < orth.length; i++){
            if(view[orth[i]].color == antTrail[0]){
                firstHolder = i
                break;
            }
        }
        if(firstHolder==-1)
            return {cell:1, color:antTrail[0]}

        var orthRot = rotates[firstHolder]
        for(var i=0; i < orthRot.length; i++)   // Basically stage 2 without the spawning of ants.
            if(view[orthRot[i]].color!=antTrail[i])
                return {cell:orthRot[i], color:antTrail[i]}

        return {cell:4, color:queenHolder}  // And if there's nothing better to do, waste your time.
    }else{
        return {cell:4, color:1}    // We're lost, go back to stage 1, and try again.
        // I could probably add logic to check if we're stage 3 or something, but meh.
    }

}else{  // If we're a worker!

    for(var i=0; i < orth.length; i++)  // Check around.
        if(view[orth[i]].ant && view[orth[i]].ant.type == 5 && view[orth[i]].ant.friend && view[orth[i]].color == queenBuild)   // Is there a queen, in build mode, around us?
            return move(4)  // Wait politely for her to finish.

    var col = antTrail[me.type-1] // Which colour I use.

    if(me.food < 1){    // If we have no food.
        for(i=0; i < orth.length; i++){ // Check Orthogonals
            if(view[orth[i]].food){ // Is there food there?
                if(turf != col) // If we're not standing on our trail.
                    return {cell: 4, color: col}    // Place our trail here, so we can still find our way back.
                return move(orth[i])    // Otherwise, move to the food!
            }
        }
    }

    if(turf == col) // If we're sitting on our trail.
        return move(2) // Move off it randomly

    var corq = (t)=>t.color == col || (t.ant && t.ant.type == 5 && t.ant.friend)    // Helper function, does this tile contain our trail or the queen?
    var corqorf = (t)=>corq(t) || t.food    // Helper function, odes this tile contain our trail, the queen, or a piece of food?
    var queenInView = false;
    for(var i=0; i < view.length; i++)  // Check the entire view.
        if(view[i].ant && view[i].ant.type == 5 && view[i].ant.friend) // Can we see the queen?
            queenInView = true; // Remember this.

    // Using food > 0 behavoir if we see a queen, makes it so that we don't accidentally build our path over the queen or something silly.

    if(me.food > 0 || queenInView){ // If we have food, or we can see the queen.
        // DON'T build paths, just orbit our path clockwise.
        var orthmov = [3,7,1,5] // Directions to move if we see a path on an orthogonal.
        var diagC = [3,1,7,5]   // Directions to move if we see a path on a diagonal.
        for(var i=0; i < orth.length; i++)  // For each Orthogonal, which takes preference.
            if(corqorf(view[orth[i]]))  // Is there the queen, a trail, or food here?
                return move(orthmov[i]) // move CW to it.
        for(var i=0; i < diag.length; i++)  // Ditto for Diagonals.
            if(corqorf(view[diag[i]]))
                return move(diagC[i])

    }else{
        // EXTEND paths, or continue orbiting clockwise.
        var orthM = [0,6,2,8]   // Directions a path should be when we check an orthogonal.
        var orthMo = [3,7,1,5]  // Directions to move if we see an orthogonal, and the diagonal is there.
        var diagC = [3,1,7,5]   // Directions to place a path if we only see an orthogonal.
        for(var i=0; i < orth.length; i++){ // In each Orthogonal.
            var v = view[orth[i]]
            if(corq(v)){    // Is there a trial?
                if(corq(view[orthM[i]]))    // Is there a trail in the after it position?
                    return move(orthMo[i])  // Move in the correct direction.
                return {cell:orthM[i], color:col}   // Place the trail in the after it position.
            }
        }
        for(var i=0; i < diag.length; i++)  // Check diagonals as a last resort.
            if(corq(view[diag[i]])) // Is there a path /HERE/?
                return {cell:diagC[i], color:col}   // Place the respective diagonal's orthogonal.

    }
    return move(2)  // When we're lost, scamper around. Just like Trail-eraser wants us to.
}

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

Причина: Неможливо створити нового працівника поверх їжі. Введення: [{"color": 7, "food": 0, "ant": null}, {"color": 1, "food": 0, "ant": null}, {"color": 8, "їжа": 0, "мураха": null}, {"колір": 3, "їжа": 1, "мураха": нуль}, {"колір": 7, "їжа": 0, "мураха": {"food": 1, "type": 5, "friend": true}}, {"color": 1, "food": 0, "ant": null}, {"color": 1, "food ": 0," ant ": null}, {" color ": 1," food ": 0," ant ": null}, {" color ": 7," food ": 0," ant ": null} ] Відповідь: {"комірка": 3, "тип":

3

Романеско-роуд

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

Перший блок коду у відповіді - це той, який автоматично включається в гру:

// Full version that won't be disqualified for moving onto another ant

var i

// Color own cell if white
if (view[4].color === 1) {
    return {cell:4, color:3}
}

// Otherwise move to food if visible
for (i=0; i<9; i++) {
    if (view[i].food) {
        return {cell:i}
    }
}

// Otherwise move to a white cell opposite a colored cell
for (i=0; i<9; i++) {
    if (view[i].color === 1 && view[8-i].color > 1 && !view[i].ant) {
        return {cell:i}
    }
}

// Otherwise move to an unoccupied cell
for (i=0; i<9; i++) {
    if (!view[i].ant) {
        return {cell:i}
    }
}

// Otherwise don't move at all
return {cell:4}

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

// Basic version for an intuitive understanding

var i

// Color own cell if white
if (view[4].color === 1) {
    return {cell:4, color:3}
}

// Otherwise move to food if visible
for (i=0; i<9; i++) {
    if (view[i].food) {
        return {cell:i}
    }
}

// Otherwise move to a white cell opposite a colored cell
for (i=0; i<9; i++) {
    if (view[i].color === 1 && view[8-i].color > 1) {
        return {cell:i}
    }
}

// Otherwise move "left and up", which will be a random direction
return {cell:0}

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

Для прикладу отримання випадкового руху, а не прямої, див. Броунівський джиг .


3

ВінсАнт

Чи відчуваєте ви, що після дев'яти місяців арена все ще виглядає трохи спокійно?

Ви вважаєте, що художник, що ходить в Антдомі та осиротілий працівник Формації, міг би зробити невелику допомогу в привабливому його оформленні?

Тоді вам сподобається цей!

Шосе, переобладнаний

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

Клод і Джин

Щоб зробити це доступним, є кістяк, піднятий з моєї вітряки - уявіть собі королеву вітряка та секретаря / навігатора, які ніколи не ростуть і не осідають. (Раніше ідея була вдосконалена Lightspeed , будучи першопрохідником вибухового обладнання вампіра . Хоча реалізація відрізняється від Lightspeed.) Необхідні були ряд модифікацій, щоб запобігти руйнуванню речі, коли ми натрапимо на своє власне потомство. .

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

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

var ANV=1;var AMK=2;var AGS=3;var AWM=4;var AQ=5;var THC=1;var THP=[0,0,0,0,0,0];THP[AMK]=19;THP[AGS]=17;THP[AWM]=15;var RM=15;var SPDAT =[0,AMK,AGS,0,AWM,0,AGS,AWM,0,AMK,0,AWM,AMK,0,AGS];var PW=1;var PY=2;var PP=3;var PC=4;var PR=5;var PG=6;var PB=7;var PK=8;var LCLR=PW;var LT=PY;var LLSF=PG;var TN=8;var POSC=4;var NOP={cell:POSC};var CCW=[6,7,8,5,2,1,0,3,6,7,8,5,2,1,0,3,6,7,8,5,2,1];
var xn=-1;var here=view[POSC];var mC=here.color;var myself=here.ant;var mT=myself.type;var mF=myself.food;var mS=(mT!=AQ&&mF>0);var dOK=[true,true,true,true,true,true,true,true,true];
var uo=true;var sL=[0,0,0,0,0,0,0,0,0];var sD=[0,0,0,0,0,0,0,0,0];var sN=[0,0,0,0,0,0,0,0,0];var sT=[0,0,0,0,0,0,0,0,0];var fdL=0;var fdD=0;var fdT=0;sT[mC]++;for (var i=0; i<TN; i+=2){var cell=view[CCW[i]];sD[cell.color]++;sN[cell.color]++;sT[cell.color]++;if (cell.food>0){fdD++;fdT++;if (mS){dOK[CCW[i]]=false;uo=false;}}}for (var i=1; i<TN; i+=2){var cell=view[CCW[i]];sL[cell.color]++;sN[cell.color]++;sT[cell.color]++;if (cell.food>0){fdL++;fdT++;if (mS){dOK[CCW[i]]=false;uo=false;}}}var aF=[0,0,0,0,0,0];var aLF=[0,0,0,0,0,0];var aUF=[0,0,0,0,0,0];var fT=0;var mQ=0;var aE=[0,0,0,0,0,0];var aLE=[0,0,0,0,0,0];var aUE=[0,0,0,0,0,0];var eT=0;for (var i=0; i<TN; i++){var cell=view[CCW[i]];if (cell.ant){if (cell.ant.friend){aF[cell.ant.type]++;fT++;if (cell.ant.type==AQ){xn=i&6;mQ=i&1;}if (cell.ant.food>0){aLF[cell.ant.type]++;} else {aUF[cell.ant.type]++;}} else {aE[cell.ant.type]++;eT++;if (cell.ant.food>0){aLE[cell.ant.type]++;} else {aUE[cell.ant.type]++;}}dOK[CCW[i]]=false;uo=false;}}switch (mT){case AQ:return (rQSs());case ANV:return (rNSs());case AMK:return (rMSs());case AGS:return (rGSs());case AWM:return (rWSs());default:return NOP;}function rQSs(){switch (aF[ANV]){case 0:return (rQScrSy());case 1:for (var i=0; i<TN; i++){var cell=view[CCW[i]];if (cell.ant&&cell.ant.friend&&cell.ant.type==ANV){xn=i&6;if (i&1){return (rQLsSy());} else {return (rQCSy());}}}break;default:return (rQCNSy());}return NOP;}function rNSs(){if (aF[AQ]>0){if (mQ==1){return (rSLSy());} else {return (rNRSy());}} else if ((mF==0)&&(fdT>0)){return (rPEgSy());} else {return (rPPgSy());}}function rMSs(){if ((aF[AQ]>0)&&(mF==0)){if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else {return NOP;}} else if ((mF>0)&&(aF[AQ]+aF[ANV]>0)){return NOP;} else if ((mF==0)&&(fdT>0)){return (rPEgSy());} else if (aF[AGS]+aF[AWM]>1){return (rPMgSy());} else if (aF[AGS]==1){return (rCPgSy());} else {return (rMPgSy());}}function rGSs(){if ((aF[AQ]>0)&&(mF==0)){if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else {return NOP;}} else if ((mF>0)&&(aF[AQ]+aF[ANV]>0)){return NOP;} else if ((mF==0)&&(fdT>0)){return (rPEgSy());} else if (aF[AMK]+aF[AWM]>1){return (rPMgSy());} else if (aF[AMK]==1){return (rJPgSy());} else {return (rGPgSy());}}function rWSs(){if ((aF[AQ]>0)&&(mF==0)){if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else {return NOP;}} else if ((mF>0)&&(aF[AQ]+aF[ANV]>0)){return NOP;} else if ((mF==0)&&(fdT>0)){return (rPEgSy());} else if (aF[AMK]+aF[AGS]>1){return (rPMgSy());} else {return (rWPgSy());}}function rQScrSy(){if (uo){if (fdT>0){return (rQSETc());} else if (mF>=THC){for (var i=0; i<TN; i+=2){if ((view[CCW[i]].color==LT)||(view[CCW[i+1]].color==LT)){return {cell:CCW[i+1],type:ANV};}}return {cell:1,type:ANV};} else if (mC!=LT){if ((mC==LCLR)||(sN[LCLR]>=TN-1)){return {cell:POSC,color:LT};} else {return (rQSTCTc());}} else if ((sN[LCLR]>=4)&&(sN[LT]==1)){for (var i=0; i<TN; i+=2){if ((view[CCW[i]].color==LT)||(view[CCW[i+1]].color==LT)){return {cell:CCW[i+4]};}}} else if (sN[LCLR]==TN){return {cell:0};} else {return (rQSATc());}} else {if ((fdT>0)&&(eT>0)&&(eT==aE[AQ])){return (rQSSTc());} else {return (rQSEvTc());}}return NOP;}function rQLsSy(){if ((sT[LCLR]<=2)&&(mF>1)&&(eT==0)){var artist=SPDAT[mF % RM];if ((artist!=0)&&(mF>=THP[artist])&&(aF[artist]<=1)){var tc=[6,2,4,5,3];for (var i=0; i<tc.length; i++){var c=CCW[xn+tc[i]];if (dOK[c]&&(view[c].food==0)){return {cell:c,type:artist};}}}}if ((eT==0)&&(fT==1)){if (view[CCW[xn+2]].food>0){return {cell:CCW[xn+2]};} else if ((view[CCW[xn+3]].food +view[CCW[xn+4]].food>0)&&(view[CCW[xn+1]].color!=LLSF)){return NOP;} else {return {cell:CCW[xn+2]};}} else if (dOK[CCW[xn+2]]&&dOK[CCW[xn+3]]){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn]]&&dOK[CCW[xn+7]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else {return NOP;}}function rQCNSy(){for (var i=0; i<TN; i++){var cell=view[CCW[i]];if (cell.ant&&cell.ant.friend&&cell.ant.type==ANV){if (i&1){if (dOK[CCW[i-1]]){return {cell:CCW[i-1]};} else if (dOK[CCW[i+1]]){return {cell:CCW[i+1]};} else if (dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else {return NOP;}} else {if (dOK[CCW[i+7]]){return {cell:CCW[i+7]};} else if (dOK[CCW[i+1]]){return {cell:CCW[i+1]};} else if (dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else {return NOP;}}}}return (rQCSy());}function rQCSy(){return NOP;}function rSLSy(){if ((eT==0)&&(fT==1)){if (view[CCW[xn]].food>0){return {cell:CCW[xn]};} else if (view[CCW[xn+7]].food +view[CCW[xn+6]].food>0){return {cell:POSC,color:LLSF};} else {return {cell:CCW[xn]};}} else if ((eT>0)&&view[CCW[xn+2]].ant&&!view[CCW[xn+2]].ant.friend){return {cell:POSC,color:LLSF};} else if ((fT>1)&&((view[CCW[xn+6]].ant&&view[CCW[xn+6]].ant.friend&&
(view[CCW[xn+6]].ant.food>0))||(view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend&&
(view[CCW[xn+5]].ant.food>0)))){return {cell:POSC,color:LLSF};} else {if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};}}return NOP;}function rNRSy(){if (view[CCW[xn+1]].ant&&view[CCW[xn+1]].ant.friend&&
(view[CCW[xn+1]].ant.type==mT)){if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else {return NOP;}} else if (view[CCW[xn+7]].ant&&view[CCW[xn+7]].ant.friend&&(view[CCW[xn+7]].ant.type==mT)){if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else {return NOP;}} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}}function rPPgSy(){if (aLF[AMK]+aLF[AGS] +aLF[AWM]>0){for (var i=0; i<TN; i++){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.food>0)){if (dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else if (dOK[CCW[i+3]]){return {cell:CCW[i+3]};} else if (dOK[CCW[i+5]]){return {cell:CCW[i+5]};}}}} else if (aF[mT]>0){return (rSPTc());}return (rPPgTc());}function rMPgSy(){if (aF[mT]>0){return (rSPTc());}return (rMPgTc());}function rGPgSy(){if (aF[mT]>0){return (rSPTc());}return (rGPgTc());}function rWPgSy(){if (aF[mT]>0){return (rSPTc());}return (rWPgTc());}function rCPgSy(){var phase=0;for (var i=0; i<TN; i++){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==AGS)){xn=i&6;phase=i&1;break;}}if ((phase==1)&&(mC==view[CCW[xn+7]].color)&&(view[CCW[xn]].color==view[CCW[xn+1]].color)){if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}} else {return {cell:CCW[xn+7],color:mC};}return NOP;}function rJPgSy(){var phase=0;for (var i=0; i<TN; i++){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==AMK)){xn=i&6;phase=i&1;break;}}if (phase==0){if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}} else {return {cell:CCW[xn+3],color:mC};}return NOP;}function rPEgSy(){for (var i=0; i<TN; i++){if ((view[CCW[i]].food>0)&&dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;}function rPMgSy(){for (var i=0; i<TN; i+=2){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type!=mT)){if (dOK[CCW[i+1]]&&!view[CCW[i+7]].ant&&!view[CCW[i+2]].ant&&!view[CCW[i+3]].ant){return {cell:CCW[i+1]};} else if (dOK[CCW[i+7]]&&!view[CCW[i+1]].ant&&
!view[CCW[i+6]].ant&&!view[CCW[i+5]].ant){return {cell:CCW[i+7]};}}}for (var i=1; i<TN; i+=2){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type!=mT)){if (dOK[CCW[i-1]]&&!view[CCW[i+6]].ant){return {cell:CCW[i-1]};} else if (dOK[CCW[i+1]]&&!view[CCW[i+2]].ant){return {cell:CCW[i+1]};}}}for (var i=0; i<TN; i++){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return {cell:POSC,color:view[CCW[0]].color};}function rQSETc(){if (mC!=LT){return {cell:POSC,color:LT};}for (var i=0; i<TN; i++){if (view[CCW[i]].food>0){return {cell:CCW[i]};}}return NOP;}function rQSSTc(){for (var i=0; i<TN; i++){if ((view[CCW[i]].food>0)&&(dOK[CCW[i]])){return {cell:CCW[i]};}}return NOP;}function rQSTCTc(){if ((mC!=LCLR)&&(sN[mC]>=4)){if (sN[LT]==0){return {cell:POSC,color:LT};} else if (sN[LT]>=3){return {cell:POSC,color:LT};} else {for (var i=0; i<TN; i++){if ((view[CCW[i]].color==LT)&&(view[CCW[i+2]].color!=LT)){return {cell:CCW[i+2],color:LT};}}return NOP;}} else if (sN[LT]==1){for (var i=0; i<TN; i++){if ((view[CCW[i]].color==LT)&&(view[CCW[i+4]].color!=LCLR)){if (view[CCW[i+1]].color==LCLR){return { cell:CCW[i+1]};} else if (view[CCW[i+7]].color==LCLR){return { cell:CCW[i+7]};} else {return {cell:POSC,color:LT};}}}return {cell:POSC,color:LT};} else {return {cell:POSC,color:LT};}return NOP;}function rQSATc(){for (var i=0; i<TN; i++){if ((view[CCW[i]].color==LCLR)&&(view[CCW[i+1]].color==LCLR)&&(view[CCW[i+2]].color==LCLR)){if ((view[CCW[i+3]].color==LCLR)&&(view[CCW[i+4]].color==LCLR)){return {cell:CCW[i+2]};}return {cell:CCW[i+1]};}}for (var i=TN-1; i>=0; i--){if (view[CCW[i]].color!=LT){return {cell:CCW[i]};}}for (var i=0; i<TN; i++){if (view[CCW[i]].color!=LT){return {cell:CCW[i],color:LCLR};}}return {cell:0,color:LCLR};}function rQSEvTc(){if (sN[LT]>0){for (var i=0; i<TN; i++){if (view[CCW[i]].color==LT){xn=i&6;}}if ( dOK[CCW[xn+7]]&&dOK[CCW[xn]]&&dOK[CCW[xn+1]]&&dOK[CCW[xn+2]]&&dOK[CCW[xn+3]] ){return {cell:CCW[xn+1]};} else if (dOK[CCW[xn+5]]&&dOK[CCW[xn+6]]&&dOK[CCW[xn+7]]&&dOK[CCW[xn]]&&dOK[CCW[xn+1]]){return {cell:CCW[xn+7]};} else if (dOK[CCW[xn+3]]&&dOK[CCW[xn+4]]&&dOK[CCW[xn+5]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+5]]&&dOK[CCW[xn+6]]&&dOK[CCW[xn+7]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+1]]&&dOK[CCW[xn+2]]&&dOK[CCW[xn+3]]){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn+7]]&&dOK[CCW[xn]]&&dOK[CCW[xn+1]]){return {cell:CCW[xn]};} else {for (var i=0; i<TN; i++){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;}} else {for (var i=0; i<TN; i++){if (dOK[CCW[i]]&&dOK[CCW[i+1]]&&dOK[CCW[i+2]]&&dOK[CCW[i+3]]&&dOK[CCW[i+4]]){return {cell:CCW[i+2]};}}for (var i=0; i<TN; i++){if (dOK[CCW[i]]&&dOK[CCW[i+1]]&&dOK[CCW[i+2]]){return {cell:CCW[i+1]};}}for (var i=0; i<TN; i++){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;}return NOP;}function rPPgTc(){if (sL[mC]==0){return {cell:1,color:mC};}for (var i=1; i<TN; i+=2){if (view[CCW[i]].color==mC){xn=i&6;break;}}var col1=(mC+1) % 8+1;if ((view[CCW[xn+5]].color==mC)&&(view[CCW[xn+3]].color==col1)&&(view[CCW[xn+7]].color!=col1)){xn=(xn+4) % 8;}if (view[CCW[xn+7]].color!=col1){return {cell:CCW[xn+7],color:col1};} else if (view[CCW[xn]].color!=col1){return {cell:CCW[xn],color:col1};}var col2=(mC+5) % 8+1;if (view[CCW[xn+3]].color!=col2){return {cell:CCW[xn+3],color:col2};} else if (view[CCW[xn+2]].color!=col2){return {cell:CCW[xn+2],color:col2};} else if (view[CCW[xn+5]].color!=mC){return {cell:CCW[xn+5],color:mC};} else if (view[CCW[xn+4]].color!=col2){return {cell:CCW[xn+4],color:col2};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else {return (rWgPTc());}}function rMPgTc(){switch (sT[mC]){case 9:var col=((mC+2) % 8)+1;return {cell:CCW[0],color:col};case 8:for (var i=0; i<TN; i++){var col=view[CCW[i]].color;if (col!=mC){if (i==0){return {cell:POSC,color:col};} else if ((i==1)&&dOK[CCW[i+3]]){return {cell:CCW[i+3]};} else if ((i==2)&&dOK[CCW[i+5]]){return {cell:CCW[i+5]};} else {return {cell:CCW[i-1],color:col};}}}break;case 7:return rWgPTc();case 6:for (var i=0; i<TN; i++){if (view[CCW[i]].color!=mC){if ((i==0)&&dOK[CCW[i+5]]){return {cell:CCW[i+5]};} else if ((i==1)&&dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else {return {cell:CCW[i],color:mC};}}}break;case 5:case 4:case 3:for (var i=0; i<TN; i++){if (view[CCW[i]].color!=mC){return {cell:CCW[i],color:mC};}}break;case 2:case 1:default:for (var i=TN-1; i>=0; i--){var col=view[CCW[i]].color;if ((col==mC)&&(sT[view[CCW[i+4]].color]==7)&&dOK[CCW[i+4]]){return {cell:CCW[i+4]};}if (sT[col]>=3){return {cell:POSC,color:col};}}var col=view[CCW[1]].color;if (view[CCW[0]].color!=col){return {cell:CCW[0],color:col};} else if (view[CCW[2]].color!=col){return {cell:CCW[2],color:col};}break;}return (rWgPTc());}function rGPgTc(){var col=0;for (var c0=view[CCW[0]].color; c0<view[CCW[0]].color+8; c0++){var c=(c0 % 8)+1;if (sN[c]==0){col=c;}}if (col==0){return (rWgPTc());}for (var i=0; i<TN; i++){if (sN[view[CCW[i]].color]>1){return {cell:CCW[i],color:col};}}return (rWgPTc());}function rWPgTc(){var col=((mC+6) % 8)+1;if (sT[mC]==9){return {cell:CCW[0],color:col};}var myRand=(view[CCW[0]].color+sT[view[CCW[2]].color]) % 3;
switch (myRand){case 0:for (var i=0; i<TN; i+=2){if (dOK[CCW[i]]){return {cell:CCW[i]};}}break;case 1:if (view[CCW[1]].color!=view[CCW[7]].color){return {cell:CCW[1],color:view[CCW[7]].color};} else if (view[CCW[5]].color!=view[CCW[3]].color){return {cell:CCW[5],color:view[CCW[3]].color};}break;case 2:if (view[CCW[5]].color!=view[CCW[3]].color){return {cell:CCW[5],color:view[CCW[3]].color};} else if (view[CCW[1]].color!=view[CCW[7]].color){return {cell:CCW[1],color:view[CCW[7]].color};}break;default:break;}for (var i=1; i<TN; i+=2){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return (rWgPTc());}function rSPTc(){for (var i=0; i<TN; i++){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==mT)){if (dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else if (dOK[CCW[i+3]]){return {cell:CCW[i+3]};} else if (dOK[CCW[i+5]]){return {cell:CCW[i+5]};} else if (dOK[CCW[i+2]]){return {cell:CCW[i+2]};} else if (dOK[CCW[i+6]]){return {cell:CCW[i+6]};} else if (dOK[CCW[i+1]]){return {cell:CCW[i+1]};}}}return NOP;}function rWgPTc(){for (var i=0; i<TN; i++){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;}

Насолоджуйтесь!

** v1.0.1 Тепер сумісний із контролерами суворого режиму.

** v1.0.2 Виправляє помилку, що дискваліфікує.


Зауважте, що якщо мурахи достатньо "креативні" і вдається збити з пантелику більш просунуті мурахи, то ця запис може насправді мати шанс перевершити Lightspeed - менш сильні конкуренти = більший бал. Мабуть, тут не буде випадку, оскільки навіть Шосе зазвичай вдається пережити лікування художників ...
Аліон,

@Alion, художникам VinceAnt час від часу вдається заплутати мурашок вітряка серед інших, а іноді блокують ворожих робітників, або спричиняють розсувні шахтарі або шахтарі вітряків заводити нові рейки в неправильних місцях - але якщо Lightspeed теж грає в ті ж ігри, Lightspeed також піде на користь ...
GNiklasch

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

@trichoplax Виправлено - вибачте і дякую!
GNiklasch

2

Половина

if(view[4].ant.type==5&&view[4].food>1)
{
    for(var i = 0; i<9; i++)
    {
        if(!view[i].ant)
        {
            return{cell:i,type: 1};
        }
    }
}
if(view[4].color != 3){
    return {cell: 4, color: 3};
}
for(var i = 0; i<9; i++)
{
    if(view[i].food==1)
    {
        return({cell:i})
    }

}


var i, j
var orthogonals = [1, 3, 7, 5]  // These are the non-diagonal cells



// Otherwise move to a white cell opposite a colored cell
for (i=0; i<4; i++) {
    j = (i+2) % 4
    if (view[orthogonals[i]].color === 1 &&
        view[orthogonals[j]].color > 1 && !view[orthogonals[i]].ant) {
        return {cell:orthogonals[i]}
    }
}

// Otherwise move to one of the vertical or horizontal cells if not occupied
for (i=1; i<9; i+=2) {
    if (!view[i].ant) {
        return {cell:i}
    }
}

// Otherwise move to one of the diagonal cells if not occupied
for (i=0; i<9; i+=2) {
    if (!view[i].ant) {
        return {cell:i}
    }
}


for(var i = 0; i<9; i++)
{
    if(view[i].color==1)
    {
        return {cell:i};
    }
}
for(var i = 0; i<9; i++)
{
    if(view[i].ant==null)
    {
        return {cell:i};
    }
}

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

ЦЕ РОБОТА В ПРОГРАМНІЙ БОТІ.


Це може бути дискваліфіковане для створення працівників на їжі в екзотичних обставинах
pppery

@ppperry насправді не може. Ніколи не робить працівників: /
Крістофер

Насправді, це не може, тому що логіка "створити працівника" - це просто мертвий код. Ще варто зазначити
pppery

Мене збентежив мертвий view[4].food > 1чек, що є помилкою, яку я один раз зробив сам
pppery

1
Зміна view[4].food > 1в view[4].ant.food > 1на першій лінії
pppery
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.