Hungry Blobs KoTH

9

Konkurs zakończony! Przeczytaj komentarze na temat obiektów blob, aby wyświetlić ich wynik.

KoTH jest luźno zainspirowany Symulacją doboru naturalnego Primer . Twój bot to kropelka. Aby przetrwać, musisz jeść granulki, aby odzyskać energię, która służy do poruszania się. Dzięki dodatkowej energii plamy można podzielić na dwie części.

Energia i ruch

Twoja kropelka zaczyna się w każdej rundzie od 100 energii i nie ma limitu ilości energii, którą może zebrać. Każda runda jest rozgrywana po kolei, przy czym każda kropla ma możliwość przesunięcia się na północ, wschód, południe lub zachód w dowolnej turze lub zatrzymania. Poruszanie zużywa 1 energię, a stanie stojące zużywa 0,25 energii. Długość boku mapy wynosiceil(0.25 * blobCount) * 2 - 1jednostki, co najmniej 9 jednostek. Wszystkie plamy zaczynają się na krawędzi mapy, z jedną umieszczoną w każdym rogu, a każda kolejna kropla jest oddalona o 2 jednostki od innych. Co 30 tur, fala granulek umieszczana jest w losowych miejscach na mapie, w odległości co najmniej 1 jednostki od dowolnej krawędzi. Za każdym razem, gdy pojawia się fala granulek, ilość granulek (pierwotnie dwukrotność liczby kropel lub szerokość mapy, w zależności od tego, która wartość jest większa) w następnej fali jest zmniejszana o 1, zmuszając liczbę kropel do zmniejszania się z czasem. Każdy osad przywraca od 5 do 15 energii. Kiedy energia kropli jest mniejsza lub równa 0, umiera.

Jedzenie

Jeśli dwie lub więcej kropek spróbuje zająć to samo miejsce, ten o największej energii zje pozostałe, odbierając ich energię. Jeśli obie mają taką samą energię, obie znikną.

Wykrywanie i informacje

Plamy mogą zobaczyć dowolne peletki lub inne plamy w odległości 4 jednostek. Po wywołaniu ich funkcji obiekty BLOB są dostarczane z:

  • Długość boku mapy
  • Położenie kropli na mapie
  • Pozycje wszystkich granulek w promieniu wyszukiwania, a także ich wartości
  • Pozycje wszystkich obiektów blob w promieniu wyszukiwania, a także ich energia i identyfikatory UID
  • Energia, identyfikator UID i lokalizacje obiektu blob, którego funkcja jest wykonywana
  • Obiekt pamięci unikalny dla obiektu blob
  • Obiekt pamięci współużytkowany przez wszystkie obiekty blob związane z obiektem blob przez podział

Rozdzielać

Jeśli kropelka ma więcej niż 50 energii, może wybrać podział. Rozdzielenie kosztuje 50 energii, a pozostała energia jest dzielona równomiernie między dwie plamy. Wszystkie obiekty BLOB są oryginałami lub kopiami podzielonymi, a każda kopia powraca do oryginału. Wszyscy razem są „krewnymi”. Wszyscy krewni mają jeden wspólny obiekt magazynowy. Krewni nadal mogą się nawzajem jeść i mogą się dzielić, używać własnego obiektu magazynowego lub zbierać energię bez wpływu na innych.

Transfer energii

Jeśli dwie plamy znajdują się obok siebie (po przeprowadzce), jeden z botów może przenosić energię na drugą. Odbywa się to poprzez zwrot SendNorth(amt), SendEast(amt), SendSouth(amt), lub SendWest(amt), z amtjest liczbą reprezentującą ilość wysłana. Może to być dowolna kwota, na którą nadawca może sobie pozwolić, w tym cała energia. Zaleca się, aby kroplowi, który otrzymuje energię, powiedziano, aby pozostawał nieruchomy przez wspólne magazynowanie, aby nie odsuwał się, gdy energia jest przenoszona (choć w tym przypadku energia nie zostanie odjęta od sumy nadawcy).

Funkcje, pamięć i identyfikatory UID

Aby umożliwić bardziej złożone zachowania edukacyjne, wszystkie obiekty BLOB otrzymają liczby całkowite UID (Unique Identifer). Te identyfikatory UID będą generowane losowo na każdej mapie, zapobiegając strategiom opartym na indywidualnych celach. Po wywołaniu funkcji obiektu blob przekazywane są cztery argumenty:

  1. Boczna długość mapy jako liczba całkowita
  2. Obiekt z dwoma tablicami: pelletsi blobs. Oba tablice zawierają obiekty, z których oba mają poswłaściwość zawierającą pozycję osadu lub obiektu blob sformatowaną jako [x,y]. Pelety będą miały energywłaściwość, a obiekty BLOB będą miały uidwłaściwość i energywłaściwość
  3. Przedmiotem zawierających różne właściwości blob jest przekazywany do: energy, uidi pos. posTablica jest sformatowana jako[x,y]
  4. Obiekt zawierający dwa obiekty pamięci obiektu blob. selfWłaściwość zawiera pojedynczy obiekt przechowywania, który może być modyfikowany, jednakże blob uzna (o właściwości obiektu, która jest przekazywana manipulacji), a communalwłaściwość, która może być modyfikowana za każdym względem.

Obiekty BLOB nie są przenoszone natychmiast, aby zapobiec przewadze wcześniejszych / późniejszych tur. Wszystkie ruchy są przetwarzane w grupach (wszystkie kolizje / jedzenie, następnie wszystkie granulki, a następnie dzielenie itp.) Jeśli kropelka wyląduje na granulce lub mniejszej kropli i podczas tego procesu zużywa swoją ostatnią energię, kropelka nadal zużywa granulkę / energia niezależna od tego, czy przyniosłaby całkowitą energię powyżej 0.

Aby względne obiekty BLOB mogły się wzajemnie rozpoznawać, pamięć wspólna musi być używana dla każdego obiektu BLOB do rejestrowania jego identyfikatora UID w tablicy lub przez inny system.

Zwracane wartości

Aby przenieść lub podzielić, używana jest wartość zwracana przez funkcję. Po pierwsze, znaczenie głównych kierunków w kategoriach współrzędnych:

  • Północ = -Y
  • Wschód = + X
  • Południe = + Y
  • Zachód = -X

Zauważ, że [0,0]jest to lewy górny róg , a Y zwiększa się wraz ze spadkiem. Zwracana wartość funkcji powinna być zgodna z następującymi regułami:

  • Nic nie robić: nic nie zwraca, 0, null, undefined, false lub dowolna inna wartość równa false
  • Aby przenieść: zwróć jedną z czterech zmiennych globalnych: Północ, Wschód, Południe lub Zachód, które są równe „północ”, „wschód”, „południe” lub „zachód” (które można również wykorzystać jako wartość zwracaną)
  • Aby podzielić: zwraca zmienną globalną SplitNorth, SplitEast, SplitSouth lub SplitWest, kierunek wskazujący, gdzie umieścić nowy obiekt blob

Jeśli zostanie zwrócone polecenie podziału, a wymagana ilość energii będzie większa lub równa energii kropelki, nic się nie wydarzy. Obiekty BLOB nie będą mogły opuścić mapy.

Predefiniowane funkcje biblioteczne

Domyślnie dostępnych jest kilka podstawowych funkcji, aby zaoszczędzić trochę czasu:

taxiDist (pt1, pt2)

Zwraca odległość taksówki między dwoma punktami (odległość X plus odległość 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)

Zwraca odległość między dwoma punktami zgodnie z twierdzeniem pitagorejskim

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 (reż, amt)

Przyjmuje wprowadzony kierunek, obraca się o 90 stopni zgodnie z ruchem wskazówek zegara amt, a następnie zwraca nową wartość.

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

Przykładowy obiekt Blob

Ten obiekt nie ruszy się, dopóki w pobliżu nie znajdzie granulki. Następnie przesunie się w kierunku, który według niego najprawdopodobniej go nagrodzi. Jeśli jego energia przekroczy kiedykolwiek 150, rozpadnie się.

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

Zasady

  • Standardowe luki są zabronione. Ponadto brak niestandardowych luk.
  • Żaden obiekt blob nie może próbować modyfikować ani odczytywać danych nieprzekazanych mu za pomocą jego parametrów
  • Żaden obiekt blob nie może próbować modyfikować zmiennej wartości zwracanej w celu sabotowania innych obiektów blob
  • Runda trwa, dopóki jedynymi pozostałymi plamami nie są krewni
  • Żaden obiekt blob nie może modyfikować danych, wprowadzając funkcje do swoich parametrów, które modyfikują wartości za pomocą thissłowa kluczowego
  • Wszystkie zgłoszenia muszą być w języku Javascript lub w języku, który nie różni się zbytnio od Javascript (na przykład Python). Wszystkie odpowiedzi zostaną przekonwertowane na JavaScript na potrzeby konkursu.
  • Zwycięzcą jest kropla, która zebrała największą ilość energii ogółem we wszystkich rundach (z pelletu lub zużywa mniejsze plamy, które nie są krewnymi)

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

Pokój czatu: https://chat.stackexchange.com/rooms/93370/hungry-blobs-koth

Programy Redwolf
źródło
1
Czy możesz to rozszerzyć na inne języki oprócz javascript?
Wcielenie nieznajomości
@EmbodimentofIgnorance Prześlij go w dowolnym wybranym języku, a ja dokonam konwersji na JS.
Programy Redwolf,
Czy obiekty BLOB mogą się przecinać Np .: obiekt blob1 o [0] [0] przesuwa się w prawo, a obiekt blob2 o [0] [1] przesuwa się w lewo, czy też zostanie zjedzony obiekt blob o niższej energii?
fəˈnɛtɪk
Powiązane
fəˈnɛtɪk
@ fəˈnɛtɪk Tak, boty mogą się krzyżować. Również związane z tym wyzwanie było moje (:
Programy Redwolf

Odpowiedzi:

3

Introwertyk

Introvert nie lubi innych obiektów blob. Kiedy widzi niepowiązany kropelek, zjada go, jeśli może, i niechętnie przyjmuje swoją obecność, jeśli nie może, choć ucieka, jeśli zobaczy oznaki agresji. Gdy widzi powiązany obiekt blob, dystansuje się. Jednak nie może pomóc, ale bardzo się rozdziela.

Szczegóły techniczne

Podstawową cechą tego obiektu blob jest jego rozdzielenie i rozłożenie, aby zmaksymalizować łączne widzenie obiektów blob. Wykorzystuje również system zapobiegający rywalizacji dwóch z nich o kulkę.

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
źródło
To wygląda na całkiem niezłego bota! Konkurs powinien być wkrótce (nagroda wygasa jutro).
Programy Redwolf
@RedwolfPrograms Właściwie przetestowałem to w biegaczu i zawsze wygrywa dość sporą marżą.
RamenChef
Średni wynik na rundę: 357,544
Programy Redwolf
1

Animowany Posiłek

Prosty bot, aby rozpocząć konkurencję. Znajduje najbliższą monetę i idzie w jej kierunku. Na podstawie przykładowego bota.

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
źródło
Średni wynik na rundę: 24,933
Programy Redwolf
I, w zaskakujący sposób, 5-liniowy (nieznacznie zmodyfikowany w celu zmniejszenia błędów) wygrywa 2
Programy Redwolf
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));
}

Rzeczywisty bot jest dość prosty, ale jest on bardziej zaprojektowany jako dowód koncepcji bloblib, zbioru funkcji i funkcjonalności, których zamierzam używać i rozwijać na innych botach (możesz również swobodnie używać / rozwijać na nim)

Krótko mówiąc, ten bot wykonuje następujące czynności:

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
źródło
Teraz możesz zobaczyć energię
kropli
1
@RedwolfPrograms zaktualizował bloblib, aby określić, czy wrogie obiekty BLOB są „zagrożeniem” na podstawie ich poziomu energii.
Skidsdev,
Średni wynik na rundę: 7,913
Programy Redwolf
Ten system mógł być prawdopodobnie używany do niektórych dobrych obiektów blob, ale ten wydawał się działać nieco dziwnie.
Programy Redwolf
1

Chciwy Tchórz

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

Lub w 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
}

Ten bot nie jest zbyt interesujący. Działa zgodnie z dwoma priorytetami:

  1. Nie daj się zjeść.
  2. Zjedz najbliższą rzecz.

Nigdy nie pluje, aby zmaksymalizować swoją zdolność do jedzenia innych rzeczy.

Towarzyszu SparklePony
źródło
Zajmę się tłumaczeniem tego! Kiedy skończę, zasugeruję edycję w wersji JS.
Programy Redwolf
@RedwolfPrograms Brzmi dobrze, dziękuję bardzo.
Towarzysz SparklePony,
Myślę, że musisz dodać if / else, aby sprawdzić, czy rzeczywiście istnieją jakieś dobre / złe obiekty. Powoduje to kilka problemów w wersji JS.
Programy Redwolf
@RedwolfPrograms Należy to teraz naprawić. Właśnie dodałem instrukcję if, która sprawdza utworzone listy interesujących i złych obiektów, aby upewnić się, że nie są one puste. Jeszcze raz dziękuję za pomoc.
Towarzysz SparklePony
@RedwolfPrograms Czy masz gotową wersję JS?
RamenChef
1

SafetyBlob

Ten bot korzysta z tej samej logiki co Safetycoin z poprzedniej KOTH.

Jak to działa

Ten bot skieruje się w stronę żywności, do której może dotrzeć albo zanim zrobią to większe boty, albo w tym samym czasie / przed mniejszym botem. Jeśli nie widzi żywności spełniającej te kryteria, będzie się poruszać w losowym kierunku (z tendencją do środka). Jeśli osiągnie 150 energii i nie będzie widział bezpiecznej żywności, podzieli się w jednym z kierunków oznaczonych jako bezpieczne do przemieszczania.

Ten bot nie śledzi własnych dzieci, ale i tak nie powinny one kolidować ze względu na mechanizmy bezpieczeństwa.

 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
źródło
Już uruchomiłem kontroler, ale mogę to zrobić później z tym nowym botem. Jest za późno, aby ponownie przypisać nagrodę, jeśli wygra, ale jestem ciekawy wyniku.
Programy Redwolf
@RedwolfPrograms Celem nie było zdobycie nagrody.
fəˈnɛtɪk
Wiem, upewniając się, że wiesz (:
Programy Redwolf