Hungry Blobs KoTH

9

Concurso concluído! Leia os comentários nos blobs para ver a pontuação deles.

Este KoTH é inspirado livremente pela simulação de seleção natural da Primer . Seu bot é um blob. Para sobreviver, você deve comer pellets para recuperar a energia, que é usada para se mover. Com energia extra, os blobs podem se dividir em dois.

Energia e Movimento

Seu blob começa a cada rodada com 100 de energia e não tem limite na quantidade de energia que pode coletar. Cada rodada é executada em turnos, com cada blob tendo a opção de mover para o norte, leste, sul ou oeste em qualquer turno ou ficar parada. Mover usa 1 energia e ficar parado usa 0,25 energia. O comprimento lateral do mapa éceil(0.25 * blobCount) * 2 - 1unidades, com um mínimo de 9 unidades. Todos os blobs começam na borda do mapa, com um colocado em cada canto e cada blob subseqüente sendo colocado a 2 unidades de qualquer outro. A cada 30 turnos, uma onda de pellets é colocada em pontos aleatórios ao redor do mapa, a pelo menos 1 unidade de qualquer borda. Cada vez que uma onda de pellets aparece, a quantidade de pellets (originalmente duas vezes o número de blobs ou a largura do mapa, o que for maior) na próxima onda é reduzida em 1, forçando o número de blobs a diminuir ao longo do tempo. Cada pellet restaura entre 5 e 15 energia. Quando a energia de um blob é menor ou igual a 0, ela morre.

Comendo

Se dois ou mais blobs tentarem ocupar o mesmo local, aquele com mais energia consumirá os outros, recebendo sua energia. Se ambos têm energia igual, ambos desaparecem.

Detecção e Informação

Os blobs podem ver pellets ou outros blobs a uma distância de 4 unidades. Quando suas funções são chamadas, os blobs são fornecidos com:

  • O comprimento lateral do mapa
  • A posição do blob no mapa
  • As posições de todos os pellets dentro do raio de pesquisa e os valores
  • As posições de todos os blobs dentro do raio de pesquisa, assim como a energia e os UIDs
  • A energia, o UID e os locais do blob cuja função está sendo executada
  • Um objeto de armazenamento exclusivo para o blob
  • Um objeto de armazenamento compartilhado por todos os blobs relacionados ao blob através da divisão

Divisão

Se um blob tiver mais de 50 de energia, poderá optar por dividir. A divisão custa 50 de energia e a energia restante é dividida igualmente entre os dois blobs. Todos os blobs são originais ou cópias divididas, com cada cópia retornando a um original. Todos esses juntos são "parentes". Todos os parentes têm um objeto de armazenamento comum. Os parentes ainda podem comer um ao outro e podem se dividir, usar seu próprio objeto de armazenamento ou coletar energia sem afetar os outros.

Transferencia de energia

Se dois blobs estiverem próximos um do outro (após o movimento), um dos bots pode transferir energia para o outro. Isto é feito através da devolução SendNorth(amt), SendEast(amt), SendSouth(amt), ou SendWest(amt), com amtsendo um número que representa a quantidade enviada. Pode ser qualquer quantia que o remetente possa pagar, incluindo toda a sua energia. Recomenda-se que o blob que está recebendo energia fique parado durante o armazenamento comunitário, para que ele não se afaste quando a energia estiver sendo transferida (embora a energia não seja deduzida do total do remetente nesse caso).

Funções, armazenamento e UIDs

Para permitir comportamentos de aprendizado mais complexos, todos os blobs receberão um UID inteiro (Identificador Único). Esses UIDs serão gerados aleatoriamente a cada mapa, impedindo estratégias baseadas em destinos individuais. Quando a função de um blob é chamada, são transmitidos quatro argumentos:

  1. O comprimento lateral do mapa como um número inteiro
  2. Um objeto com duas matrizes:, pelletse blobs. Ambas as matrizes contêm objetos, ambos com uma pospropriedade contendo a posição do pellet ou blob formatada como [x,y]. Os pellets terão uma energypropriedade, enquanto os blobs terão uma uidpropriedade e uma energypropriedade
  3. Um objecto contendo várias propriedades da gota que é passado para: energy, uid, e pos. A posmatriz está formatada como[x,y]
  4. Um objeto que contém os dois objetos de armazenamento do blob. Uma selfpropriedade contém um objeto de armazenamento individual que pode ser modificado da maneira que o blob considerar adequado (manipulando propriedades do objeto que é passado) e uma communalpropriedade que pode ser modificada por qualquer parente.

Os blobs não são movidos imediatamente para evitar que as curvas anteriores / posteriores tenham uma vantagem. Todos os movimentos são processados ​​em grupos. energia independente de se isso traria sua energia total acima de 0.

Para que os blobs relativos se reconheçam, o armazenamento comum deve ser usado para que cada blob registre seu UID em uma matriz ou através de algum outro sistema.

Retornar valores

Para mover ou dividir, o valor de retorno da função é usado. Primeiro, o significado das direções cardinais em termos de coordenadas:

  • Norte = -Y
  • Leste = + X
  • Sul = + Y
  • Oeste = -X

Observe que [0,0]é o canto superior esquerdo e Y aumenta à medida que você desce. O valor de retorno da função deve seguir estas regras:

  • Para não fazer nada: não retorne nada, 0, nulo, indefinido, falso ou qualquer outro valor que seja igual a falso
  • Para mover: retorne uma das quatro variáveis ​​globais: norte, leste, sul ou oeste, que equivale a "norte", "leste", "sul" ou "oeste" (que também pode ser usado como valor de retorno)
  • Para dividir: retorne a variável global SplitNorth, SplitEast, SplitSouth ou SplitWest, a direção que indica onde colocar o novo blob

Se um comando de divisão for retornado e a quantidade de energia necessária for maior ou igual à energia do blob, nada acontecerá. Os blobs não poderão sair do mapa.

Funções de biblioteca predefinidas

Existem algumas funções básicas disponíveis por padrão, para economizar tempo:

taxiDist (pt1, pt2)

Retorna a distância do táxi entre dois pontos (distância X mais distância 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)

Retorna a distância entre dois pontos de acordo com o teorema de Pitágoras

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)

Toma a direção inserida, gira 90 graus no sentido amthorário e retorna o novo valor.

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

Blob de exemplo

Este blob não se moverá até encontrar um pellet próximo. Então, ele se moverá na direção que achar mais provável de recompensá-lo. Se sua energia estiver sempre acima de 150, ela se dividirá.

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

Regras

  • As brechas padrão são proibidas. Além disso, não há brechas incomuns.
  • Nenhum blob pode tentar modificar ou ler quaisquer dados que não foram passados ​​para ele através de seus parâmetros
  • Nenhum blob pode tentar modificar uma variável de valor de retorno para sabotar outros blobs
  • Uma rodada dura até que os únicos blobs restantes sejam parentes
  • Nenhum blob pode modificar dados injetando funções em seus parâmetros que modificam valores usando a thispalavra - chave
  • Todos os envios devem estar em Javascript ou em um idioma que não seja muito diferente do Javascript (Python, por exemplo). Todas as respostas serão convertidas em Javascript para a competição.
  • O vencedor é o blob que coletou a maior quantidade de energia no total em todas as rodadas (de bolinhas ou consumindo blobs menores que não são parentes)

Controlador: https://gist.github.com/RedwolfPrograms/1facc0afe24c5dfd3ada8b8a2c493242

Chatroom: https://chat.stackexchange.com/rooms/93370/hungry-blobs-koth

Programas Redwolf
fonte
11
Você pode expandir isso para outros idiomas além do javascript?
Modalidade de ignorância
@EmbodimentofIgnorance Envie-o no idioma que você escolher, e eu farei a conversão para JS.
Programas Redwolf
Os blobs podem se cruzar Ex: blob1 em [0] [0] se move para a direita e blob2 em [0] [1] se move para a esquerda ou o blob com menor energia será comido?
f Maynɛtɪk 10/05/19
Relacionado
fəˈnɛtɪk # 10/19
@ fəˈnɛtɪk Sim, os bots podem se cruzar. Além disso, o desafio relacionado foi o meu (:
Programas Redwolf

Respostas:

3

Introvertido

O Introvertido não gosta de outras bolhas. Quando vê um borrão não relacionado, come-o, se puder, e, relutantemente, aceita sua presença, se não pode, embora foge se vê sinais de agressão. Quando vê um blob relacionado , ele se distancia. No entanto, não pode deixar de dividir muito.

Detalhes técnicos

O principal recurso desse blob é separar e espalhar para maximizar a visão combinada dos blobs. Também emprega um sistema para impedir que dois deles competam por um pellet.

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
fonte
Parece um bom bot! O concurso deve ser breve (a recompensa expira amanhã).
Programas Redwolf
@RedwolfPrograms Na verdade, testei no corredor e ele sempre vence por uma margem bastante grande.
RamenChef 15/05/19
Pontuação média por rodada: 357.544
Programas Redwolf
1

Refeição Animada

Um bot simples, apenas para começar a competição. Encontra a moeda mais próxima e vai em sua direção. Baseado no bot de exemplo.

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
fonte
Pontuação média por rodada: 24.933
Programas Redwolf
E, em uma surpreendente reviravolta dos eventos, o 5-liner (modificado levemente para reduzir bugs) vence o 2º
Redwolf Programs
1

testador de 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));
}

O bot real é bastante simples, mas isso é mais projetado como uma prova de conceito bloblib, uma coleção de funções e funcionalidades que planejo usar e desenvolver em outros bots (fique à vontade para usar / expandir também)

Em resumo, este bot faz o seguinte:

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
fonte
Agora você pode ver a energia de uma bolha, que pode ser útil
Programas Redwolf
11
O @RedwolfPrograms bloblib atualizado para determinar se os blobs inimigos são uma "ameaça" com base em seus níveis de energia.
Skidsdev 8/05/19
Pontuação média por rodada: 7.913
Programas Redwolf
Provavelmente, esse sistema poderia ter sido usado para obter bons blobs, mas este parecia agir um pouco estranhamente.
Programas Redwolf
1

Covarde ganancioso

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

Ou, em 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
}

Este bot não é muito interessante. Ele atua de acordo com duas prioridades:

  1. Não seja comido.
  2. Coma a coisa mais próxima.

Ele nunca cospe para maximizar sua capacidade de comer outras coisas.

Camarada SparklePony
fonte
Vou trabalhar na tradução disso! Quando terminar, sugerirei uma edição com a versão JS.
Programas Redwolf
@RedwolfPrograms Parece bom, muito obrigado.
Camarada SparklePony
Eu acho que você precisa adicionar um if / else para verificar se realmente existem objetos bons / ruins. Está causando vários problemas na versão JS.
Programas Redwolf
@RedwolfPrograms Deve ser corrigido agora. Acabei de adicionar uma instrução if que verifica as listas criadas de objetos interessantes e ruins para garantir que eles não estejam vazios. Mais uma vez, obrigado pela ajuda.
Camarada SparklePony
@RedwolfPrograms Você tem a versão JS pronta?
RamenChef 15/05/19
1

SafetyBlob

Este bot usa algumas das mesmas lógicas do Safetycoin do KOTH anterior.

Como funciona

Esse bot seguirá em direção a comida que pode ser alcançada antes de qualquer robô maior ou ao mesmo tempo / antes de um robô menor. Se não conseguir ver nenhum alimento que atenda a esses critérios, ele se moverá em uma direção aleatória (inclinado em direção ao centro). Se atingir 150 de energia e não conseguir ver alimentos seguros, ele se dividirá em uma das direções que rotulou como seguras de se mover.

Este bot não controla seus próprios filhos, mas eles não devem colidir de qualquer maneira devido aos mecanismos de segurança.

 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
fonte
Já executei o controlador, mas posso fazê-lo novamente mais tarde com este novo bot. É tarde demais para reatribuir a recompensa, se vencer, mas estou curioso sobre o resultado.
Programas Redwolf
@RedwolfPrograms O objetivo não era ganhar a recompensa.
fəˈnɛtɪk 22/05/19
Eu sei, apenas certificando-me de que você saiba (:
Programas Redwolf