Голодні краплі KoTH


9

Конкурс завершено! Читайте коментарі про краплі, щоб переглянути їхні оцінки.

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

Енергія та рух

Ваша крапля починається з кожного раунду зі 100 енергією, і вона не обмежує кількість енергії, яку може зібрати. Кожен раунд проводиться по черзі, при цьому кожен крап має можливість рухатись на північ, схід, південь чи захід у будь-який заданий поворот або стояти нерухомо. Переміщення використовує 1 енергію, а стояння використовує 0,25 енергії. Довжина сторони карти дорівнюєceil(0.25 * blobCount) * 2 - 1одиниць, мінімум 9 одиниць. Усі краплі починаються з краю карти, по одному розміщується в кожному куті, а кожен наступний крап розміщується на 2 одиниці від інших. Кожні 30 оборотів хвиля гранул розміщується у випадкових місцях навколо карти, щонайменше, на 1 одиницю від будь-якого краю. Щоразу, коли з’являється хвиля гранул, кількість гранул (спочатку вдвічі більше крапок або ширини карти, залежно від того, що більше) в наступній хвилі зменшується на 1, змушуючи кількість крапель зменшуватися з часом. Кожен гранул відновлює від 5 до 15 енергії. Коли енергія краплини менше або дорівнює 0, вона гине.

Їсть

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

Виявлення та інформація

Краплі можуть бачити будь-які гранули або інші краплі на відстані 4 одиниць. Коли викликаються їх функції, краплі надаються:

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

Розщеплення

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

Передача енергії

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

Функції, Зберігання та UID

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

  1. Довжина сторони карти як ціле число
  2. Об'єкт з двома масивами:, pelletsі blobs. Обидва масиви містять об'єкти, обидва мають posвластивість, що містить позицію гранул або блобу, відформатовану як [x,y]. Гранули матимуть energyмайно, а краплі матимуть uidмайно та energyмайно
  3. Об'єкт , що містить різні властивості згустку він передається: energy, uidі pos. posМасив в форматі[x,y]
  4. Об'єкт, що містить два об'єкти зберігання блобу. selfВластивість містить окремий об'єкт зберігання , який може бути змінений , однак вважає за потрібне БЛОБ (шляхом маніпулювання властивості об'єкта , який передається), і communalвластивість , яке може бути змінено з допомогою будь-якого родича.

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

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

Повернення цінностей

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

  • Північ = -Y
  • Схід = + X
  • Південь = + Y
  • Захід = -X

Зверніть увагу, що [0,0]це верхній лівий кут , і Y збільшується, коли ви спускаєтесь вниз. Повернене значення функції повинно відповідати цим правилам:

  • Не робити нічого: повернути нічого, 0, null, undefined, false або будь-яке інше значення, яке прирівнюється до false
  • Для переміщення: поверніть одну з чотирьох глобальних змінних: Північ, Схід, Південь або Захід, які прирівнюються до "північ", "Схід", "Південь" або "Захід" (що також може використовуватися як повернене значення)
  • До Split: повернути глобальну змінну SplitNorth, SplitEast, SplitSouth або SplitWest, напрямок, який вказує, де розмістити нову крапку

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

Заздалегідь визначені бібліотечні функції

За замовчуванням доступно кілька основних функцій, щоб заощадити час:

taxiDist (pt1, pt2)

Повертає відстань між двома пунктами (відстань X плюс відстань Y).

taxiDist([0, 0], [2, 2]) //4
taxiDist([3, 4], [1, 5]) //3
taxiDist([1.25, 1.3], [1.3, 1.4]) //0.15
taxiDist([0, 0], [5, 2.5], 2.5) //3
taxiDist([0, 0], [2, 4], 2.5) //2.4

hypotDist (pt1, pt2)

Повертає відстань між двома точками згідно теореми піфагора

hypotDist([0, 0], [5, 12]) //13
hypotDist([4, 6], [8, 9]) //5
hypotDist([0, 1], [2, 1]) //2
hypotDist([1, 1], [2, 2]) //sqrt(2)

modDir (dir, amt)

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

modDist(North, 1) //East
modDist(East, 2) //West
modDist(West, 3) //South
modDist(South, 4) //South

Приклад Blob

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

function(map, near, me, storage) {
    if (me.energy > 150)
        return SplitNorth;
    if (!near.pellets.length)
        return null;
    var dirs = [0, 0, 0, 0];
    for (let p, i = 0; i < near.pellets.length; i++) {
        p = near.pellets[i];
        dirs[0] += me.pos[1] - p.pos[1];
        dirs[1] += p.pos[0] - me.pos[0];
        dirs[2] += p.pos[1] - me.pos[1];
        dirs[3] += me.pos[0] - p.pos[0];
    }
    return [North, East, South, West][dirs.indexOf(Math.max(...dirs))];
}

Правила

  • Стандартні лазівки заборонені. Також немає жодних нестандартних лазів.
  • Жодна крапка не може намагатися змінювати чи читати будь-які дані, не передані їй за допомогою своїх параметрів
  • Жодна крапка не може намагатися змінити змінну поверненого значення на саботаж інших крапок
  • Раунд триває до тих пір, поки єдині залишилися краплі не будуть родичами
  • Жодна крапка не може змінювати дані, вводячи функції в її параметри, які змінюють значення за допомогою thisключового слова
  • Усі подання повинні бути або в Javascript, або в мові, яка не надто відрізняється від Javascript (наприклад, Python). Усі відповіді будуть перетворені на Javascript для проведення змагань.
  • Переможець - це крапля, яка зібрала найбільшу кількість енергії в цілому за всі раунди (від гранул або споживання дрібних крапель, які не є родичами)

Контролер: https://gist.github.com/RedwolfPrograms/1facc0afe24c5dfd3ada8b8a2c493242

Чат: https://chat.stackexchange.com/rooms/93370/hungry-blobs-koth


1
Чи можете ви розширити це на інші мови, крім javascript?
Втілення

@EmbodimentofIgnorance Надішліть його будь-якою мовою, яку ви оберете, і я проведу перетворення на JS.
Програми

Чи можуть краплі перетинатися один з одним Напр .: blob1 в [0] [0] рухається праворуч, а blob2 в [0] [1] рухається вліво, або буде з'їдена крапля з меншою енергією?
fəˈnɛtɪk


@ fəˈnɛtɪk Так, боти можуть перетинати один одного. Також виклик, пов'язаний з цим, був моїм (:
Програми

Відповіді:


3

Інтроверт

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

Технічні деталі

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

function introvert(mapSize, vision, self, storage) {
  if (!storage.communal.friends)
    storage.communal.friends = {};
  if (!storage.communal.claims)
    storage.communal.claims = {};
  storage.communal.friends[self.uid] = true;
  for (var i in storage.communal.claims)
    if (storage.communal.claims[i] === self.uid) {
      storage.communal.claims = {};
      break;
    }
  var food = {};
  for (var p of vision.pellets) {
    var score = p.energy - taxiDist(p.pos, self.pos);
    if (score > 0)
      food[p.pos] = score;
  }
  var danger = {};
  for (var i = 0; i < mapSize; i++) {
    danger['-1,' + i] = true;
    danger[mapSize + ',' + i] = true;
    danger[i + ',' + mapSize] = true;
    danger[i + ',-1'] = true;
  }
  var relatives = {};
  for (var b of vision.blobs) {
    if (b.uid in storage.communal.friends) {
      relatives[b.pos] = true;
    } else if (!storage.self.justSplit && b.energy < self.energy - taxiDist(b.pos, self.pos) * 0.75) {
      var score = b.energy - taxiDist(b.pos, self.pos) * 1.25;
      if (score > 0)
        food[b.pos] = score;
    } else {
      danger[b.pos] = true;
      danger[b.pos[0] + ',' + (b.pos[1] - 1)] = true;
      danger[b.pos[0] + 1 + ',' + b.pos[1]] = true;
      danger[b.pos[0] + ',' + (b.pos[1] + 1)] = true;
      danger[b.pos[0] - 1 + ',' + b.pos[1]] = true;
    }
  }
  storage.self.justSplit = !danger[self.pos] && self.energy > 150;
  function fromData(n) {
    return n.split(',').map(s => parseInt(s));
  }
  function fs(f) {
    return food[f] / taxiDist(f, self.pos);
  }
  var target = Object.keys(food).filter(f => !(f in storage.communal.claims)).map(fromData).sort((a, b) => fs(b) - fs(a))[0];
  if (target)
    storage.communal.claims[target] = self.uid;
  function ms(m) {
    if (danger[m])
      return 99999999;
    var dists = Object.keys(relatives).map(r => hypotDist(fromData(r), m));
    return (target ? taxiDist(target, m) : 0) - (dists.length ? dists.reduce((a, b) => a + b) / dists.length : 0);
  }
  var candidates = [
    {p: self.pos},
    {p: [self.pos[0], self.pos[1] - 1], d: storage.self.justSplit ? SplitNorth : North},
    {p: [self.pos[0] + 1, self.pos[1]], d: storage.self.justSplit ? SplitEast : East},
    {p: [self.pos[0], self.pos[1] + 1], d: storage.self.justSplit ? SplitSouth : South},
    {p: [self.pos[0] - 1, self.pos[1]], d: storage.self.justSplit ? SplitWest : West}
  ];
  if (storage.self.justSplit)
    candidates.shift();
  return candidates.sort((a, b) => ms(a.p) - ms(b.p))[0].d;
}

Це виглядає як досить приємний бот! Конкурс має бути скоро (виграш закінчується завтра).
Програми

@RedwolfPrograms Я фактично перевірив це в бігуні, і він завжди виграє з досить великим відривом.
RamenChef

Середній бал за раунд: 357.544
Програми

1

Анімована їжа

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

function(map, near, me, storage) {
    var targs = near.pellets.map(el => taxiDist(el.pos, me.pos));
    var targ = near.pellets[targs.indexOf(Math.max(...targs))].pos;
    if (targ[0] == me.pos[0])
        return targ[1] < me.pos[1] ? North : South;
    return targ[0] < me.pos[0] ? West : East;
}

Середній бал за раунд: 24.933
програми

І, в дивовижну чергу подій, 5-лайнер (трохи модифікований, щоб зменшити помилки) виграє 2-е місце
програми

1

тестер bloblib

function(map, near, me, storage) {
    // BlobLib, the main purpose of this post
    const bloblib = {
        // Returns only pellets and blobs that are within the immediate neighbourhood (within 1 space of) me
        getNeighbours: (known) => {
            let neighbours = {};
            neighbours.pellets = known.pellets.filter(x => x.pos[0] >= me.pos[0] - 1 && x.pos[0] <= me.pos[0] + 1 && x.pos[1] >= me.pos[1] - 1 && x.pos[1] <= me.pos[1] + 1);
            neighbours.blobs = known.blobs.filter(x => x.pos[0] >= me.pos[0] - 1 && x.pos[0] <= me.pos[0] + 1 && x.pos[1] >= me.pos[1] - 1 && x.pos[1] <= me.pos[1] + 1);
            return neighbours;
        },
        // Gets the blob or pellet at the given location
        getByPos: (pos, known) => {
            let pellets = known.pellets.filter(x => x.pos[0] == pos[0] && x.pos[1] == pos[1]);
            let blobs = known.blobs.filter(x => x.pos[0] == pos[0] && x.pos[1] == pos[1]);
            if (blobs.length) return blobs[0];
            if (pellets.length) return pellets[0];
            return null;
        },
        // Returns a 2d array of size, containing any known blobs or pellets
        areaMatrix: (size, known) => {
            let matrix = [];
            for (let x = 0; x < size; x++) {
                let row = [];
                for (let y = 0; y < size; y++) {
                    let realPos = [me.pos[0] - (x + Math.floor(size / 2)), me.pos[1] - (y + Math.floor(size / 2))];
                    row.push(getByPos(realPos, known));
                }
                matrix.push(row);
            }
            return matrix;
        },
        // Gets a cardinal direction pointing from from to to
        cardDirTo: (to, from = me.pos) => {
            let diff = bloblib.multiDist(from, to);

            if (diff[0] == 0 && diff[1] == 0) return null;

            if (Math.abs(diff[0]) > Math.abs(diff[1])) {
                // Gunna be east or west
                return diff[0] > 0
                    ? East
                    : West;
            } else {
                return diff[1] > 0
                    ? South
                    : North;
            }
        },
        // Returns a vector of the X and Y distances between from and to
        multiDist: (from, to) => {
            return [to[0] - from[0], to[1] - from[1]]
        },
        // Gets the closest object in objs to position to
        getClosest: (objs, to = me.pos) => {
            if (!objs || !objs.length) return null;

            let sorted = objs.concat().sort((a, b) => taxiDist(a.pos, to) - taxiDist(b.pos, to));
            return sorted[0];
        },
        // Should be run at startup. Calculates which directions are unsafe to move in
        dangerSense: (origin) => {
            let neighbours = bloblib.getNeighbours(near);
            let matrix = bloblib.areaMatrix(3, neighbours);

            if (me.pos[1] == 0 || (matrix[1,0] && isThreat(matrix[1,0]))) bloblib.unsafeDirs.push(North);
            if (me.pos[0] == map - 1 || (matrix[2,1] && isThreat(matrix[2,1]))) bloblib.unsafeDirs.push(East);
            if (me.pos[0] == 0 || (matrix[0,1] && isThreat(matrix[0,1]))) bloblib.unsafeDirs.push(West);
            if (me.pos[1] == map - 1 || (matrix[1,2] && isThreat(matrix[1,2]))) bloblib.unsafeDirs.push(South);
        },
        isThreat: (blob) => {
            if (!blob.uid) return false;
            if (storage.communal.blobs.includes(blob.uid)) return true;

            return blob.energy >= me.energy - 1;
        }
        // Attempts to move in the given direction
        // Rotates the direction 90 if it can't safely move
        attemptMove: (dir = North) => {
            for (let i = 0; i < 4; i++) {
                if (bloblib.unsafeDirs.includes(dir)) dir = modDir(dir, i);
                else return dir;
            }
            return null;
        },
        // Attempts to split in the given direction
        // Rotates the direction 90 if it can't safely split
        attemptSplit: (dir = SplitNorth) => {
            for (let i = 0; i < 4; i++) {
                if (bloblib.unsafeDirs.includes(dir)) dir = modDir(dir, i);
                else return dir;
            }
            return null;
        },
        // Returns the next direction in which to move toward pos
        // Don't bother checking if we have enough energy, because if
        // we have < 1 energy we're basically dead anyway
        moveTo: (pos) => {
            return bloblib.performAction(bloblib.attemptMove(bloblib.cardDirTo(pos)));
        },
        // Simply registers the action in communal history, then returns it unmodified
        performAction: (action) => {
            storage.communal.history[me.uid].push(action);
            return action;
        },

        // Stores directions in which there is another blob
        // This wouldn't make sense to store across turns, so we don't bother
        unsafeDirs: []
    };
    bloblib.dangerSense(me.pos);

    // Register this blob
    if (!storage.communal.blobs) storage.communal.blobs = [];
    if (!storage.communal.blobs.includes(me.uid)) storage.communal.blobs.push(me.uid);

    // Register history for this blob
    if (!storage.communal.history) storage.communal.history = {};
    if (!storage.communal.history[me.uid]) storage.communal.history[me.uid] = [];

    // Split if we can and there are fewer than 10 blobs in our community
    if (me.energy > 150 && storage.communal.blobs.length < 10) {
        let split = bloblib.getSplit();
        if (split) return split;
    }

    // If we can't see any pellets or blobs, don't do anything
    if (!near.pellets.length && !near.blobs.length) return null;

    // Move toward the nearest pellet
    return bloblib.moveTo(bloblib.getClosest(near.pellets));
}

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

Коротше кажучи, цей бот робить наступне:

If energy > 150 and blobs_in_team < 10: Try to split
If visible_pellets = 0 and visible_blobs = 0: do nothing
Move toward the closest pellet in a safe way
    that avoids moving into other stronger or equal blobs
    or off the edge of the map

Тепер ви можете бачити енергію краплі, яка може стати в нагоді
програми

1
@RedwolfPrograms оновив bloblib, щоб визначити, чи є ворожі краплі "загрозою", виходячи з рівня енергії.
Скидсдев

Середній бал за раунд: 7.913
Програми

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

1

Жадібний боягуз

import random

def greedy_coward(map_length, near, me, storage):
    interesting_objects = [] #objects I can eat
    bad_objects = [] #objects that eat me
    allowed_directions = ["North", "East", "South", "West"]

    # add pellets to objects that I'm interested in
    for i in near.pellets:
        interesting_objects.append(i)

    # figure out which blobs are good and which are bad
    for i in near.blobs:
        # if I'm under or equal powered, add it to bad_objects
        if i.energy >= me.energy: 
            bad_objects.append(i)
        # if I can eat it, add it to interesting objects.
        else:
            interesting_objects.append(i)

    # if there are any bad objects, process them.
    if not len(bad_objects) == 0:

        # find the nearest bad object and make sure I don't move towards it
        bad_objects_distances = []
        for i in bad_objects:
            bad_objects_distances.append(taxiDist(i.pos, me.pos))
        worst_object = bad_objects[bad_objects_distances.index(min(bad_objects))]

        # find the direction of the worst object
        bad_object_xy_distance = [worst_object.pos[0] - me.pos[1], worst_object.pos[1] - me.pos[1]]
        closest_number = min(bad_object_xy_distance)
        bad_object_direction_vague = [["West","East"],["North","South"]][bad_object_xy_distance.index(closest_number)]
        if closest_number < 0:
            bad_object_direction = bad_object_direction_vague[1]
        else:
            bad_object_direction = bad_object_direction_vague[0]

        # remove bad object direction from allowed directions
        allowed_directions.remove(bad_object_direction)

    # process interesting objects if they exist
    if not len(interesting_objects) == 0:

        # find the nearest interesting object
        interesting_objects_distances = []
        for i in interesting_objects:
            interesting_objects_distances.append(taxiDist(me.pos, i.pos))
            interesting_object = interesting_objects[interesting_objects_distances.index(min(interesting_objects_distances))]

        # find the direction of the best object
            good_object_xy_distance = [interesrting_object.pos[0] - me.pos[1], interesting_object.pos[1] - me.pos[1]]
            closest_number = min(good_object_xy_distance)
            good_object_direction_vague = [["West","East"],["North","South"]][good_object_xy_distance.index(closest_number)]
            if closest_number < 0:
                good_object_direction = good_object_direction_vague[1]
            else:
                good_object_direction = good_object_direction_vague[0]

        # if the good and bad objects are in the same direction, move randomly in a different direction
        if good_object_direction == bad_object_direction:
            return random.choice(allowed_directions)
        else: # otherwise go towards the good object.
            return good_object_direction

    return 0 # when in doubt, stay still

Або в JavaScript,

function(map_length, near, me, storage) {
    var interesting_objects = []; //objects I can eat
    var bad_objects = []; //objects that eat me
    var allowed_directions = ["north", "east", "south", "west"];

    //add pellets to objects that I'm interested in
    for (let i in near.pellets) {
        interesting_objects.push(near.pellets[i]);
    }

    //figure out which blobs are good and which are bad
    for (let i in near.blobs) {
        //if I'm under or equal powered, add it to bad_objects
        if (near.blobs[i].energy >= me.energy) {
            bad_objects.push(near.blobs[i]);
        }
        //if I can eat it, add it to interesting objects.
        else {
            interesting_objects.push(near.blobs[i]);
        }
    }

    //if there are any bad objects, process them.
    if (bad_objects.length) {

        //find the nearest bad object and make sure I don't move towards it
        var bad_objects_distances = [];
        for (i in bad_objects) {
            bad_objects_distances.push(taxiDist(bad_objects[i].pos, me.pos));
        }
        var worst_object = bad_objects[bad_objects_distances.indexOf(Math.min(...bad_objects_distances))];

        //find the direction of the worst object
        var bad_object_xy_distance = [worst_object.pos[0] - me.pos[1], worst_object.pos[1] - me.pos[1]];
        var closest_number = Math.min(...bad_object_xy_distance.map(el => Math.abs(el)));
        var bad_object_direction_vague = [["west","east"],["north","south"]][bad_object_xy_distance.map(el => Math.abs(el)).indexOf(closest_number)];
        if (closest_number < 0) {
            var bad_object_direction = bad_object_direction_vague[1];
        } else {
            var bad_object_direction = bad_object_direction_vague[0];
        }

        //remove bad object direction from allowed directions
        allowed_directions = allowed_directions.filter(el => el !== bad_object_direction);

    }

    //process interesting objects if they exist
    if (interesting_objects.length) {

        //find the nearest interesting object
        var interesting_objects_distances = [];
        for (i in interesting_objects) {
            interesting_objects_distances.push(taxiDist(me.pos, interesting_objects[i].pos))
        }
        var interesting_object = interesting_objects[interesting_objects_distances.indexOf(Math.min(...interesting_objects_distances))];

        //find the direction of the best object
        var good_object_xy_distance = [interesting_object.pos[0] - me.pos[1], interesting_object.pos[1] - me.pos[1]];
        var closest_number = Math.min(...good_object_xy_distance.map(el => Math.abs(el)));
        var good_object_direction_vague = [["west","east"],["north","south"]][good_object_xy_distance.map(el => Math.abs(el)).indexOf(closest_number)];
        if (closest_number < 0) {
            var good_object_direction = good_object_direction_vague[1];
        } else {
            var good_object_direction = good_object_direction_vague[0];
        }

        //if the good and bad objects are in the same direction, move randomly in a different direction
        if (good_object_direction == bad_object_direction) {
            return allowed_directions[allowed_directions.length * Math.random() | 0];
        } else{ //otherwise go towards the good object.
            return good_object_direction;
        }

    }

    return 0; //when in doubt, stay still
}

Цей бот не дуже цікавий. Він діє за двома пріоритетами:

  1. Не їжте.
  2. Їжте найближчу річ.

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


Я перейду до роботи з перекладом цього! Коли я закінчу, запропоную редагувати версію JS.
Програми

@RedwolfPrograms Звучить добре, дуже дякую.
Товариш СпарклПоні

Я думаю, вам потрібно додати if / else, щоб перевірити, чи є насправді хороші / погані об’єкти. Це викликає кілька проблем у версії JS.
Програми

@RedwolfPrograms Це слід виправити зараз. Я щойно додав оператор if, який перевіряє створені списки цікавих і поганих об'єктів, щоб переконатися, що вони не порожні. Ще раз дякую за допомогу.
Товариш SparklePony

@RedwolfPrograms У вас є готова версія JS?
RamenChef

1

SafetyBlob

Цей бот використовує таку ж логіку, що і Safetycoin з попереднього KOTH.

Як це функціонує

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

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

 function SafetyBlob(map,local,me,stor){
  var center=(map/2|0)+1;
  var [x,y]=me.pos
  var uid=me.uid
  var others=local.blobs;
  var pellets=local.pellets;
  //Bot doesnt use storage because it just tries to find what it can.
  var willSplit=me.energy>150;
  var bestSafePelletValue=0;
  var bestSafePellet=null;
  var pellet;
  var other;
  //Head towards the best valued pellet (energy/distance) which can be reached before any larger or equal sized blobs or can be reached at the same time as smaller blobs
  for(i=0;i<pellets.length;i++){
    pellet=pellets[i]
    if(bestSafePelletValue<=pellet.energy/taxiDist(pellet.pos,me.pos)){
      for(j=0;j<others.length;j++){
        other=others[j];
        if(other.energy<me.energy){
          if(taxiDist(pellet.pos,me.pos)<=taxiDist(other.pos,pellet.pos)){
            if(taxiDist(pellet.pos,me.pos)<taxiDist(bestSafePellet.pos,me.pos)){
              bestSafePellet=pellet;
              bestSafePelletValue=pellet.energy/taxiDist(pellet.pos,me.pos);
            }
          }
        }
        if(other.energy>=me.energy){
          if(taxiDist(pellet.pos,me.pos)<taxiDist(other.pos,pellet.pos)){
            if(taxiDist(pellet.pos,me.pos)<taxiDist(bestSafePellet.pos,me.pos)){
              bestSafePellet=pellet;
              bestSafePelletValue=pellet.energy/taxiDist(pellet.pos,me.pos);
            }
          }
        }
      }
    }
  }

  if(bestSafePellet){
    [xPellet,yPellet]=bestSafePellet.pos;
    if(x<xPellet&&Math.abs(x-xPellet)>=Math.abs(y-yPellet)){
      return East;
    }
    if(x<xPellet&&Math.abs(x-xPellet)>=Math.abs(y-yPellet)){
      return West;
    }
    if(y<yPellet&&Math.abs(x-xPellet)<Math.abs(y-yPellet)){
      return South;
    }
    if(y<yPellet&&Math.abs(x-xPellet)<Math.abs(y-yPellet)){
      return North;
    }
  }
  
  var validMoves=["North","East","South","West","Stay"];
  var removeIndex=0;
  var safeEnergy;
  if(x==0)
    validMoves.splice(validMoves.indexOf("West"));
  if(x==map)
    validMoves.splice(validMoves.indexOf("East"));
  if(y==0)
    validMoves.splice(validMoves.indexOf("North"));
  if(y==map)
    validMoves.splice(validMoves.indexOf("South"));

  var possibleMoves=[...validMoves];
  possibleMoves.splice(possibleMoves.indexOf("Stay"));
  //If there is no safe pellet try to stick somewhat towards the middle
  //Ignore enemies unless at 2 distance from self and there is no safe pellet
  for(i=0;i<others.length;i++){
    other=others[i];
    safeEnergy=willSplit?(me.energy-50)/2:me.energy;
    if((other.energy>=safeEnergy)&&(taxiDist(me.pos,other.pos)<=2)){
      if(taxiDist(me.pos,other.pos)==1){
        if((removeIndex=validMoves.indexOf("Stay"))>=0){
          validMoves.splice(removeIndex,1)
        }
      }
      if(other.pos[0]<x){
        if((removeIndex=validMoves.indexOf("West"))>=0){
          validMoves.splice(removeIndex,1)
        }
      }
      if(other.pos[1]<y){
        if((removeIndex=validMoves.indexOf("South"))>=0){
          validMoves.splice(removeIndex,1)
        }
      }
      if(other.pos[0]>x){
        if((removeIndex=validMoves.indexOf("East"))>=0){
          validMoves.splice(removeIndex,1)
        }
      }
      if(other.pos[1]>y){
        if((removeIndex=validMoves.indexOf("North"))>=0){
          validMoves.splice(removeIndex,1)
        }
      }
    }
  }
  //If there are no safe moves move in a random direction (Reduce energy as much as possible with a slight chance of survival)
  if(!validMoves.length){
    switch (possibleMoves[Math.random()*possibleMoves.length|0]){
      case "North":
        return North;
      case "South":
        return South;
      case "East":
        return East;
      case "West":
        return West;
    }
  }
  //If there are safe moves bias towards moving towards the center block of 1/3 of the way from the sides
  if(!willSplit){
    //bias moving towards near the center
    biasedMoves=[];
    for(var i=0;i<validMoves.length;i++){
      switch(validMoves[i]){
        case "North":
          biasedMoves=biasedMoves.concat(y>center?"0".repeat(center/3|0).split``:"0".repeat(y-center).split``);
          break;
        case "South":
          biasedMoves=biasedMoves.concat(y<center?"2".repeat(center/3|0).split``:"2".repeat(center-y).split``);
          break;
        case "East":
          biasedMoves=biasedMoves.concat(y>center?"1".repeat(center/3|0).split``:"1".repeat(x-center).split``);
          break;
        case "West":
          biasedMoves=biasedMoves.concat(y<center?"3".repeat(center/3|0).split``:"3".repeat(center-x).split``);
          break;
        case "Stay":
          biasedMoves=biasedMoves.concat(["4"]);
          break;
      }
    }
  }
  if(willSplit){
    switch (biasedMoves[Math.random()*biasedMoves.length|0]){
      case "0":
        return SplitNorth;
      case "2":
        return SplitSouth;
      case "1":
        return SplitEast;
      case "3":
        return SplitWest;
      case "4":
        return Stay;
    }
  }
  else{
    switch (biasedMoves[Math.random()*biasedMoves.length|0]){
      case "0":
        return North;
      case "2":
        return South;
      case "1":
        return East;
      case "3":
        return West;
      case "4":
        return Stay;
    }
  }
}

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

@RedwolfPrograms Метою було не виграти щедрості.
fəˈnɛtɪk

Я знаю, просто переконуюсь, що ви знаєте (:
Програми
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.