Hungrige Blobs KoTH


9

Wettbewerb beendet! Lesen Sie Kommentare zu Blobs, um deren Punktzahl anzuzeigen.

Dieses KoTH ist lose von Primers natürlicher Selektionssimulation inspiriert . Ihr Bot ist ein Blob. Um zu überleben, müssen Sie Pellets essen, um Energie zurückzugewinnen, die zum Bewegen verwendet wird. Mit zusätzlicher Energie können Blobs in zwei Teile geteilt werden.

Energie und Bewegung

Dein Blob beginnt jede Runde mit 100 Energie und es gibt keine Begrenzung für die Menge an Energie, die er sammeln kann. Jede Runde wird abwechselnd ausgeführt, wobei jeder Blob die Möglichkeit hat, sich in einer bestimmten Runde nach Norden, Osten, Süden oder Westen zu bewegen oder still zu stehen. Das Bewegen verbraucht 1 Energie und das Stillstehen verbraucht 0,25 Energie. Die Seitenlänge der Karte beträgtceil(0.25 * blobCount) * 2 - 1Einheiten, mit mindestens 9 Einheiten. Alle Blobs beginnen am Rand der Karte, wobei einer in jeder Ecke platziert wird und jeder nachfolgende Blob 2 Einheiten von anderen entfernt ist. Alle 30 Umdrehungen wird eine Welle von Pellets an zufälligen Stellen auf der Karte platziert, mindestens 1 Einheit von jeder Kante entfernt. Jedes Mal, wenn eine Welle von Pellets erscheint, wird die Anzahl der Pellets (ursprünglich doppelt so viele Blobs oder die Breite der Karte, je nachdem, welcher Wert größer ist) in der nächsten Welle um 1 verringert, wodurch die Anzahl der Blobs mit der Zeit abnimmt. Jedes Pellet stellt zwischen 5 und 15 Energie wieder her. Wenn die Energie eines Blobs kleiner oder gleich 0 ist, stirbt er.

Essen

Wenn zwei oder mehr Blobs versuchen, denselben Ort zu besetzen, frisst derjenige mit der meisten Energie die anderen und erhält ihre Energie. Wenn beide die gleiche Energie haben, verschwinden beide.

Erkennung und Information

Blobs können alle Pellets oder andere Blobs in einem Abstand von 4 Einheiten sehen. Wenn ihre Funktionen aufgerufen werden, erhalten Blobs Folgendes:

  • Die Seitenlänge der Karte
  • Die Position des Blobs auf der Karte
  • Die Positionen aller Pellets innerhalb ihres Suchradius sowie ihre Werte
  • Die Positionen aller Blobs innerhalb ihres Suchradius sowie ihre Energie und UIDs
  • Die Energie, UID und Positionen des Blobs, dessen Funktion ausgeführt wird
  • Ein für den Blob eindeutiges Speicherobjekt
  • Ein Speicherobjekt, das von allen Blobs gemeinsam genutzt wird, die sich durch Aufteilen auf den Blob beziehen

Aufteilen

Wenn ein Blob mehr als 50 Energie hat, kann er sich teilen. Das Aufteilen kostet 50 Energie und die verbleibende Energie wird gleichmäßig auf die beiden Blobs aufgeteilt. Alle Blobs sind entweder Originale oder geteilte Kopien, wobei jede Kopie auf ein Original zurückgeht. Alle zusammen sind "Verwandte". Alle Verwandten haben ein gemeinsames Speicherobjekt. Verwandte können sich immer noch gegenseitig essen und sich teilen, ihr eigenes Speicherobjekt verwenden oder Energie sammeln, ohne andere zu beeinflussen.

Energieübertragung

Wenn zwei Blobs nebeneinander liegen (nach dem Bewegen), kann einer der Bots Energie auf den anderen übertragen. Dies erfolgt durch Zurückkehren SendNorth(amt), SendEast(amt), SendSouth(amt), oder SendWest(amt), mit amteiner Zahl , die die Menge gesendet. Dies kann ein beliebiger Betrag sein, den sich der Absender leisten kann, einschließlich seiner gesamten Energie. Es wird empfohlen, dass der Blob, der Energie empfängt, angewiesen wird, durch die gemeinsame Speicherung still zu bleiben, damit er sich beim Übertragen der Energie nicht wegbewegt (obwohl die Energie in diesem Fall nicht von der Gesamtsumme des Absenders abgezogen würde).

Funktionen, Speicher und UIDs

Um komplexere Lernverhalten zu ermöglichen, erhalten alle Blobs eine ganzzahlige UID (Unique Identifer). Diese UIDs werden zufällig auf jeder Karte generiert, wodurch Strategien verhindert werden, die auf einzelnen Zielen basieren. Wenn die Funktion eines Blobs aufgerufen wird, werden vier Argumente übergeben:

  1. Die Seitenlänge der Karte als Ganzzahl
  2. Ein Objekt mit zwei Arrays:, pelletsund blobs. Beide Arrays enthalten Objekte, wobei beide eine posEigenschaft haben, die die Position des Pellets oder Blobs enthält, die als formatiert ist [x,y]. Pellets haben eine energyEigenschaft, während Blobs eine uidEigenschaft und eine energyEigenschaft haben
  3. Ein Objekt verschiedene Eigenschaften des BLOB enthält , wird es weitergeleitet: energy, uid, und pos. Das posArray ist formatiert als[x,y]
  4. Ein Objekt, das die beiden Speicherobjekte des Blobs enthält. Eine selfEigenschaft enthält ein einzelnes Speicherobjekt, das nach Belieben des Blobs geändert werden kann (durch Manipulieren der Eigenschaften des übergebenen Objekts), sowie eine communalEigenschaft, die von einem beliebigen Verwandten geändert werden kann.

Blobs werden nicht sofort verschoben, um zu verhindern, dass frühere / spätere Runden einen Vorteil haben. Alle Bewegungen werden in Gruppen verarbeitet (Alle Kollisionen / Essen, dann alle Pellets, dann Spalten usw.). Wenn ein Blob auf einem Pellet oder einem kleineren Blob landet und dabei seine letzte Energie verbraucht, verbraucht der Blob immer noch das Pellet / Energie unabhängig davon, ob dies ihre Gesamtenergie über 0 bringen würde.

Damit sich relative Blobs gegenseitig erkennen, muss der gemeinsame Speicher für jeden Blob verwendet werden, um seine UID in einem Array oder über ein anderes System aufzuzeichnen.

Rückgabewerte

Zum Verschieben oder Teilen wird der Rückgabewert der Funktion verwendet. Erstens die Bedeutung der Himmelsrichtungen in Bezug auf Koordinaten:

  • Norden = -Y
  • Ost = + X.
  • Süd = + Y.
  • West = -X

Beachten Sie, dass dies [0,0]die obere linke Ecke ist und Y beim Abstieg zunimmt. Der Rückgabewert der Funktion sollte den folgenden Regeln entsprechen:

  • Nichts tun: Geben Sie nichts zurück, 0, null, undefiniert, false oder einen anderen Wert, der false entspricht
  • Verschieben: Geben Sie eine von vier globalen Variablen zurück: Nord, Ost, Süd oder West, die "Nord", "Ost", "Süd" oder "West" entsprechen (die auch als Rückgabewert verwendet werden können).
  • Um Split: Return die globale Variable SplitNorth, SplitEast, SplitSouth oder SplitWest, die die Richtung , wo die neue Blob platzieren

Wenn ein Teilungsbefehl zurückgegeben wird und die benötigte Energiemenge größer oder gleich der Energie des Blobs ist, geschieht nichts. Blobs können die Karte nicht verlassen.

Vordefinierte Bibliotheksfunktionen

Standardmäßig sind einige Grundfunktionen verfügbar, um Zeit zu sparen:

TaxiDist (pt1, pt2)

Gibt den Taxidistanz zwischen zwei Punkten zurück (X-Abstand plus Y-Abstand).

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)

Gibt den Abstand zwischen zwei Punkten gemäß dem pythagoreischen Theorem zurück

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)

Nimmt die eingegebene Richtung an, dreht sich um 90 Grad im Uhrzeigersinn amtund gibt dann den neuen Wert zurück.

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

Beispiel Blob

Dieser Klecks bewegt sich nicht, bis er ein Pellet in der Nähe findet. Dann bewegt es sich in die Richtung, in die es am wahrscheinlichsten belohnt wird. Wenn seine Energie jemals über 150 liegt, wird es sich teilen.

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

Regeln

  • Standardlücken sind verboten. Auch keine ungewöhnlichen Schlupflöcher.
  • Kein Blob darf versuchen, Daten zu ändern oder zu lesen, die nicht über seine Parameter an ihn übergeben wurden
  • Kein Blob darf versuchen, eine Rückgabewertvariable zu ändern, um andere Blobs zu sabotieren
  • Eine Runde dauert, bis die einzigen verbleibenden Blobs Verwandte sind
  • Kein Blob darf Daten ändern, indem Funktionen in seine Parameter eingefügt werden, die Werte mithilfe des thisSchlüsselworts ändern
  • Alle Einsendungen müssen entweder in Javascript oder in einer Sprache erfolgen, die sich nicht zu stark von Javascript unterscheidet (z. B. Python). Alle Antworten werden für den Wettbewerb in Javascript konvertiert.
  • Der Gewinner ist der Blob, der in allen Runden insgesamt die höchste Energiemenge gesammelt hat (entweder durch Pellets oder durch den Verzehr kleinerer Blobs, die keine Verwandten sind).

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

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


1
Können Sie dies auf andere Sprachen als Javascript ausweiten?
Verkörperung der Unwissenheit

@EmbodimentofIgnorance Senden Sie es in einer beliebigen Sprache, und ich werde die Konvertierung in JS durchführen.
Redwolf Programme

Können sich Blobs überkreuzen? Beispiel: Blob1 bei [0] [0] bewegt sich nach rechts und Blob2 bei [0] [1] bewegt sich nach links oder wird der Blob mit niedrigerer Energie gegessen?
fəˈnɛtɪk


@ fəˈnɛtɪk Ja, Bots können sich gegenseitig kreuzen. Die damit verbundene Herausforderung war auch meine (:
Redwolf-Programme

Antworten:


3

Introvertiert

Der Introvertierte mag keine anderen Blobs. Wenn es einen nicht verwandten Fleck sieht, isst es ihn, wenn es kann, und akzeptiert seine Anwesenheit widerwillig, wenn es nicht kann, obwohl es wegläuft, wenn es Anzeichen von Aggression sieht. Wenn es einen verwandten Blob sieht , distanziert es sich. Es kann jedoch nicht anders, als viel auseinander zu spalten.

Technische Details

Das Hauptmerkmal dieses Blobs ist das Aufteilen und Verteilen, um die kombinierte Sicht der Blobs zu maximieren. Es wird auch ein System verwendet, um zu verhindern, dass zwei von ihnen um ein Pellet konkurrieren.

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

Das sieht aus wie ein hübscher Bot! Der Wettbewerb sollte bald sein (Kopfgeld läuft morgen ab).
Redwolf Programme

@RedwolfPrograms Ich habe es tatsächlich im Runner getestet und es gewinnt immer mit einem ziemlich großen Vorsprung.
RamenChef

Durchschnittliche Punktzahl pro Runde: 357.544
Redwolf Programme

1

Animierte Mahlzeit

Ein einfacher Bot, nur um den Wettbewerb zu starten. Findet die nächste Münze und geht darauf zu. Basierend auf dem Beispielbot.

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

Durchschnittliche Punktzahl pro Runde: 24.933
Redwolf Programme

Und in einer überraschenden Wendung der Ereignisse gewinnt der (leicht modifizierte) 5-Liner den 2.
Redwolf Programs

1

Bloblib-Tester

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

Der eigentliche Bot ist ziemlich einfach, aber dies ist eher als Proof-of-Concept gedacht, als eine bloblibSammlung von Funktionen und Funktionen, die ich für andere Bots verwenden und entwickeln möchte (Sie können ihn auch selbst verwenden / erweitern).

Kurz gesagt, dieser Bot macht Folgendes:

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

Sie können jetzt die Energie eines Blobs sehen, die sich als nützlich
erweisen

1
@RedwolfPrograms hat bloblib aktualisiert, um zu bestimmen, ob feindliche Blobs aufgrund ihres Energieniveaus eine "Bedrohung" darstellen.
Skidsdev

Durchschnittliche Punktzahl pro Runde: 7.913
Redwolf Programme

Dieses System hätte wahrscheinlich für einige gute Blobs verwendet werden können, aber dieses schien etwas seltsam zu wirken.
Redwolf Programme

1

Gieriger Feigling

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

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

Dieser Bot ist nicht sehr interessant. Es handelt nach zwei Prioritäten:

  1. Lass dich nicht essen.
  2. Iss das Nächste.

Es spuckt nie, um seine Fähigkeit zu maximieren, andere Dinge zu essen.


Ich werde mich an die Arbeit machen, um das zu übersetzen! Wenn ich fertig bin, schlage ich eine Bearbeitung mit der JS-Version vor.
Redwolf Programme

@ RedwolfProgramme Hört sich gut an, vielen Dank.
Genosse SparklePony

Ich denke, Sie müssen ein if / else hinzufügen, um zu überprüfen, ob es tatsächlich gute / schlechte Objekte gibt. Es verursacht mehrere Probleme in der JS-Version.
Redwolf Programme

@ RedwolfProgramme Es sollte jetzt behoben sein. Ich habe gerade eine if-Anweisung hinzugefügt, die die erstellten Listen interessanter und fehlerhafter Objekte überprüft, um sicherzustellen, dass sie nicht leer sind. Nochmals vielen Dank für die Hilfe.
Genosse SparklePony

@ RedwolfProgramme Hast du die JS-Version fertig?
RamenChef

1

SafetyBlob

Dieser Bot verwendet die gleiche Logik wie Safetycoin aus dem vorherigen KOTH.

Wie es funktioniert

Dieser Bot geht auf Nahrung zu, die er entweder vor größeren Bots oder gleichzeitig / vor einem kleineren Bot erreichen kann. Wenn es keine Lebensmittel sehen kann, die diese Kriterien erfüllen, bewegt es sich in eine zufällige Richtung (voreingenommen zur Mitte). Wenn es 150 Energie erreicht und keine sicheren Lebensmittel sehen kann, wird es in eine der Richtungen aufgeteilt, die es als sicher zum Bewegen gekennzeichnet hat.

Dieser Bot verfolgt nicht seine eigenen Kinder, aber sie sollten aufgrund der Sicherheitsmechanismen trotzdem nicht kollidieren.

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

Ich habe den Controller bereits ausgeführt, kann ihn aber später mit diesem neuen Bot erneut ausführen. Es ist zu spät, um das Kopfgeld neu zuzuweisen, wenn es gewinnt, aber ich bin gespannt auf das Ergebnis.
Redwolf Programme

@ RedwolfProgramme Das Ziel war nicht, das Kopfgeld zu gewinnen.
fəˈnɛtɪk

Ich weiß, nur um sicher zu gehen, dass Sie wissen (:
Redwolf Programme
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.