Голодные капли KoTH

9

Конкурс завершен! Читайте комментарии на блобах, чтобы посмотреть их счет.

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

Энергия и Движение

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

принимать пищу

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

Обнаружение и информация

Капли могут видеть любые шарики или другие шарики на расстоянии 4 единиц. Когда их функции вызываются, BLOB-объекты предоставляются:

  • Длина стороны карты
  • Положение капли на карте
  • Позиции всех гранул в пределах их радиуса поиска, а также их значения
  • Позиции всех BLOB-объектов в радиусе их поиска, а также их энергия и UID
  • Энергия, UID и местоположение большого двоичного объекта, функция которого выполняется
  • Объект хранения, уникальный для BLOB-объекта
  • Объект хранения, общий для всех BLOB-объектов, связанных с BLOB-объектами, путем разделения

расщепляющий

Если у сгустка больше 50 энергии, он может разделиться. Расщепление стоит 50 энергии, а любая оставшаяся энергия делится поровну между двумя каплями. Все капли являются либо оригиналами, либо разделенными копиями, причем каждая копия восходит к оригиналу. Все это вместе "родственники". Все родственники имеют один объект общего хранения. Родственники могут поедать друг друга и могут разделяться, использовать свои собственные объекты хранения или собирать энергию, не влияя на других.

Передача энергии

Если два шарика находятся рядом друг с другом (после перемещения), один из ботов может передавать энергию другому. Это делается путем возвращения SendNorth(amt), SendEast(amt), SendSouth(amt)или SendWest(amt), с amtбыть число , представляющее сумму отправки. Это может быть любая сумма, которую может позволить отправитель, включая всю его энергию. Рекомендуется, чтобы блоб, получающий энергию, велел оставаться неподвижным через коммунальное хранилище, чтобы он не уходил при передаче энергии (хотя в этом случае энергия не будет вычтена из общей суммы отправителя).

Функции, хранилище и UID

Для обеспечения более сложного поведения при обучении всем BLOB-объектам будет присвоен целочисленный 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
  • To Move: вернуть одну из четырех глобальных переменных: север, восток, юг или запад, которые равны «север», «восток», «юг» или «запад» (которые также могут использоваться в качестве возвращаемого значения)
  • To Split: вернуть глобальную переменную SplitNorth, SplitEast, SplitSouth или SplitWest, направление, указывающее, куда поместить новый BLOB-объект

Если возвращается команда разделения и требуемое количество энергии больше или равно энергии BLOB-объекта, ничего не произойдет. Капли не смогут покинуть карту.

Предопределенные библиотечные функции

Есть несколько основных функций, доступных по умолчанию, чтобы сэкономить время:

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))];
}

правила

  • Стандартные лазейки запрещены. Также нет нестандартных лазеек.
  • Ни один BLOB-объект не может пытаться изменить или прочитать любые данные, не переданные ему через его параметры.
  • Ни один BLOB-объект не может пытаться изменить переменную возвращаемого значения, чтобы саботировать другие BLOB-объекты.
  • Раунд длится до тех пор, пока единственные оставшиеся капли не станут родственниками
  • Ни один BLOB-объект не может изменять данные, вводя функции в свои параметры, которые изменяют значения с помощью thisключевого слова.
  • Все материалы должны быть либо на Javascript, либо на языке, который не слишком отличается от Javascript (например, Python). Все ответы будут преобразованы в Javascript для конкурса.
  • Победителем является капля, которая собрала наибольшее количество энергии за все раунды (либо от гранул, либо от использования мелких капель, которые не являются родственниками)

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

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

Redwolf Программы
источник
1
Можете ли вы распространить это на другие языки, кроме JavaScript?
Воплощение невежества
@EmbodimentofIgnorance Отправьте его на любом языке по вашему выбору, и я сделаю преобразование в JS.
Программы
Могут ли капли пересекаться друг с другом, например: blob1 в [0] [0] перемещается вправо, а blob2 в [0] [1] перемещается влево или будет съеден шарик с меньшей энергией?
fəˈnɛtɪk
Связанные
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;
}
RamenChef
источник
Это похоже на довольно хороший бот! Конкурс должен быть в ближайшее время (награда истекает завтра).
Программы
@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;
}
Muffinz72
источник
Средний балл за раунд: 24,933
Программы
И, в удивительном повороте событий, 5-лайнер (слегка измененный, чтобы уменьшить количество ошибок) выигрывает 2-е
Программы
1

тестер блоблиб

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
Skidsdev
источник
Теперь вы можете увидеть энергию капли, которая может пригодиться
Программы
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 Звучит хорошо, большое спасибо.
Товарищ SparklePony
Я думаю, вам нужно добавить 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;
    }
  }
}
fənɛtɪk
источник
Я уже запустил контроллер, но я мог бы сделать это позже с этим новым ботом. Слишком поздно переназначать награду, если она выиграет, но мне любопытно узнать результат.
Программы
@RedwolfPrograms Цель не состояла в том, чтобы выиграть награду.
fəˈnɛtɪk
Я знаю, просто убедившись, что вы знаете (:
Программы