Hungry Blobs KoTH


9

Contest concluso! Leggi i commenti sui BLOB per visualizzare il loro punteggio.

Questo KoTH è liberamente ispirato alla simulazione di selezione naturale di Primer . Il tuo bot è un blob. Per sopravvivere, devi mangiare i pellet per recuperare energia, che viene utilizzata per spostarti. Con energia extra, i blob possono essere divisi in due.

Energia e movimento

Il tuo blob inizia ogni round con 100 energia e non ha limiti sulla quantità di energia che può raccogliere. Ogni round viene eseguito a turni, con ogni blob che ha la possibilità di spostarsi a nord, est, sud o ovest in un dato turno o rimanere fermi. Lo spostamento utilizza 1 energia e la posizione eretta utilizza ancora 0,25 energia. La lunghezza del lato della mappa èceil(0.25 * blobCount) * 2 - 1unità, con un minimo di 9 unità. Tutti i BLOB iniziano sul bordo della mappa, con uno posizionato in ogni angolo e ogni BLOB successivo viene posizionato a 2 unità di distanza da qualsiasi altro. Ogni 30 turni, un'onda di pallini viene posizionata in punti casuali attorno alla mappa, almeno 1 unità da qualsiasi bordo. Ogni volta che appare un'onda di pellet, la quantità di pellet (originariamente il doppio del numero di chiazze o della larghezza della mappa, a seconda di quale sia maggiore) nell'onda successiva viene ridotta di 1, costringendo il numero di chiazze a diminuire nel tempo. Ogni pellet ripristina tra 5 e 15 energia. Quando l'energia di un blob è inferiore o uguale a 0, muore.

Mangiare

Se due o più BLOB tentano di occupare la stessa posizione, quello con più energia mangerà gli altri, ricevendo la loro energia. Se entrambi hanno la stessa energia, entrambi svaniscono.

Rilevamento e informazioni

Le chiazze possono vedere le palline o altre macchie entro una distanza di 4 unità. Quando vengono chiamate le loro funzioni, i BLOB vengono forniti con:

  • La lunghezza laterale della mappa
  • La posizione del blob sulla mappa
  • Le posizioni di tutti i pellet nel loro raggio di ricerca, nonché i loro valori
  • Le posizioni di tutti i BLOB nel loro raggio di ricerca, nonché la loro energia e UID
  • Energia, UID e posizioni del BLOB di cui viene eseguita la funzione
  • Un oggetto di archiviazione univoco per il BLOB
  • Un oggetto di archiviazione condiviso da tutti i BLOB correlati al BLOB mediante suddivisione

scissione

Se un blob ha più di 50 energie, può scegliere di dividere. La divisione costa 50 energia e l'eventuale energia rimanente viene suddivisa equamente tra le due chiazze. Tutti i BLOB sono originali o copie divise, con ogni copia risalente a un originale. Tutti insieme sono "parenti". Tutti i parenti hanno un oggetto di archiviazione comune. I parenti possono ancora mangiarsi a vicenda e possono dividere, utilizzare il proprio oggetto di archiviazione o raccogliere energia senza influire sugli altri.

Trasferimento di energia

Se due blob sono uno accanto all'altro (dopo lo spostamento), uno dei robot può trasferire energia all'altro. Questo viene fatto ritornando SendNorth(amt), SendEast(amt), SendSouth(amt), o SendWest(amt)con amtessendo un numero che rappresenta l'importo inviato. Questo può essere qualsiasi importo che il mittente può permettersi, compresa tutta la sua energia. Si raccomanda che al blob che sta ricevendo energia venga detto di rimanere fermo attraverso l'archiviazione comune, in modo che non si allontani quando l'energia viene trasferita (anche se in questo caso l'energia non verrebbe detratta dal totale del mittente).

Funzioni, archiviazione e UID

Per consentire comportamenti di apprendimento più complessi, a tutti i BLOB verrà assegnato un UID intero (Unique Identifer). Questi UID verranno generati casualmente su ogni mappa, impedendo strategie basate su singoli target. Quando viene chiamata la funzione di un BLOB, vengono passati quattro argomenti:

  1. La lunghezza laterale della mappa come numero intero
  2. Un oggetto con due matrici:, pelletse blobs. Entrambi gli array contengono oggetti, entrambi con una posproprietà contenente la posizione della pallina o del blob formattata come [x,y]. I pellet avranno una energyproprietà, mentre i blob avranno una uidproprietà e una energyproprietà
  3. Un oggetto che contiene varie proprietà del blob si passa a: energy, uide pos. L' posarray è formattato come[x,y]
  4. Un oggetto contenente i due oggetti di archiviazione del BLOB. Una selfproprietà contiene un singolo oggetto di archiviazione che può essere modificato nel modo che il BLOB ritiene opportuno (modificando le proprietà dell'oggetto che viene passato) e una communalproprietà che può essere modificata da qualsiasi parente.

I BLOB non vengono spostati immediatamente per evitare che i turni precedenti / successivi abbiano un vantaggio. Tutti i movimenti vengono elaborati in gruppi (tutte le collisioni / alimentazione, quindi tutte le palline, quindi si scindono, ecc.) Se un blob atterra su un pellet o blob più piccolo e, nel processo utilizza la sua ultima energia, il blob consumerà comunque il pellet / energia indipendente dal fatto che ciò porterebbe la sua energia totale al di sopra di 0.

Affinché i BLOB relativi possano riconoscersi reciprocamente, è necessario utilizzare l'archiviazione comune per ciascun BLOB per registrare il proprio UID in un array o tramite un altro sistema.

Valori di restituzione

Per spostare o dividere, viene utilizzato il valore restituito della funzione. Innanzitutto, il significato delle direzioni cardinali in termini di coordinate:

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

Si noti che [0,0]è l' angolo in alto a sinistra e Y aumenta mentre si scende. Il valore di ritorno della funzione dovrebbe seguire queste regole:

  • Per non fare nulla: non restituire nulla, 0, null, indefinito, falso o qualsiasi altro valore equivalente a falso
  • Per spostare: restituisce una delle quattro variabili globali: Nord, Est, Sud o Ovest, che equivalgono a "nord", "est", "sud" o "ovest" (che potrebbe anche essere usato come valore di ritorno)
  • Per dividere: restituire la variabile globale SplitNorth, SplitEast, SplitSouth o SplitWest, la direzione che indica dove posizionare il nuovo BLOB

Se viene restituito un comando diviso e la quantità di energia richiesta è maggiore o uguale all'energia del blob, non accadrà nulla. I BLOB non saranno in grado di lasciare la mappa.

Funzioni di libreria predefinite

Ci sono alcune funzioni di base disponibili per impostazione predefinita, per risparmiare tempo:

taxiDist (pt1, pt2)

Restituisce la distanza del taxi tra due punti (distanza X più distanza 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)

Restituisce la distanza tra due punti secondo il teorema di Pitagora

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)

Prende la direzione immessa, ruota di 90 gradi in senso orario amt, quindi restituisce il nuovo valore.

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

BLOB di esempio

Questo blob non si muoverà finché non troverà un pellet nelle vicinanze. Quindi, si sposterà nella direzione che ritiene sia più probabile che lo ricompensi. Se la sua energia è mai superiore a 150, si dividerà.

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

Regole

  • Le scappatoie standard sono vietate. Inoltre, nessuna scappatoia non standard.
  • Nessun BLOB può tentare di modificare o leggere i dati non trasmessi tramite i suoi parametri
  • Nessun BLOB può tentare di modificare una variabile di valore restituito per sabotare altri BLOB
  • Un round dura fino a quando le uniche chiazze rimanenti sono parenti
  • Nessun BLOB può modificare i dati iniettando funzioni nei suoi parametri che modificano i valori utilizzando la thisparola chiave
  • Tutti gli invii devono essere in Javascript o in una lingua che non sia troppo diversa da Javascript (ad esempio Python). Tutte le risposte verranno convertite in Javascript per la competizione.
  • Il vincitore è il blob che ha raccolto la più alta quantità di energia in totale in tutti i round (da pallini o consumando blob più piccoli che non sono parenti)

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

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


1
Puoi espanderlo in altre lingue oltre a JavaScript?
Incarnazione dell'ignoranza

@EmbodimentofIgnorance Invialo in qualsiasi lingua tu scelga e farò la conversione in JS.
Programmi Redwolf,

I blob possono incrociarsi a vicenda Ex: blob1 a [0] [0] si sposta a destra e blob2 a [0] [1] si sposta a sinistra o verrà mangiato il blob con energia inferiore?
fəˈnɛtɪk,


@ fəˈnɛtɪk Sì, i robot possono incrociarsi. Inoltre, la sfida correlata era mia (:
Programmi Redwolf,

Risposte:


3

Introverso

All'Introverso non piacciono gli altri BLOB. Quando vede una chiazza non correlata, la mangia se può, e accetta a malincuore la sua presenza se non può, anche se scappa se vede segni di aggressività. Quando vede un blob correlato , prende le distanze. Tuttavia, non può fare a meno di separarsi molto.

Dettagli tecnici

La caratteristica principale di questo BLOB è di separarsi e allargarsi in modo da massimizzare la visione combinata dei BLOB. Impiega anche un sistema per impedire a due di loro di competere su un 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;
}

Sembra un robot molto carino! Il concorso dovrebbe essere presto (la ricompensa scade domani).
Programmi Redwolf,

@RedwolfPrograms In realtà l'ho provato nel corridore e vince sempre con un margine abbastanza grande.
RamenChef,

Punteggio medio per round: 357.544
Programmi Redwolf

1

Pasto animato

Un semplice bot, solo per iniziare la competizione. Trova la moneta più vicina e va verso di essa. Basato sul bot di esempio.

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

Punteggio medio per round: 24.933
Programmi Redwolf

E, in una svolta sorprendente di eventi, il (leggermente modificato per ridurre i bug) 5-liner vince 2 °
Redwolf Programmi

1

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

Il bot attuale è abbastanza semplice, ma questo è più progettato come una prova del concetto di bloblib, una raccolta di funzioni e funzionalità che prevedo di utilizzare e sviluppare su altri robot (sentiti libero di usarlo / espanderlo anche tu)

In breve, questo bot esegue le seguenti operazioni:

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

Ora puoi vedere l'energia di un blob, che potrebbe tornare utile
Programmi Redwolf

1
@RedwolfProgramma il bloblib aggiornato per determinare se i blob nemici sono una "minaccia" in base ai loro livelli di energia.
Skidsdev

Punteggio medio per round: 7.913
Programmi Redwolf

Probabilmente questo sistema avrebbe potuto essere usato per alcuni buoni blob, ma questo sembrava agire in modo un po 'strano.
Programmi di Redwolf

1

Codardo goloso

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

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

Questo bot non è molto interessante. Agisce secondo due priorità:

  1. Non farti mangiare.
  2. Mangia la cosa più vicina.

Non sputa mai per massimizzare la sua capacità di mangiare altre cose.


Mi metterò al lavoro traducendolo! Al termine, suggerirò una modifica con la versione JS.
Programmi Redwolf

@RedwolfPrograms Suona bene, grazie mille.
Compagno SparklePony,

Penso che sia necessario aggiungere un if / else per verificare se ci sono oggetti buoni / cattivi. Sta causando diversi problemi nella versione JS.
Programmi Redwolf,

@RedwolfPrograms Ora dovrebbe essere risolto. Ho appena aggiunto un'istruzione if che controlla gli elenchi creati di oggetti interessanti e danneggiati per assicurarsi che non siano vuoti. Ancora grazie per l'aiuto.
Compagno SparklePony,

@RedwolfPrograms Hai la versione JS pronta?
RamenChef,

1

SafetyBlob

Questo bot utilizza la stessa logica di Safetycoin del precedente KOTH.

Come funziona

Questo robot si dirigerà verso il cibo che può raggiungere prima di qualsiasi robot più grande o allo stesso tempo / prima di un robot più piccolo. Se non riesce a vedere alcun alimento che soddisfi questi criteri, si sposterà in una direzione casuale (di parte verso il centro). Se arriva a 150 energia e non riesce a vedere cibo sicuro, si dividerà in una delle direzioni che ha etichettato come sicuro da spostare.

Questo bot non tiene traccia dei propri figli, ma non dovrebbero comunque scontrarsi a causa dei meccanismi di sicurezza.

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

Ho già eseguito il controller, ma potrei farlo di nuovo in seguito con questo nuovo bot. È troppo tardi per riassegnare la taglia se vince, ma sono curioso di sapere il risultato.
Programmi Redwolf,

@RedwolfPrograms L'obiettivo non era vincere la taglia.
fəˈnɛtɪk,

Lo so, solo assicurandoti di sapere (:
Programmi Redwolf
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.