Hungry Blobs KoTH

9

Kontes Selesai! Baca komentar di gumpalan untuk melihat skor mereka.

KoTH ini secara longgar terinspirasi oleh Simulasi Seleksi Alam Primer . Bot Anda adalah gumpalan. Untuk bertahan hidup, Anda harus makan pelet untuk mendapatkan kembali energi, yang digunakan untuk bergerak. Dengan energi ekstra, gumpalan dapat dibagi menjadi dua.

Energi dan Gerakan

Gumpalan Anda memulai setiap putaran dengan 100 energi, dan tidak memiliki batasan jumlah energi yang dapat dikumpulkannya. Setiap putaran dijalankan bergantian, dengan masing-masing gumpalan memiliki pilihan untuk bergerak Utara, Timur, Selatan, atau Barat pada setiap belokan yang diberikan, atau berdiri diam. Bergerak menggunakan 1 energi, dan berdiri masih menggunakan energi 0,25. Panjang sisi peta adalahceil(0.25 * blobCount) * 2 - 1unit, dengan minimum 9 unit. Semua gumpalan dimulai di tepi peta, dengan satu gumpalan ditempatkan di setiap sudut dan setiap gumpalan berikutnya ditempatkan 2 unit dari yang lain. Setiap 30 putaran, gelombang pelet ditempatkan di tempat-tempat acak di sekitar peta, setidaknya 1 unit dari tepi mana pun. Setiap kali gelombang pelet muncul, jumlah pelet (awalnya dua kali jumlah gumpalan atau lebar peta, mana yang lebih besar) dalam gelombang berikutnya berkurang sebesar 1, memaksa jumlah gumpalan berkurang seiring waktu. Setiap pelet mengembalikan energi antara 5 dan 15. Ketika energi gumpalan kurang dari atau sama dengan 0, ia mati.

Memakan

Jika dua atau lebih gumpalan mencoba untuk menempati lokasi yang sama, yang memiliki energi paling besar akan memakan yang lain, menerima energinya. Jika keduanya memiliki energi yang sama, keduanya lenyap.

Deteksi dan Informasi

Gumpalan dapat melihat pelet atau gumpalan lainnya dalam jarak 4 unit. Ketika fungsinya dipanggil, gumpalan disediakan dengan:

  • Panjang sisi peta
  • Posisi gumpalan di peta
  • Posisi semua pelet dalam radius pencarian mereka, serta nilainya
  • Posisi semua gumpalan dalam radius pencarian mereka, serta energi dan UID mereka
  • Energi, UID, dan lokasi gumpalan yang fungsinya dijalankan
  • Objek penyimpanan yang unik untuk gumpalan
  • Objek penyimpanan yang dibagikan oleh semua gumpalan yang terkait dengan gumpalan melalui pemisahan

Pemisahan

Jika gumpalan memiliki lebih dari 50 energi, ia dapat memilih untuk membelah. Membelah biaya energi 50, dan energi yang tersisa dibagi secara merata antara dua gumpalan. Semua gumpalan adalah dokumen asli atau salinan split, dengan setiap salinan ditelusuri kembali ke sumber asli. Semua ini bersama-sama adalah "saudara." Semua kerabat memiliki satu objek penyimpanan komunal. Kerabat masih bisa makan satu sama lain, dan dapat membelah, menggunakan objek penyimpanan mereka sendiri, atau mengumpulkan energi tanpa mempengaruhi orang lain.

Transfer energi

Jika dua gumpalan bersebelahan (setelah bergerak), salah satu bot dapat mentransfer energi ke yang lain. Hal ini dilakukan dengan kembali SendNorth(amt), SendEast(amt), SendSouth(amt), atau SendWest(amt), dengan amtmenjadi angka yang mewakili jumlah yang dikirim. Ini dapat berupa jumlah berapa pun yang mampu dibeli pengirim, termasuk semua energinya. Disarankan bahwa gumpalan yang menerima energi diberitahu untuk tetap diam melalui penyimpanan komunal, sehingga tidak bergerak ketika energi sedang ditransfer (meskipun energi tidak akan dikurangkan dari total pengirim dalam kasus ini).

Fungsi, Penyimpanan, dan UID

Untuk memungkinkan perilaku belajar yang lebih kompleks, semua blob akan diberi integer UID (Unique Identifer). UID ini akan dihasilkan secara acak setiap peta, mencegah strategi berdasarkan target individu. Ketika fungsi gumpalan dipanggil, dilewatkan empat argumen:

  1. Panjang sisi peta sebagai bilangan bulat
  2. Objek dengan dua array:, pelletsdan blobs. Kedua array berisi objek, keduanya memiliki posproperti yang berisi posisi pelet atau gumpalan yang diformat sebagai [x,y]. Pelet akan memiliki energyproperti, sedangkan gumpalan akan memiliki uidproperti dan energyproperti
  3. Sebuah objek yang berisi berbagai properti dari gumpalan itu akan diteruskan ke: energy, uid, dan pos. The posarray diformat sebagai[x,y]
  4. Objek yang berisi dua objek penyimpanan gumpalan. Sebuah selfproperti berisi objek penyimpanan individu yang dapat dimodifikasi namun gumpalan melihat cocok (dengan memanipulasi properti dari objek yang dilewatkan), dan communalproperti yang dapat dimodifikasi oleh relatif apapun.

Gumpalan tidak dipindahkan segera untuk mencegah ternyata lebih awal / lambat memiliki keuntungan. Semua gerakan diproses dalam kelompok (Semua tabrakan / makan, maka semua pelet, lalu pemisahan, dll.) Jika gumpalan mendarat di pelet atau gumpalan yang lebih kecil dan, dalam proses menggunakan energi terakhirnya, gumpalan tersebut akan tetap memakan pelet / energi terlepas dari apakah itu akan membawa total energi di atas 0.

Agar gumpalan relatif mengenali satu sama lain, penyimpanan komunal harus digunakan untuk setiap gumpalan untuk merekam UID-nya dalam array, atau melalui beberapa sistem lain.

Nilai Pengembalian

Untuk memindahkan atau membagi, nilai kembali fungsi digunakan. Pertama, arti dari arah mata angin dalam hal koordinat:

  • Utara = -Y
  • Timur = + X
  • Selatan = + Y
  • Barat = -X

Perhatikan bahwa itu [0,0]adalah sudut kiri atas , dan Y meningkat saat Anda turun. Nilai pengembalian fungsi harus mengikuti aturan berikut:

  • Untuk tidak melakukan apa pun: Mengembalikan apa pun, 0, nol, tidak terdefinisi, salah, atau nilai lain apa pun yang setara dengan salah
  • Untuk Pindah: Kembalikan satu dari empat variabel global: Utara, Timur, Selatan, atau Barat, yang sama dengan "utara", "timur", "selatan", atau "barat" (yang juga dapat digunakan sebagai nilai pengembalian)
  • Untuk Berpisah: Kembalikan variabel global SplitNorth, SplitEast, SplitSouth, atau SplitWest, arah yang menunjukkan di mana menempatkan gumpalan baru

Jika perintah split dikembalikan dan jumlah energi yang dibutuhkan lebih besar atau sama dengan energi gumpalan, tidak ada yang akan terjadi. Gumpalan tidak akan dapat meninggalkan peta.

Fungsi Perpustakaan yang Sudah Ditentukan

Ada beberapa fungsi dasar yang tersedia secara default, untuk menghemat waktu:

taxiDist (pt1, pt2)

Mengembalikan jarak taksi antara dua titik (jarak X ditambah jarak 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)

Mengembalikan jarak antara dua titik sesuai dengan teorema pythagoras

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)

Mengambil arah yang dimasukkan, berputar 90 derajat searah jarum jam amt, lalu mengembalikan nilai baru.

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

Contoh Gumpalan

Gumpalan ini tidak akan bergerak sampai ia menemukan pelet di dekatnya. Kemudian, itu akan bergerak ke arah yang menurutnya paling mungkin untuk menghargai itu. Jika energinya pernah di atas 150, itu akan terpecah.

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

Aturan

  • Celah Standar dilarang. Juga, tidak ada lubang tidak standar.
  • Tidak ada gumpalan yang dapat mencoba untuk mengubah atau membaca data apa pun yang tidak diteruskan melalui parameternya
  • Tidak ada gumpalan yang dapat mencoba untuk memodifikasi variabel nilai-kembali untuk menyabot gumpalan lainnya
  • Putaran berlangsung sampai satu-satunya gumpalan yang tersisa adalah saudara
  • Tidak ada gumpalan yang dapat memodifikasi data dengan menyuntikkan fungsi ke dalam parameternya yang mengubah nilai menggunakan thiskata kunci
  • Semua kiriman harus dalam Javascript atau bahasa yang tidak terlalu berbeda dari Javascript (Python, misalnya). Semua jawaban akan dikonversi ke Javascript untuk kompetisi.
  • Pemenangnya adalah gumpalan yang telah mengumpulkan total energi tertinggi di semua putaran (dari pelet atau mengkonsumsi gumpalan kecil yang bukan saudara)

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

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

Program Redwolf
sumber
1
Bisakah Anda memperluas ini ke bahasa lain selain javascript?
Perwujudan Ketidaktahuan
@EmbodimentofIgnorance Kirim dalam bahasa apa pun yang Anda pilih, dan saya akan melakukan konversi ke JS.
Program Redwolf
Dapatkah gumpalan saling silang Contoh: blob1 di [0] [0] bergerak ke kanan dan gumpalan di [0] [1] bergerak ke kiri atau akankah gumpalan dengan energi lebih rendah dimakan?
fəˈnɛtɪk
Terkait
fəˈnɛtɪk
@ fəˈnɛtɪk Ya, bot dapat saling melintas. Juga, tantangan terkait adalah milikku (:
Program Redwolf

Jawaban:

3

Introvert

Introvert tidak suka gumpalan lainnya. Ketika ia melihat gumpalan yang tidak terkait, ia memakannya jika ia bisa, dan dengan enggan menerima kehadirannya jika tidak bisa, meskipun melarikan diri jika ia melihat tanda-tanda agresi. Ketika ia melihat gumpalan terkait , itu jarak itu sendiri. Namun, tidak bisa tidak banyak terpecah belah.

Detail Teknis

Fitur inti dari gumpalan ini adalah untuk membelah dan menyebar sehingga memaksimalkan visi gabungan gumpalan. Itu juga menggunakan sistem untuk mencegah dua dari mereka bersaing atas pelet.

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
sumber
Ini terlihat seperti bot yang cukup bagus! Kontes harus segera (hadiah akan berakhir besok).
Program Redwolf
@RedwolfPrograms Saya benar-benar mengujinya di pelari dan selalu menang dengan margin yang cukup besar.
RamenChef
Skor Rata-rata per Putaran: 357.544
Program Redwolf
1

Makanan Animasi

Bot sederhana, hanya untuk memulai kompetisi. Temukan koin terdekat, dan lanjutkan ke sana. Berdasarkan contoh bot.

function(map, near, me, storage) {
    var targs = near.pellets.map(el => taxiDist(el.pos, me.pos));
    var targ = near.pellets[targs.indexOf(Math.max(...targs))].pos;
    if (targ[0] == me.pos[0])
        return targ[1] < me.pos[1] ? North : South;
    return targ[0] < me.pos[0] ? West : East;
}
Muffinz72
sumber
Skor Rata-rata per Putaran: 24.933
Program Redwolf
Dan, dalam peristiwa yang mengejutkan, 5-liner (dimodifikasi sedikit untuk mengurangi bug) menang ke
Program 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));
}

Bot sebenarnya cukup sederhana, tetapi ini lebih dirancang sebagai bukti konsep bloblib , kumpulan fungsi dan fungsi yang saya rencanakan untuk gunakan dan kembangkan di seluruh bot lain (jangan ragu untuk menggunakan / memperluasnya sendiri juga)

Singkatnya, bot ini melakukan hal berikut:

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
sumber
Anda sekarang dapat melihat energi gumpalan, yang mungkin berguna
Program Redwolf
1
@RedwolfPrograms memperbarui bloblib untuk menentukan apakah gumpalan musuh adalah "ancaman" berdasarkan tingkat energinya.
Skidsdev
Skor Rata-rata per Putaran: 7.913
Program Redwolf
Sistem ini mungkin bisa digunakan untuk beberapa gumpalan yang bagus, tetapi yang ini tampaknya bertindak agak aneh.
Program Redwolf
1

Pengecut serakah

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

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

Bot ini tidak terlalu menarik. Kerjanya sesuai dengan dua prioritas:

  1. Jangan dimakan.
  2. Makanlah yang terdekat.

Ia tidak pernah meludah untuk memaksimalkan kemampuannya untuk memakan makanan lain.

Kamerad SparklePony
sumber
Saya akan mulai bekerja menerjemahkan ini! Ketika saya selesai, saya akan menyarankan edit dengan versi JS.
Program Redwolf
@RedwolfPrograms Kedengarannya bagus, terima kasih banyak.
Kamerad SparklePony
Saya pikir Anda perlu menambahkan if / else untuk memeriksa apakah sebenarnya ada benda baik / buruk. Ini menyebabkan beberapa masalah dalam versi JS.
Program Redwolf
@RedwolfPrograms Ini harus diperbaiki sekarang. Saya baru saja menambahkan pernyataan if yang memeriksa daftar objek menarik dan buruk yang dibuat untuk memastikan mereka tidak kosong. Sekali lagi, terima kasih atas bantuannya.
Kamerad SparklePony
@RedwolfPrograms Apakah Anda sudah menyiapkan versi JS?
RamenChef
1

SafetyBlob

Bot ini menggunakan beberapa logika yang sama dengan Safetycoin dari KOTH sebelumnya.

Bagaimana fungsinya

Bot ini akan menuju makanan yang dapat dijangkau sebelum bot yang lebih besar melakukan atau pada saat yang sama / sebelum bot yang lebih kecil. Jika tidak bisa melihat makanan yang memenuhi kriteria ini, ia akan bergerak ke arah acak (bias ke pusat). Jika energi mencapai 150 dan tidak bisa melihat makanan yang aman, ia akan terbelah menjadi salah satu arah yang dicap aman untuk dipindahkan.

Bot ini tidak melacak anak-anaknya sendiri tetapi mereka tidak boleh bertabrakan karena mekanisme keselamatan.

 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
sumber
Saya sudah menjalankan pengontrol, tapi saya mungkin akan melakukannya lagi nanti dengan bot baru ini. Sudah terlambat untuk menugaskan kembali karunia jika menang, tapi saya ingin tahu tentang hasilnya.
Program Redwolf
@RedwolfPrograms Tujuannya bukan untuk memenangkan hadiah.
fəˈnɛtɪk
Saya tahu, hanya memastikan Anda tahu (:
Program Redwolf