Hungry Blobs KoTH

9

Concours terminé! Lisez les commentaires sur les blobs pour voir leur score.

Ce KoTH est vaguement inspiré par la simulation de sélection naturelle de Primer . Votre bot est un blob. Pour survivre, vous devez manger des boulettes pour regagner de l'énergie, qui est utilisée pour se déplacer. Avec plus d'énergie, les gouttes peuvent se diviser en deux.

Énergie et mouvement

Votre blob démarre à chaque tour avec 100 énergies, et il n'a pas de limite sur la quantité d'énergie qu'il peut collecter. Chaque tour se déroule à tour de rôle, chaque goutte ayant la possibilité de se déplacer vers le nord, l'est, le sud ou l'ouest dans un tour donné, ou de rester immobile. Se déplacer utilise 1 énergie et rester immobile utilise 0,25 énergie. La longueur du côté de la carte estceil(0.25 * blobCount) * 2 - 1unités, avec un minimum de 9 unités. Tous les blobs commencent sur le bord de la carte, avec un placé dans chaque coin et chaque blob suivant étant placé à 2 unités des autres. Tous les 30 tours, une vague de pellets est placée dans des endroits aléatoires autour de la carte, à au moins 1 unité de n'importe quel bord. Chaque fois qu'une vague de pastilles apparaît, la quantité de pastilles (à l'origine deux fois le nombre de taches ou la largeur de la carte, la plus grande des deux) dans la vague suivante est diminuée de 1, forçant le nombre de taches à diminuer avec le temps. Chaque pastille restaure entre 5 et 15 énergies. Lorsque l'énergie d'un blob est inférieure ou égale à 0, il meurt.

En mangeant

Si deux gouttes ou plus tentent d'occuper le même emplacement, celui qui a le plus d'énergie mangera les autres, recevant leur énergie. Si les deux ont une énergie égale, les deux disparaissent.

Détection et information

Les blobs peuvent voir les pellets ou autres blobs à une distance de 4 unités. Lorsque leurs fonctions sont appelées, les blobs sont fournis avec:

  • La longueur latérale de la carte
  • La position du blob sur la carte
  • Les positions de toutes les pastilles dans leur rayon de recherche, ainsi que leurs valeurs
  • Les positions de tous les blobs dans leur rayon de recherche, ainsi que leur énergie et leurs UID
  • L'énergie, l'UID et les emplacements de l'objet blob dont la fonction est en cours d'exécution
  • Un objet de stockage unique au blob
  • Un objet de stockage partagé par tous les objets blob liés à l'objet blob via le fractionnement

Scission

Si un blob a plus de 50 énergies, il peut choisir de se diviser. Le fractionnement coûte 50 énergie, et toute énergie restante est répartie également entre les deux blobs. Tous les blobs sont des originaux ou des copies fractionnées, chaque copie remontant à un original. Tous ces éléments ensemble sont des «parents». Tous les parents ont un objet de stockage commun. Les proches peuvent toujours se manger et peuvent se séparer, utiliser leur propre objet de stockage ou collecter de l'énergie sans affecter les autres.

Transfert d'énergie

Si deux blobs sont côte à côte (après avoir bougé), l'un des robots peut transférer de l'énergie à l'autre. Cela se fait en revenant SendNorth(amt), SendEast(amt), SendSouth(amt)ou SendWest(amt), avec amtétant un nombre représentant le montant envoyé. Il peut s'agir de tout montant que l'expéditeur peut se permettre, y compris toute son énergie. Il est recommandé de dire à la goutte qui reçoit de l'énergie de rester immobile grâce au stockage commun, afin qu'elle ne s'éloigne pas lorsque l'énergie est transférée (bien que l'énergie ne soit pas déduite du total de l'expéditeur dans ce cas).

Fonctions, stockage et UID

Afin de permettre des comportements d'apprentissage plus complexes, tous les blobs recevront un UID entier (Unique Identifer). Ces UID seront générés aléatoirement sur chaque carte, empêchant les stratégies basées sur des cibles individuelles. Lorsque la fonction d'un blob est appelée, quatre arguments lui sont transmis:

  1. La longueur latérale de la carte sous forme d'entier
  2. Un objet avec deux tableaux:, pelletset blobs. Les deux tableaux contiennent des objets, les deux ayant une pospropriété contenant la position du culot ou du blob au format [x,y]. Les granulés auront une energypropriété, tandis que les blobs auront une uidpropriété et une energypropriété
  3. Objet contenant diverses propriétés du blob il est transmis à: energy, uid, et pos. Le postableau est formaté comme[x,y]
  4. Un objet contenant les deux objets de stockage du blob. Une selfpropriété contient un objet de stockage individuel qui peut être modifié comme le blob le souhaite (en manipulant les propriétés de l'objet transmis) et une communalpropriété qui peut être modifiée par n'importe quel parent.

Les blobs ne sont pas déplacés immédiatement pour éviter que les virages précédents / ultérieurs aient un avantage. Tous les mouvements sont traités en groupes (toutes les collisions / manger, puis toutes les boulettes, puis se séparer, etc.) Si une goutte tombe sur une pastille ou une goutte plus petite et, dans le processus utilise sa dernière énergie, la goutte consommera toujours la pastille / l'énergie indépendamment du fait que cela porterait son énergie totale au-dessus de 0.

Pour que les objets blob relatifs se reconnaissent, le stockage commun doit être utilisé pour chaque objet blob pour enregistrer son UID dans un tableau ou via un autre système.

Valeurs de retour

Pour déplacer ou diviser, la valeur de retour de la fonction est utilisée. Tout d'abord, la signification des directions cardinales en termes de coordonnées:

  • Nord = -Y
  • Est = + X
  • Sud = + Y
  • Ouest = -X

Notez que [0,0]c'est le coin supérieur gauche et Y augmente à mesure que vous descendez. La valeur de retour de la fonction doit suivre ces règles:

  • Pour ne rien faire: ne renvoyer rien, 0, null, non défini, faux ou toute autre valeur équivalente à false
  • Pour se déplacer: Renvoyez l'une des quatre variables globales: Nord, Est, Sud ou Ouest, qui correspondent à "nord", "est", "sud" ou "ouest" (qui pourraient également être utilisées comme valeur de retour)
  • Pour fractionner: renvoyer la variable globale SplitNorth, SplitEast, SplitSouth ou SplitWest, la direction indiquant où placer le nouveau blob

Si une commande de division est renvoyée et que la quantité d'énergie requise est supérieure ou égale à l'énergie de la goutte, rien ne se passera. Les blobs ne pourront pas quitter la carte.

Fonctions de bibliothèque prédéfinies

Il existe quelques fonctions de base disponibles par défaut, pour gagner du temps:

taxiDist (pt1, pt2)

Renvoie la distance du taxi entre deux points (distance X plus distance 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)

Renvoie la distance entre deux points selon le théorème de Pythagore

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)

Prend la direction entrée, pivote de 90 degrés dans le sens horaire amt, puis renvoie la nouvelle valeur.

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

Exemple d'objet blob

Cette goutte ne bougera pas tant qu'elle ne trouvera pas de boulette à proximité. Ensuite, il ira dans la direction qu'il pense la plus susceptible de le récompenser. Si son énergie dépasse jamais 150, il se divisera.

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

Règles

  • Les échappatoires standard sont interdites. En outre, aucune échappatoire non standard.
  • Aucun blob ne peut tenter de modifier ou de lire les données qui ne lui sont pas transmises via ses paramètres
  • Aucun blob ne peut tenter de modifier une variable de valeur de retour pour saboter d'autres blobs
  • Un tour dure jusqu'à ce que les seuls blobs restants soient des parents
  • Aucun blob ne peut modifier les données en injectant des fonctions dans ses paramètres qui modifient les valeurs à l'aide du thismot - clé
  • Toutes les soumissions doivent être en Javascript ou dans une langue qui n'est pas trop différente de Javascript (Python, par exemple). Toutes les réponses seront converties en Javascript pour le concours.
  • Le gagnant est le blob qui a collecté la plus grande quantité d'énergie au total à travers tous les tours (soit des boulettes, soit des petits blobs qui ne sont pas apparentés)

Contrôleur: https://gist.github.com/RedwolfPrograms/1facc0afe24c5dfd3ada8b8a2c493242

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

Programmes Redwolf
la source
1
Pouvez-vous l'étendre à d'autres langues en plus du javascript?
Incarnation de l'ignorance
@EmbodimentofIgnorance Soumettez-le dans la langue que vous choisissez, et je ferai la conversion en JS.
Programmes Redwolf
Les blobs peuvent-ils se croiser Ex: blob1 à [0] [0] se déplace vers la droite et blob2 à [0] [1] se déplace vers la gauche ou le blob avec une énergie moindre sera-t-il mangé?
fəˈnɛtɪk
En relation
fəˈnɛtɪk
@ fəˈnɛtɪk Oui, les robots peuvent se croiser. En outre, le défi connexe était le mien (:
Redwolf Programs

Réponses:

3

Introverti

L'Introvert n'aime pas les autres blobs. Quand il voit un blob sans rapport, il le mange s'il le peut, et accepte à contrecœur sa présence s'il ne le peut pas, mais s'enfuyant s'il voit des signes d'agression. Lorsqu'il voit une goutte apparentée , il s'éloigne. Cependant, cela ne peut que beaucoup se séparer.

Détails techniques

La caractéristique principale de ce blob est de se séparer et de s'étaler de manière à maximiser la vision combinée des blobs. Il utilise également un système pour empêcher deux d'entre eux de concurrencer une pastille.

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
la source
Cela ressemble à un très joli bot! Le concours devrait avoir lieu prochainement (la prime expire demain).
Programmes Redwolf
@RedwolfPrograms Je l'ai testé sur le coureur et il gagne toujours par une assez grande marge.
RamenChef
Score moyen par tour: 357,544
Redwolf Programs
1

Repas animé

Un simple bot, juste pour commencer la compétition. Trouve la pièce la plus proche et va vers elle. Basé sur l'exemple de 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
la source
Score moyen par tour: 24.933
Redwolf Programs
Et, dans une tournure surprenante des événements, le 5-liner (légèrement modifié pour réduire les bugs) remporte le 2e
Redwolf Programs
1

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

Le bot réel est assez simple, mais il est plus conçu comme une preuve de concept bloblib, une collection de fonctions et de fonctionnalités que je prévois d'utiliser et de développer sur d'autres bots (n'hésitez pas à l'utiliser / à l'étendre vous-même également)

En bref, ce bot fait ce qui suit:

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
la source
Vous pouvez maintenant voir l'énergie d'un blob, qui pourrait être utile
Redwolf Programs
1
@RedwolfPrograms a mis à jour bloblib pour déterminer si les blobs ennemis sont une "menace" en fonction de leur niveau d'énergie.
Skidsdev
Score moyen par tour: 7.913
Redwolf Programs
Ce système aurait probablement pu être utilisé pour de bons blobs, mais celui-ci semblait agir un peu étrangement.
Programmes Redwolf
1

Lâche gourmand

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

Ce bot n'est pas très intéressant. Il agit selon deux priorités:

  1. Ne vous mangez pas.
  2. Mangez la chose la plus proche.

Il ne crache jamais pour maximiser sa capacité à manger d'autres choses.

Camarade SparklePony
la source
Je vais travailler pour traduire ça! Quand j'aurai fini, je proposerai une modification avec la version JS.
Programmes Redwolf
@RedwolfPrograms Sonne bien, merci beaucoup.
Camarade SparklePony
Je pense que vous devez ajouter un if / else pour vérifier s'il y a réellement de bons / mauvais objets. Cela cause plusieurs problèmes dans la version JS.
Programmes Redwolf
@RedwolfPrograms Cela devrait être corrigé maintenant. Je viens d'ajouter une instruction if qui vérifie les listes créées d'objets intéressants et mauvais pour s'assurer qu'elles ne sont pas vides. Encore une fois, merci pour l'aide.
Camarade SparklePony
@RedwolfPrograms Avez-vous la version JS prête?
RamenChef
1

SafetyBlob

Ce bot utilise une partie de la même logique que Safetycoin du précédent KOTH.

Comment ça fonctionne

Ce bot se dirigera vers de la nourriture qu'il pourra atteindre avant tout plus gros bot ou en même temps / avant un plus petit bot. S'il ne peut voir aucun aliment répondant à ces critères, il se déplacera dans une direction aléatoire (biaisé vers le centre). S'il atteint une énergie de 150 et ne peut pas voir des aliments sains, il se divisera dans l'une des directions qu'il a étiquetées comme sûres à déplacer.

Ce bot ne garde pas la trace de ses propres enfants mais ils ne devraient de toute façon pas entrer en collision en raison des mécanismes de sécurité.

 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
la source
J'ai déjà exécuté le contrôleur, mais je pourrais le refaire plus tard avec ce nouveau bot. Il est trop tard pour réaffecter la prime si elle gagne, mais je suis curieux du résultat.
Programmes Redwolf
@RedwolfPrograms Le but n'était pas de gagner la prime.
fəˈnɛtɪk
Je sais, juste pour être sûr de savoir (:
Redwolf Programs