Blobs hambrientos KoTH

9

Concurso terminado! Lea los comentarios en blobs para ver su puntuación.

Este KoTH está ligeramente inspirado en la simulación de selección natural de Primer . Tu bot es una gota. Para sobrevivir, debes comer gránulos para recuperar energía, que se usa para moverte. Con energía extra, las gotas se pueden dividir en dos.

Energía y movimiento

Tu gota comienza cada ronda con 100 de energía, y no tiene límite en la cantidad de energía que puede recolectar. Cada ronda se ejecuta por turnos, y cada burbuja tiene la opción de moverse hacia el norte, este, sur u oeste en cualquier turno o quedarse quieto. Moverse usa 1 energía, y quedarse quieto usa 0.25 energía. La longitud del lado del mapa esceil(0.25 * blobCount) * 2 - 1unidades, con un mínimo de 9 unidades. Todos los blobs comienzan en el borde del mapa, con uno colocado en cada esquina y cada blob posterior colocado a 2 unidades de distancia de cualquier otro. Cada 30 turnos, se coloca una ola de gránulos en puntos aleatorios alrededor del mapa, al menos a 1 unidad de cualquier borde. Cada vez que aparece una ola de gránulos, la cantidad de gránulos (originalmente el doble del número de manchas o el ancho del mapa, lo que sea mayor) en la siguiente ola disminuye en 1, lo que obliga a que el número de gotas disminuya con el tiempo. Cada pellet restaura entre 5 y 15 energías. Cuando la energía de una gota es menor o igual a 0, muere.

Comiendo

Si dos o más gotas intentan ocupar el mismo lugar, el que tenga más energía se comerá a los demás, recibiendo su energía. Si ambos tienen la misma energía, ambos desaparecen.

Detección e información

Los blobs pueden ver cualquier bolita u otros blobs dentro de una distancia de 4 unidades. Cuando se llaman sus funciones, los blobs cuentan con:

  • La longitud lateral del mapa.
  • La posición de la burbuja en el mapa.
  • Las posiciones de todos los gránulos dentro de su radio de búsqueda, así como sus valores.
  • Las posiciones de todos los blobs dentro de su radio de búsqueda, así como su energía y UID
  • La energía, el UID y las ubicaciones del blob cuya función se está ejecutando
  • Un objeto de almacenamiento exclusivo del blob.
  • Un objeto de almacenamiento compartido por todos los blobs relacionados con el blob mediante la división

Terrible

Si una gota tiene más de 50 energías, puede optar por dividirse. La división cuesta 50 de energía, y cualquier energía restante se divide de manera uniforme entre las dos gotas. Todos los blobs son originales o copias divididas, y cada copia se remonta a un original. Todos estos juntos son "parientes". Todos los familiares tienen un objeto de almacenamiento comunitario. Los familiares aún pueden comerse unos a otros, y pueden dividirse, usar su propio objeto de almacenamiento o recolectar energía sin afectar a los demás.

Transferencia de energía

Si dos blobs están uno al lado del otro (después de moverse), uno de los bots puede transferir energía al otro. Esto se hace mediante la devolución SendNorth(amt), SendEast(amt), SendSouth(amt), o SendWest(amt), con amtser un número que representa la cantidad enviada. Esto puede ser cualquier cantidad que el remitente pueda pagar, incluida toda su energía. Se recomienda que se le diga al blob que está recibiendo energía que permanezca quieto a través del almacenamiento comunitario, para que no se aleje cuando se transfiere la energía (aunque la energía no se deduciría del total del remitente en este caso).

Funciones, almacenamiento y UID

Para permitir comportamientos de aprendizaje más complejos, todos los blobs recibirán un UID entero (Identificador único). Estos UID se generarán aleatoriamente en cada mapa, evitando estrategias basadas en objetivos individuales. Cuando se llama a la función de un blob, se le pasan cuatro argumentos:

  1. La longitud lateral del mapa como un entero
  2. Un objeto con dos matrices: pelletsy blobs. Ambas matrices contienen objetos, tanto que tiene una pospropiedad que contiene el sedimento o la posición de blob formateado como [x,y]. Los pellets tendrán una energypropiedad, mientras que los blobs tendrán una uidpropiedad y una energypropiedad
  3. Un objeto que contiene varias propiedades de la burbuja se pasa a: energy, uid, y pos. La posmatriz está formateada como[x,y]
  4. Un objeto que contiene los dos objetos de almacenamiento del blob. Una selfpropiedad contiene un objeto de almacenamiento individual que puede modificarse como el blob lo considere apropiado (manipulando las propiedades del objeto que se pasa) y una communalpropiedad que puede modificar cualquier pariente.

Los blobs no se mueven inmediatamente para evitar que los turnos anteriores / posteriores tengan una ventaja. Todos los movimientos se procesan en grupos (todas las colisiones / alimentación, luego todos los gránulos, luego se dividen, etc.) Si una gota cae sobre una bolita o una bolita más pequeña y, en el proceso usa su última energía, la burbuja aún consumirá la bolita / energía independiente de si eso llevaría su energía total por encima de 0.

Para que los blobs relativos se reconozcan entre sí, se debe usar el almacenamiento comunal para que cada blob registre su UID en una matriz o mediante algún otro sistema.

Valores de retorno

Para mover o dividir, se utiliza el valor de retorno de la función. Primero, el significado de las direcciones cardinales en términos de coordenadas:

  • Norte = -Y
  • Este = + X
  • Sur = + Y
  • Oeste = -X

Tenga en cuenta que [0,0]es la esquina superior izquierda , e Y aumenta a medida que baja. El valor de retorno de la función debe seguir estas reglas:

  • Para no hacer nada: no devuelve nada, 0, nulo, indefinido, falso o cualquier otro valor que equivalga a falso
  • Para mover: Devuelva una de las cuatro variables globales: Norte, Este, Sur u Oeste, que equivalen a "norte", "este", "sur" u "oeste" (que también podría usarse como valor de retorno)
  • Para dividir: Devuelve la variable global SplitNorth, SplitEast, SplitSouth o SplitWest, la dirección que indica dónde colocar el nuevo blob

Si se devuelve un comando dividido y la cantidad de energía requerida es mayor o igual que la energía del blob, no sucederá nada. Los blobs no podrán abandonar el mapa.

Funciones de biblioteca predefinidas

Hay algunas funciones básicas disponibles por defecto, para ahorrar algo de tiempo:

taxiDist (pt1, pt2)

Devuelve la distancia del taxi entre dos puntos (distancia X más distancia 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)

Devuelve la distancia entre dos puntos de acuerdo con el 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 la dirección ingresada, gira 90 grados en el sentido de las agujas del reloj amt, luego devuelve el nuevo valor.

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

Blob de ejemplo

Esta burbuja no se moverá hasta que encuentre un gránulo cerca. Luego, se moverá en la dirección que cree que es más probable que lo recompense. Si su energía es alguna vez superior a 150, 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))];
}

Reglas

  • Las lagunas estándar están prohibidas. Además, no hay lagunas no estándar.
  • Ningún blob puede intentar modificar o leer los datos que no se le pasan a través de sus parámetros
  • Ningún blob puede intentar modificar una variable de valor de retorno para sabotear otros blobs
  • Una ronda dura hasta que las únicas gotas restantes son parientes
  • Ningún blob puede modificar datos mediante la inyección de funciones en sus parámetros que modifican valores utilizando la thispalabra clave
  • Todos los envíos deben estar en Javascript o en un lenguaje que no sea muy diferente de Javascript (Python, por ejemplo). Todas las respuestas se convertirán a Javascript para la competencia.
  • El ganador es la burbuja que ha acumulado la mayor cantidad de energía en total en todas las rondas (ya sea de pellets o de consumir gotas más pequeñas que no son parientes)

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

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

Programas Redwolf
fuente
1
¿Puedes expandir esto a otros idiomas además de javascript?
Encarnación de la ignorancia
@EmbodimentofIgnorance Envíalo en el idioma que elijas, y haré la conversión a JS.
Programas Redwolf
¿Pueden los blobs cruzarse entre sí Ej: blob1 en [0] [0] se mueve hacia la derecha y blob2 en [0] [1] se mueve hacia la izquierda o se comerá el blob con menor energía?
fəˈnɛtɪk
Relacionado
fəˈnɛtɪk
@ fəˈnɛtɪk Sí, los bots pueden cruzarse entre sí. Además, el desafío relacionado era mío (:
Programas Redwolf

Respuestas:

3

Introvertido

Al introvertido no le gustan otros blobs. Cuando ve una gota no relacionada, la come si puede, y acepta a regañadientes su presencia si no puede, pero huye si ve signos de agresión. Cuando ve un blob relacionado , se distancia. Sin embargo, no puede evitar separarse mucho.

Detalles técnicos

La característica principal de este blob es separarse y extenderse para maximizar la visión combinada de los blobs. También emplea un sistema para evitar que dos de ellos compitan por una pastilla.

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
fuente
¡Parece un bot bastante bonito! El concurso debe ser pronto (la recompensa vence mañana).
Programas Redwolf
@RedwolfPrograms Realmente lo probé en el corredor y siempre gana por un margen bastante grande.
RamenChef
Puntaje promedio por ronda: 357.544
Programas Redwolf
1

Comida animada

Un bot simple, solo para comenzar la competencia. Encuentra la moneda más cercana y se dirige hacia ella. Basado en el ejemplo bot.

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
fuente
Puntaje promedio por ronda: 24.933
Programas Redwolf
Y, en un sorprendente giro de los acontecimientos, el 5-liner (modificado ligeramente para reducir errores) gana el 2 °
Programas Redwolf
1

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

El bot real es bastante simple, pero está más diseñado como una prueba de concepto bloblib, una colección de funciones y funcionalidades que planeo usar y desarrollar en otros bots (siéntase libre de usarlo / expandirlo también)

En resumen, este bot hace lo siguiente:

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
fuente
Ahora puede ver la energía de una gota, que podría ser útil
Redwolf Programs
1
@RedwolfPrograms actualizó bloblib para determinar si los blobs enemigos son una "amenaza" en función de sus niveles de energía.
Skidsdev
Puntaje promedio por ronda: 7.913
Programas Redwolf
Este sistema probablemente podría haber sido usado para algunos buenos blobs, pero este parecía actuar un poco extraño.
Programas Redwolf
1

Codicioso cobarde

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

O, en 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 no es muy interesante. Actúa de acuerdo con dos prioridades:

  1. No te coman.
  2. Come lo más cercano.

Nunca escupe para maximizar su capacidad de comer otras cosas.

Camarada SparklePony
fuente
¡Me pondré a trabajar traduciendo esto! Cuando termine, sugeriré una edición con la versión JS.
Programas Redwolf
@RedwolfPrograms Suena bien, muchas gracias.
Camarada SparklePony
Creo que necesita agregar un if / else para verificar si realmente hay objetos buenos / malos. Está causando varios problemas en la versión JS.
Programas Redwolf
@RedwolfPrograms Debería arreglarse ahora. Acabo de agregar una declaración if que verifica las listas creadas de objetos interesantes y malos para asegurarse de que no estén vacías. De nuevo, gracias por la ayuda.
Camarada SparklePony
@RedwolfPrograms ¿Tienes lista la versión JS?
RamenChef
1

SafetyBlob

Este bot utiliza parte de la misma lógica que Safetycoin del KOTH anterior.

Como funciona

Este bot se dirigirá hacia la comida que puede alcanzar antes de que lo hagan los bots más grandes o al mismo tiempo / antes que un bot más pequeño. Si no puede ver ningún alimento que cumpla con estos criterios, se moverá en una dirección aleatoria (sesgada hacia el centro). Si llega a 150 de energía y no puede ver alimentos seguros, se dividirá en una de las direcciones que ha etiquetado como seguras para moverse.

Este bot no rastrea a sus propios hijos, pero no deben colisionar de todos modos debido a los mecanismos de seguridad.

 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
fuente
Ya he ejecutado el controlador, pero podría volver a hacerlo más tarde con este nuevo bot. Es demasiado tarde para reasignar la recompensa si gana, pero tengo curiosidad sobre el resultado.
Programas Redwolf
@RedwolfPrograms El objetivo no era ganar la recompensa.
fəˈnɛtɪk
Lo sé, solo asegurándome de saber (:
Programas Redwolf