Fungsi Formik - Kontes Ratu Semut Bukit


104

Tonton langsung | Jawaban aktif | Tambahkan jawaban baru | Ruang obrolan | Kode sumber | Papan peringkat

Turnamen baru kapanpun dibutuhkan. Pemain baru dan pembaruan baru sangat disambut.

Ratu semut di lantai dansa dengan mengganti ubin berwarna

Bukan cuplikan game yang sebenarnya.

Setiap pemain mulai dengan satu semut - seorang ratu, yang mengumpulkan makanan. Setiap potong makanan dapat dipegang atau digunakan untuk menghasilkan pekerja. Pekerja juga mengumpulkan makanan untuk dibawa kembali ke ratu.

16 pemain bersaing dalam satu arena. Pemenangnya adalah ratu yang memegang paling banyak makanan setelah dia melakukan 30.000 giliran. Tangkapannya adalah bahwa semut hanya dapat berkomunikasi dengan mengubah warna kotak arena, yang juga dapat diubah oleh semut saingan ...

Menonton pertandingan

Ini adalah kompetisi JavaScript, yang berarti Anda dapat menonton pertandingan dimainkan langsung di peramban Anda dengan mengeklik tautan di bawah.

Klik di sini untuk menyaksikan pertandingan dimainkan secara langsung

Terima kasih banyak kepada Helka Homba untuk kontes Stack Snippet King of the Hill yang asli, Battlebots Team Merah vs. Biru-Pixel , dan Block Building Bot Flocks , yang memberikan gagasan tentang browser web yang menyelenggarakan KotH dan sangat menginformasikan kode untuk yang ini.

Terima kasih juga atas semua umpan balik dan pengujian dari orang-orang hebat di Sandbox dan di Obrolan.

Papan peringkat

Gambar tempat teratas papan peringkat

(Klik gambar untuk melihat papan peringkat penuh dan penjelasan tempat bersama - hanya beberapa pemain yang ditampilkan di sini untuk menghemat ruang.)

Leaderboard ini didasarkan pada pemain karena mereka pada hari Minggu 2 nd September 2018.

Tangkapan layar

Beberapa gambar bagaimana arena terlihat menjelang akhir pertandingan. Klik gambar untuk melihat ukuran penuh.

gambar arena gambar arena gambar arena gambar arena gambar arena gambar arena gambar arena gambar arena gambar arena gambar arena gambar arena gambar arena gambar arena gambar arena gambar arena gambar arena

Untuk mendapatkan gambaran tentang apa yang terjadi di arena dan bagaimana semua pola ini terbentuk, Anda dapat menjalankan game dan mengarahkan mouse ke atas arena untuk memperbesar dan melihat semut sedang bekerja. Lihat juga penjelasan menarik dalam jawabannya.

Arena

Arena adalah kotak toroidal (pembungkus tepi) sel kuadrat. Memiliki lebar 2500 dan tinggi 1000. Semua sel dimulai dengan warna 1.

Awalnya tepatnya 0,1% sel akan mengandung makanan. 2500 potong makanan akan tersebar secara seragam secara acak. Tidak ada makanan baru yang akan diperkenalkan selama pertandingan.

Ratu akan ditempatkan secara acak di sel kosong, tanpa jaminan bahwa mereka tidak akan berdekatan satu sama lain (meskipun ini sangat tidak mungkin).

Kemampuan semut

  • Sight: Setiap semut melihat 9 sel di lingkungan 3 oleh 3. Ia tidak memiliki pengetahuan tentang semut lain di luar lingkungan ini. Ia melihat isi dari masing-masing 9 sel (semut dan makanan lainnya), dan juga warna masing-masing sel .
  • Tidak ada memori: Setiap semut membuat keputusan berdasarkan apa yang dilihatnya - ia tidak ingat apa yang dilakukannya pada giliran sebelumnya dan tidak memiliki cara untuk menyimpan keadaan selain dalam warna sel arena.
  • Tanpa orientasi: Semut tidak tahu di mana ia berada atau ke arah mana ia menghadap - ia tidak memiliki konsep Utara. Lingkungan 3 by 3 akan disajikan padanya dengan orientasi yang dirotasi secara acak yang mengubah setiap belokan sehingga ia bahkan tidak dapat berjalan dalam garis lurus kecuali memiliki warna untuk membimbingnya. (Melakukan gerakan yang sama setiap belokan akan menghasilkan jalan acak daripada garis lurus.)
  • Memindahkan, menandai warna, dan memproduksi pekerja: Lihat Output di bawah ini.
  • Keabadian: Ini adalah semut dataran tinggi yang tidak bisa mati. Anda dapat membingungkan semut saingan dengan mengubah warna di sekitar mereka, atau membatasi mereka untuk bergerak dengan mengelilinginya dengan 8 semut Anda sendiri, tetapi mereka tidak dapat dirugikan selain dari ini.
  • Membawa makanan: Seorang pekerja dapat membawa hingga 1 potong makanan. Seorang ratu dapat membawa jumlah makanan yang sewenang-wenang.
  • Transfer makanan: Jika seorang pekerja berdekatan dengan seorang ratu (di salah satu dari 8 arah), makanan akan secara otomatis ditransfer dengan salah satu cara berikut:
    • Seorang pekerja sarat yang berdekatan dengan ratunya sendiri akan mentransfer makanannya kepada ratunya.
    • Seorang pekerja tanpa muatan yang berdekatan dengan ratu musuh akan mencuri 1 potong makanan, jika ada.

Seorang pekerja tidak dapat mencuri dari seorang pekerja, dan seorang ratu tidak dapat mencuri dari seorang ratu. Juga seorang pekerja tidak dapat mengambil makanan dari ratu sendiri, dan seorang ratu tidak dapat mencuri dari seorang pekerja musuh.

Perhatikan bahwa semut bergiliran secara berurutan dan perpindahan makanan terjadi pada akhir setiap giliran semut dan tidak mengambil belokan. Itu terjadi terlepas dari apakah seorang pekerja bergerak di samping seorang ratu atau seorang ratu bergerak di samping seorang pekerja, dan masih terjadi jika kedua semut yang terlibat berdiri diam untuk bergerak mereka.

Coding

Menyediakan fungsi tubuh

Setiap semut dikendalikan oleh fungsi semut. Setiap belokan, fungsi semut pemain dipanggil secara terpisah untuk setiap semut (tidak hanya sekali per pemain, tetapi sekali untuk ratu dan sekali untuk setiap pekerja yang dikontrol pemain). Setiap belokan, fungsi semut akan menerima inputnya dan mengembalikan gerakan untuk semut tertentu itu.

Posting jawaban yang berisi blok kode yang menunjukkan isi dari fungsi JavaScript, dan itu akan secara otomatis dimasukkan dalam controller (cukup segarkan halaman controller). Nama pemain membentuk judul jawaban, dalam bentuk # PlayerName(yang akan dipotong hingga maksimal 40 karakter dalam tabel pengontrol).

Tidak ada keadaan, tidak ada waktu, tidak ada yang acak

Suatu fungsi tidak boleh mengakses variabel global dan tidak harus menyimpan keadaan di antara belokan. Ini dapat menggunakan fungsi bawaan yang tidak melibatkan status penyimpanan. Misalnya, penggunaan Math.abs()baik-baik saja, tetapi Date.getTime()tidak boleh digunakan.

Fungsi semut hanya dapat menggunakan generator nomor acak pseudo yang disuplai sendiri, yang tidak menyimpan status. Misalnya, mungkin menggunakan warna / makanan / semut yang terlihat sebagai benih setiap belokan. Math.random()secara eksplisit dilarang, karena seperti hampir semua generator nomor pseudorandom, ia menyimpan status untuk maju ke nomor berikutnya secara berurutan.

Strategi acak sederhana masih dimungkinkan karena orientasi acak dari input - seekor semut yang selalu memilih arah yang sama akan melakukan jalan acak daripada jalur garis lurus. Lihat contoh jawaban untuk cara sederhana menggunakan keacakan ini dan menghindari keacakan ini .

Fungsi semut diizinkan mengandung fungsi lebih lanjut di dalam tubuhnya. Lihat jawaban yang ada untuk contoh bagaimana ini bisa bermanfaat.

Console.log

Anda dapat masuk ke konsol selama menguji pemain penantang baru, tetapi setelah diposting sebagai jawaban di sini, pemain tidak akan memiliki akses console.log. Mencoba menggunakannya akan menghasilkan kesalahan dan diskualifikasi hingga diedit. Ini akan membantu menjaga turnamen papan peringkat tetap cepat, sambil tetap memungkinkan kode debugging disisipkan ke area teks penantang baru.

Masukan dan keluaran

Memasukkan

Orientasi input akan dipilih secara acak untuk setiap semut dan untuk setiap belokan. Input akan diputar 0, 90, 180 atau 270 derajat, tetapi tidak akan pernah direfleksikan.

Sel diberi nomor dalam urutan membaca bahasa Inggris:

0 1 2
3 4 5
6 7 8

Fungsi semut akan menerima array yang disebut view, berisi objek untuk masing-masing dari 9 sel yang terlihat. Setiap objek akan memiliki yang berikut ini:

color: a number from 1 to 8
food: 0 or 1
ant: null if there is no ant on that cell, or otherwise an ant object

Jika sel berisi semut, objek semut akan memiliki yang berikut:

food: 0 or more (maximum 1 for a worker)
type: 1 to 4 for a worker, or 5 for a queen
friend: true or false

Semut dapat menentukan rinciannya sendiri dengan melihat semut di sel pusat view[4].ant,. Misalnya, view[4].ant.typeadalah 5 untuk seorang ratu, atau angka dari 1 hingga 4 untuk seorang pekerja (menunjukkan jenisnya).

Keluaran

Output dikembalikan sebagai objek yang mewakili tindakan yang harus diambil. Ini dapat memiliki salah satu dari yang berikut:

cell: a number from 0 to 8 (mandatory)
color: a number from 1 to 8 (optional)
type: a number from 1 to 4 (optional)

Jika colordan typedihilangkan atau nol, maka celltunjukkan sel yang akan dipindahkan.

Jika colortidak nol, sel yang ditunjukkan diatur ke warna itu.

Jika typetidak nol, semut pekerja dari jenis itu dibuat pada sel yang ditunjukkan. Hanya seorang ratu yang dapat menciptakan pekerja baru, dan hanya jika dia memiliki makanan, karena ini menghabiskan satu potong makanan per pekerja.

Output contoh:

{cell:0}: move to cell 0
{cell:4}: move to cell 4 (that is, do nothing, as 4 is the central cell)
{cell:4, color:8}: set own cell to color 8
{cell:6, type:1}: create a type 1 worker on cell 6
{cell:6, color:1}: set cell 6 to color 1
{cell:6, color:0}: equivalent to just `{cell:6}` - move rather than set color
{cell:6, type:0}: equivalent to just `{cell:6}` - move rather than create worker
{cell:6, color:0, type:0}: move to cell 6 - color 0 and type 0 are ignored

Output tidak valid:

{cell:9}: cell must be from 0 to 8
{cell:0, color:9}: color must be from 1 to 8
{cell:0, type:5}: type must be from 1 to 4 (cannot create a new queen)
{cell:4, type:1}: cannot create a worker on a non-empty cell
{cell:0, color:1, type:1}: cannot set color and create worker in the same turn

Semut yang bergerak ke sel yang berisi makanan akan secara otomatis mengambil potongan makanan.

Jenis pekerja

Setiap pekerja memiliki tipe , angka dari 1 hingga 4. Ini tidak memiliki arti bagi pengontrol, dan bagi pemain untuk melakukan seperti yang mereka inginkan. Seorang ratu bisa menghasilkan semua pekerjanya sebagai tipe 1, dan memberi mereka semua perilaku yang sama, atau dia bisa menghasilkan beberapa tipe pekerja dengan perilaku yang berbeda, mungkin tipe 1 sebagai pengumpul dan tipe 2 sebagai penjaga.

Nomor tipe pekerja diberikan oleh Anda ketika seorang pekerja dibuat, dan tidak dapat diubah setelahnya. Gunakan sesuai keinginan Anda.

Putar pesanan

Semut bergantian dalam urutan tertentu. Di awal permainan, ratu diberi urutan acak yang tidak berubah selama sisa permainan. Ketika seorang ratu menciptakan seorang pekerja, pekerja itu dimasukkan ke dalam urutan belokan di posisi sebelum ratu. Ini berarti bahwa semua semut lain milik semua pemain akan bergerak tepat satu kali sebelum pekerja baru mengambil belokan pertama.

Batasi jumlah pemain

Jelas jumlah pemain yang tidak terbatas tidak dapat masuk ke arena. Karena sekarang ada lebih dari 16 jawaban, setiap permainan akan menampilkan 16 pilihan yang dipilih secara acak. Kinerja rata-rata pada banyak gim akan memberikan papan peringkat yang menampilkan semua pemain, tanpa harus memiliki lebih dari 16 dalam satu gim.

Batas waktu per belokan

Setiap kali fungsi semut dipanggil, ia harus kembali dalam 15 milidetik. Karena batas waktu dapat dilampaui karena fluktuasi di luar kendali fungsi semut, rata-rata akan dihitung. Jika pada suatu titik rata-rata di atas 15 milidetik dan total waktu yang diambil oleh fungsi semut khusus itu untuk semua panggilan sejauh ini lebih dari 10 detik, pemain yang relevan akan didiskualifikasi.

Diskualifikasi

Ini berarti pemain tidak akan memenuhi syarat untuk menang dan fungsi semutnya tidak akan dipanggil lagi selama pertandingan itu. Mereka juga tidak akan dimasukkan dalam game lebih lanjut. Jika seorang pemain didiskualifikasi pada mesin turnamen selama pertandingan leaderboard maka itu akan dikeluarkan dari semua game leaderboard di masa depan sampai diedit.

Seorang pemain akan didiskualifikasi karena salah satu dari yang berikut untuk semutnya (ratu atau pekerja):

  • Melebihi batas waktu seperti yang dijelaskan (rata-rata lebih dari 10 detik).
  • Mengembalikan langkah yang tidak valid seperti yang dijelaskan di bawah Output.
  • Sel yang akan dipindahkan berisi semut.
  • Sel untuk bergerak berisi makanan dan semut sudah menjadi pekerja yang sarat.
  • Sel untuk menghasilkan pekerja tidak kosong (berisi makanan atau semut).
  • Seorang pekerja sedang berusaha menghasilkan seorang pekerja.

Mungkin tampak sulit untuk mendiskualifikasi gerakan yang tidak valid, alih-alih hanya menafsirkan ini sebagai gerakan yang tidak ada. Namun, saya percaya bahwa menegakkan implementasi yang benar akan mengarah pada strategi yang lebih menarik dari waktu ke waktu. Ini tidak dimaksudkan sebagai tantangan tambahan, jadi alasan yang jelas akan ditampilkan ketika pemain didiskualifikasi, dengan input dan output khusus di samping untuk membantu memperbaiki kode.

Beberapa jawaban dan pengeditan

Anda dapat memberikan beberapa jawaban, asalkan mereka tidak bekerja sama dengan yang lain. Asalkan setiap jawaban bekerja semata-mata menuju kemenangannya sendiri, Anda diizinkan untuk menyesuaikan strategi Anda untuk mengambil keuntungan dari kelemahan dalam strategi spesifik lainnya, termasuk mengubah warna sel untuk membingungkan atau memanipulasi mereka. Ingatlah bahwa semakin banyak jawaban masuk, kemungkinan bertemu dengan pemain tertentu dalam permainan tertentu akan berkurang.

Anda juga dapat mengedit jawaban Anda kapan pun Anda mau. Terserah Anda apakah Anda memposting jawaban baru atau mengedit yang sudah ada. Asalkan permainan tidak dibanjiri dengan banyak variasi yang hampir identik, seharusnya tidak ada masalah.

Jika Anda membuat variasi dari jawaban orang lain, harap ingat untuk memberi mereka pujian dengan menautkan jawaban mereka dari Anda.

Mencetak gol

Pada akhir setiap pertandingan, skor pemain adalah jumlah pemain lain yang memiliki lebih sedikit makanan yang dibawa oleh ratu mereka. Makanan yang dibawa oleh pekerja tidak dihitung. Skor ini ditambahkan ke papan peringkat, yang ditampilkan dalam urutan skor rata-rata per game.

Tempat gabungan menunjukkan bahwa urutan pemain belum konsisten antara 6 himpunan bagian dari permainan yang dimainkan sejauh ini. Daftar permainan dibagi menjadi 6 himpunan bagian karena ini adalah jumlah minimum yang akan memberikan probabilitas kurang dari 5% bahwa sepasang pemain tertentu akan diberikan tempat yang berbeda dalam urutan yang salah.

Obrolan

Untuk menjaga bagian komentar jelas di sini, silakan gunakan ruang obrolan khusus untuk setiap pertanyaan dan diskusi. Komentar pada posting ini kemungkinan akan dihapus setelah beberapa saat, sedangkan pesan di ruang obrolan akan disimpan secara permanen.

Sekadar memberi tahu Anda, saya akan lebih cenderung memilih jawaban yang menyertakan penjelasan yang jelas dan menarik tentang cara kerja kode.


2
@DestructibleLemon demi siapa pun yang membaca komentar ini, saya telah menjawab bahwa di ruang obrolan
trichoplax


7
Hei, saya membuat sesuatu ! Anda mungkin menganggapnya menarik karena terinspirasi oleh tantangan ini dan mencakup implementasi pengujian Fungsi Formik .
Dave

2
@Dave Pengontrol Anda sangat cepat :) - tapi izinkan saya menyebutkan bahwa skornya tampaknya berbeda dari aslinya dalam kasus di mana ratu terikat untuk makanan di akhir pertandingan. Skornya haruslah jumlah peserta lain yang ratu-nya ( kurang ) makan. Misalnya, jika tiga pemain memiliki 0 makanan di akhir, mereka semua harus mendapat skor nol untuk permainan ini, bukan tiga.
GNiklasch

2
@GNiklasch terima kasih; tetap. Saya juga melihat bahwa semut Anda mendominasi permainan sekarang. Impresif!
Dave

Jawaban:


20

Semut Forensik

Semua jawaban saya membagikan fungsi pembantu tingkat rendah yang sama. Cari "Logika tingkat tinggi dimulai di sini" untuk melihat kode spesifik untuk jawaban ini.

// == Shared low-level helpers for all solutions ==

var QUEEN = 5;

var WHITE = 1;
var COL_MIN = WHITE;
var COL_LIM = 9;

var CENTRE = 4;

var NOP = {cell: CENTRE};

var DIR_FORWARDS = false;
var DIR_REVERSE = true;
var SIDE_RIGHT = true;
var SIDE_LEFT = false;

function sanity_check(movement) {
  var me = view[CENTRE].ant;
  if(!movement || movement.cell < 0 || movement.cell > 8) {
    return false;
  }
  if(movement.type) {
    if(movement.color) {
      return false;
    }
    if(movement.type < 1 || movement.type > 4) {
      return false;
    }
    if(view[movement.cell].ant || view[movement.cell].food) {
      return false;
    }
    if(me.type !== QUEEN || me.food < 1) {
      return false;
    }
    return true;
  }
  if(movement.color) {
    if(movement.color < COL_MIN || movement.color >= COL_LIM) {
      return false;
    }
    if(view[movement.cell].color === movement.color) {
      return false;
    }
    return true;
  }
  if(view[movement.cell].ant) {
    return false;
  }
  if(view[movement.cell].food + me.food > 1 && me.type !== QUEEN) {
    return false;
  }
  return true;
}

function as_array(o) {
  if(Array.isArray(o)) {
    return o;
  }
  return [o];
}

function best_of(movements) {
  var m;
  for(var i = 0; i < movements.length; ++ i) {
    if(typeof(movements[i]) === 'function') {
      m = movements[i]();
    } else {
      m = movements[i];
    }
    if(sanity_check(m)) {
      return m;
    }
  }
  return null;
}

function play_safe(movement) {
  // Avoid disqualification: no-op if moves are invalid
  return best_of(as_array(movement)) || NOP;
}

var RAND_SEED = (() => {
  var s = 0;
  for(var i = 0; i < 9; ++ i) {
    s += view[i].color * (i + 1);
    s += view[i].ant ? i * i : 0;
    s += view[i].food ? i * i * i : 0;
  }
  return s % 29;
})();

var ROTATIONS = [
  [0, 1, 2, 3, 4, 5, 6, 7, 8],
  [6, 3, 0, 7, 4, 1, 8, 5, 2],
  [8, 7, 6, 5, 4, 3, 2, 1, 0],
  [2, 5, 8, 1, 4, 7, 0, 3, 6],
];

function try_all(fns, limit, wrapperFn, checkFn) {
  var m;
  fns = as_array(fns);
  for(var i = 0; i < fns.length; ++ i) {
    if(typeof(fns[i]) !== 'function') {
      if(checkFn(m = fns[i])) {
        return m;
      }
      continue;
    }
    for(var j = 0; j < limit; ++ j) {
      if(checkFn(m = wrapperFn(fns[i], j))) {
        return m;
      }
    }
  }
  return null;
}

function identify_rotation(testFns) {
  // testFns MUST be functions, not constants
  return try_all(
    testFns,
    4,
    (fn, r) => fn(ROTATIONS[r]) ? ROTATIONS[r] : null,
    (r) => r
  );
}

function near(a, b) {
  return (
    Math.abs(a % 3 - b % 3) < 2 &&
    Math.abs(Math.floor(a / 3) - Math.floor(b / 3)) < 2
  );
}

function try_all_angles(solverFns) {
  return try_all(
    solverFns,
    4,
    (fn, r) => fn(ROTATIONS[r]),
    sanity_check
  );
}

function try_all_cells(solverFns, skipCentre) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i === CENTRE && skipCentre) ? null : fn(i)),
    sanity_check
  );
}

function try_all_cells_near(p, solverFns) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i !== p && near(p, i)) ? fn(i) : null),
    sanity_check
  );
}

function ant_type_at(i, friend) {
  return (view[i].ant && view[i].ant.friend === friend) ? view[i].ant.type : 0;
}

function friend_at(i) {
  return ant_type_at(i, true);
}

function foe_at(i) {
  return ant_type_at(i, false);
}

function foe_near(p) {
  for(var i = 0; i < 9; ++ i) {
    if(foe_at(i) && near(i, p)) {
      return true;
    }
  }
  return false;
}

function move_agent(agents) {
  var me = view[CENTRE].ant;
  var buddies = [0, 0, 0, 0, 0, 0];
  for(var i = 0; i < 9; ++ i) {
    ++ buddies[friend_at(i)];
  }

  for(var i = 0; i < agents.length; i += 2) {
    if(agents[i] === me.type) {
      return agents[i+1](me, buddies);
    }
  }
  return null;
}

function grab_nearby_food() {
  return try_all_cells((i) => (view[i].food ? {cell: i} : null), true);
}

function go_anywhere() {
  return try_all_cells((i) => ({cell: i}), true);
}

function colours_excluding(cols) {
  var r = [];
  for(var i = COL_MIN; i < COL_LIM; ++ i) {
    if(cols.indexOf(i) === -1) {
      r.push(i);
    }
  }
  return r;
}

function generate_band(start, width) {
  var r = [];
  for(var i = 0; i < width; ++ i) {
    r.push(start + i);
  }
  return r;
}

function colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function(c) {
      return colours[(colours.indexOf(c) + 1) % colours.length];
    }
  };
}

function random_colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function() {
      return colours[RAND_SEED % colours.length];
    }
  };
}

function fast_diagonal(colourBand) {
  var m = try_all_angles([
    // Avoid nearby checked areas
    (rot) => {
      if(
        !colourBand.contains(view[rot[0]].color) &&
        colourBand.contains(view[rot[5]].color) &&
        colourBand.contains(view[rot[7]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // Go in a straight diagonal line if possible
    (rot) => {
      if(
        !colourBand.contains(view[rot[0]].color) &&
        colourBand.contains(view[rot[8]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // When in doubt, pick randomly but avoid doubling-back
    (rot) => (colourBand.contains(view[rot[0]].color) ? null : {cell: rot[0]}),

    // Double-back when absolutely necessary
    (rot) => ({cell: rot[0]})
  ]);

  // Lay a colour track so that we can avoid doubling-back
  // (and mess up our foes as much as possible)
  if(!colourBand.contains(view[CENTRE].color)) {
    var prevCol = m ? view[8-m.cell].color : WHITE;
    return {cell: CENTRE, color: colourBand.next(prevCol)};
  }

  return m;
}

function follow_edge(obstacleFn, side) {
  // Since we don't know which direction we came from, this can cause us to get
  // stuck on islands, but the random orientation helps to ensure we don't get
  // stuck forever.

  var order = ((side === SIDE_LEFT)
    ? [0, 3, 6, 7, 8, 5, 2, 1, 0]
    : [0, 1, 2, 5, 8, 7, 6, 3, 0]
  );
  return try_all(
    [obstacleFn],
    order.length - 1,
    (fn, i) => (fn(order[i+1]) && !fn(order[i])) ? {cell: order[i]} : null,
    sanity_check
  );
}

function start_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => ((
      !protectedCols.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 2 : 0]].color) &&
      !colourBand.contains(view[rot[1]].color)
    )
      ? {cell: rot[right ? 5 : 3], color: colourBand.next(WHITE)}
      : null)
  ]);
}

function lay_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => {
      var ahead = rot[right ? 2 : 0];
      var behind = rot[right ? 8 : 6];
      if(
        colourBand.contains(view[behind].color) &&
        !protectedCols.contains(view[ahead].color) &&
        !colourBand.contains(view[ahead].color) &&
        !colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        return {cell: ahead, color: colourBand.next(view[behind].color)};
      }
    }
  ]);
}

function follow_dotted_path(colourBand, side, direction) {
  var forwards = (direction === DIR_REVERSE) ? 7 : 1;
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    // Cell on our side? advance
    (rot) => {
      if(
        colourBand.contains(view[rot[right ? 5 : 3]].color) &&
        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[0]].color) &&
        !colourBand.contains(view[rot[2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    },

    // Cell ahead and behind? advance
    (rot) => {
      var passedCol = view[rot[right ? 8 : 6]].color;
      var nextCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(passedCol) &&
        nextCol === colourBand.next(passedCol) &&

        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[right ? 0 : 2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    }
  ]);
}

function escape_dotted_path(colourBand, side, newColourBand) {
  var right = (side === SIDE_RIGHT);
  if(!newColourBand) {
    newColourBand = colourBand;
  }

  return try_all_angles([
    // Escape from beside the line
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        !colourBand.contains(view[rot[right ? 8 : 6]].color) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[rot[7]].color) ||
        colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        // not oriented, or in a corner
        return null;
      }
      return best_of([
        {cell: rot[right ? 0 : 2], color: newColourBand.next(approachingCol)},
        {cell: rot[right ? 3 : 5]},
        {cell: rot[right ? 0 : 2]},
        {cell: rot[right ? 6 : 8]},
        {cell: rot[right ? 2 : 0]},
        {cell: rot[right ? 8 : 6]},
        {cell: rot[right ? 5 : 3]}
      ]);
    },

    // Escape from inside the line
    (rot) => {
      if(
        !colourBand.contains(view[rot[7]].color) ||
        !colourBand.contains(view[rot[1]].color) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      return best_of([
        {cell: rot[3]},
        {cell: rot[5]},
        {cell: rot[0]},
        {cell: rot[2]},
        {cell: rot[6]},
        {cell: rot[8]}
      ]);
    }
  ]);
}

function latch_to_dotted_path(colourBand, side) {
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(approachingCol) &&
        view[rot[right ? 8 : 6]].color === colourBand.next(approachingCol) &&
        !colourBand.contains(view[rot[right ? 5 : 3]].color)
      ) {
        // We're on the wrong side; go inside the line
        return {cell: rot[right ? 5 : 3]};
      }
    },

    // Inside the line? pick a side
    (rot) => {
      var passedCol = view[rot[7]].color;
      var approachingCol = view[rot[1]].color;
      if(
        !colourBand.contains(passedCol) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      if((approachingCol === colourBand.next(passedCol)) === right) {
        return best_of([{cell: rot[3]}, {cell: rot[6]}, {cell: rot[0]}]);
      } else {
        return best_of([{cell: rot[5]}, {cell: rot[2]}, {cell: rot[8]}]);
      }
    }
  ]);
}


// == High-level logic begins here ==


var PARTNER = 1;
var SENTINEL = 2;

var COL_DANCING1 = 8;
var COL_DANCING2 = 7;
var SAFE_COLOURS = random_colour_band(colours_excluding([WHITE, COL_DANCING1]));

function pass_time() {
  // Wait patiently for the blockage to go away by setting
  // random cell colours (unless we're near the sentinel)
  for(var i = 0; i < 9; ++ i) {
    if(i !== 4 && friend_at(i) === SENTINEL) {
      return null;
    }
  }
  return {cell: 0, color: SAFE_COLOURS.next()};
}

function move_sentinel(me, buddies) {
  // Our job is to be a sentinel showing when the queen has wrapped around.
  // We are created first, so will move first.
  // We won't find any food.

  if(!buddies[QUEEN] && !buddies[PARTNER]) {
    // No ongoing dance; make sure our state is good for when they arrive
    return try_all_angles([
      {cell: CENTRE, color: WHITE},
      (rot) => ({cell: rot[1], color: COL_DANCING2}),
      (rot) => ((view[rot[0]].color === COL_DANCING1)
        ? {cell: rot[0], color: SAFE_COLOURS.next()}
        : null)
    ]);
  }

  // Dance when queen passes
  var danceStage = view[CENTRE].color;

  if(danceStage === WHITE) {
    // Dance has not begun yet, but queen & partner are nearby
    return try_all_angles((rot) => {
      if(friend_at(rot[5]) === QUEEN && friend_at(rot[8]) === PARTNER) {
        return {cell: CENTRE, color: COL_DANCING1};
      }
    });
  }

  if(danceStage === COL_DANCING1) {
    if(buddies[PARTNER]) {
      return null; // Wait for partner to see us
    }
    // Partner saw us @8 and moved down, queen followed.
    // We must also move down (will end up on a COL_DANCING2)
    return try_all_angles((rot) =>
      ((friend_at(rot[8]) === QUEEN) ? {cell: rot[7]} : null));
  }

  // Move towards queen counter-clockwise when she's diagonally connected
  return try_all_angles((rot) =>
    ((friend_at(rot[2]) === QUEEN) ? {cell: rot[1]} : null));
}

function move_partner(me, buddies) {
  // Our job is to travel with the queen and keep her oriented.
  // We are created second, so move after the sentinel.
  // Any food we find will immediately go to the queen, since
  // we are adjacent at all times.

  // Queen will be N of us; orient ourselves
  var rot = identify_rotation((rot) => friend_at(rot[1]) === QUEEN);

  if(!rot) {
    // Queen is lagging or lost;
    return null;
  }

  var danceStage = view[rot[0]].color;
  if(
    friend_at(rot[0]) === SENTINEL &&
    (danceStage === COL_DANCING1 || danceStage === COL_DANCING2)
  ) {
    // Dance down (queen will follow)
    return {cell: rot[7]};
  }

  if(view[rot[0]].ant) {
    // Queen is blocked
    return null;
  }

  // Lead queen if both can move
  return {cell: rot[3]};
}

function move_queen(me, buddies) {
  // Our job is to travel over the entire level collecting food.
  // We move last.

  if(buddies[PARTNER]) {
    // Partner will be S or SW of us; follow if they are ahead
    return try_all_angles((rot) =>
      (friend_at(rot[6]) === PARTNER) ? {cell: rot[3]} : null);
  }

  var rot = identify_rotation((rot) => friend_at(rot[3]) === SENTINEL);
  if(rot && view[rot[0]].color >= 7) {
    // Dance down (follow partner)
    return {cell: rot[7]};
  }

  // We're on our own, or the buddy strategy failed. Start again.

  rot = identify_rotation((rot) => friend_at(rot[5]) === SENTINEL);
  if(rot && me.food >= 1) {
    // Already have a sentinel; just need a partner
    return best_of([
      {cell: rot[7], type: PARTNER},
      {cell: rot[6], type: PARTNER},
    ]);
  } else if(me.food >= 2) {
    // Create sentinel first so that we'll know to create the partner next.
    // (ensure the sentinel is created on a white cell so that it won't
    // think it's dancing)
    return try_all_angles(
      (rot) => ((view[rot[5]].color === WHITE)
        ? {cell: rot[5], type: SENTINEL} : null),
      (rot) => ({cell: rot[5], color: WHITE})
    );
  }

  // Not able to start yet; fall back to lone behaviour:
  // Random-walk until we find or make a buddy
  return best_of([
    grab_nearby_food,
    fast_diagonal.bind(null, SAFE_COLOURS),
    go_anywhere
  ]);
}

return play_safe([move_agent([
  PARTNER, move_partner,
  SENTINEL, move_sentinel,
  QUEEN, move_queen,
]), pass_time]);

Semut forensik mengambil pendekatan ilmiah untuk menyapu grid. Setelah perebutan panik awal untuk makanan, 2 semut pekerja akan dibuat. Perannya adalah:

Ratu

Ratu akan bekerja sama dengan pasangannya untuk melakukan perjalanan dalam garis lurus dengan kecepatan cahaya. Tak satu pun dari mereka akan menyimpang untuk mengambil makanan; mereka hanya mengambil apa pun yang mereka temukan.

Pasangan

Pasangan itu bergerak dengan sang ratu, menjaga dia menghadap ke arah yang sama. Karena kedua semut dapat bergerak 1 bujur sangkar setiap belokan, mereka dapat bertahan dalam garis lurus tanpa membuang waktu mengecat tanah.

Jika pasangan itu pernah menemukan makanan, itu akan segera pergi ke ratu, karena mereka berdekatan setiap saat.

Penjaga

Semut yang paling penting. Yang ini tetap diletakkan sampai ratu dan pasangan mencapainya, lalu memberitahu mereka untuk memindahkan 2 piksel, dan memindahkan 2 piksel itu sendiri. Hal ini menyebabkan ratu dan pasangannya secara bertahap menyapu seluruh papan (well, sekitar 30 piksel dari itu) Itu hanya bergerak ketika ratu di dekatnya, sehingga makanan yang ditemukannya akan segera diserahkan.

Di waktu luangnya, hobi penjaga itu termasuk mengecat tanah di sekitarnya secara acak untuk mudah-mudahan mengacaukan pesaing.


Ini tampil sangat konsisten; di antara mereka, mereka dapat menyapu 2 sel setiap frame, yang lebih dari 30000 frame berarti 60000 sel, dan dengan 0,1% mengandung makanan, itu berarti skor akhir 60, yang mereka capai secara konsisten.


(dan ini yang lain yang saya siapkan sementara pertanyaannya adalah beta! - itu sudah saya selesaikan sekarang; saya yakin ini akan dikalahkan dengan cepat!)
Dave

Skornya sangat konsisten. Akan menarik untuk melihat bagaimana itu terpengaruh ketika arena dipenuhi dengan lebih banyak pesaing ...
trichoplax

Aku ingin tahu apakah menambahkan pasangan lain di sisi lain ratu akan membantu?
K Zhang

1
@ KZhang Saya pikir itu akan (secara teoritis itu akan meningkatkan skor menjadi ~ 90), tapi cukup sulit menjaga keduanya tetap sinkron bersama dengan penjaga! Tarian "pindahkan semua orang 2 piksel" butuh waktu untuk mencari tahu. Pergi ke 3 piksel akan memblokir salah satu trik yang saya andalkan (penjaga menyiapkan ruang di sekitar itu sendiri di muka).
Dave

Ke atas papan peringkat pertama ...
trichoplax

18

Penambang Sliding 6.4

const DEBUG = false;
const ADD = (a,b) => a + b;
var toReturn;
var me = view[4].ant;
me.me = true; // for basedOn to know
var food = me.food;
var type = me.type;
var isQueen = type == 5;

// raw directions
const UL = 0; const U  = 1; const UR = 2;
const L  = 3; const C  = 4; const R  = 5;
const DL = 6; const D  = 7; const DR = 8;

// directions from the reference point
const ul = 16; const u  = 17; const ur = 18;
const l  = 19; const c  = 20; const r  = 21;
const dl = 22; const d  = 23; const dr = 24;
const rp = 16;

function allRots (arr) {
  return [arr,
  [arr[2], arr[5], arr[8],
   arr[1], arr[4], arr[7],
   arr[0], arr[3], arr[6]],

  [arr[8], arr[7], arr[6],
   arr[5], arr[4], arr[3],
   arr[2], arr[1], arr[0]],

  [arr[6], arr[3], arr[0],
   arr[7], arr[4], arr[1],
   arr[8], arr[5], arr[2]]];
}
var allVRots = allRots(view);

function rotateCW3([[a,b,c],[d,e,f],[g,h,i]]) {
  return [[g,d,a],[h,e,b],[i,f,c]]
}

function on (where, what) {
  if (Array.isArray(where)) return where.some(c=>on(c, what));
  if (Array.isArray(what)) return what.some(c=>on(where, c));
  return basedOn(get(where), what);
}
function find (what) {
  return view.findIndex(c=>basedOn(c, what));
}
function findAll (what) {
  return view.map((c,i)=>[c,i]).filter(c=>basedOn(c[0], what)).map(c=>c[1]);
}
function count (what) {
  return findAll(what).length;
}
function findRel (what) {
  return ref(find(what));
}
function findAllRel (what) {
  return findAll(what).map(c=>ref(c));
}
function found (what) {
  return find(what) != -1;
}
function get (dir) {
  if (Array.isArray(dir)) return dir.map(c=>get(c));
  return view[raw(dir)];
}
function deq (a, b) {
  return a==b || raw(a)==raw(b);
}

// returns a random number from 0 to 4, based on the rotation. Will always have a possibility of being 0
function random4 () {
  var scores = allRots(view.map(c=>c.color)).map((c) => {
    let cscore = 0;
    c.forEach((c) => {
      cscore*= 8;
      cscore+= c-1;
    });
    return cscore;
  });
  var bestscore = -1, bestindex = 1;
  scores.forEach((score, index) => {
    if (score > bestscore) {
      bestscore = score;
      bestindex = index;
    }
  })
  return bestindex;
}

function rotate (what, times) {
  for (var i = 0; i < times; i++) what = [2,5,8,1,4,7,0,3,6][what];
  return what;
}

function raw(dir) {
  if (dir&rp) return rotate(dir&~rp, selectedRot);
  return dir;
}

function ref(dir) {
  if (dir == -1) return -1;
  if (dir&rp) return dir;
  return rotate(dir, 4-selectedRot)|rp;
}

function move(dir, force) {
  if (Array.isArray(dir)) return dir.some(c=>move(c, force));
  dir = raw(dir);
  return result({cell:dir}, force);
}

function color(dir, col) {
  if (Array.isArray(dir)) return dir.some(cdir => !color(cdir, col));
  dir = raw(dir);
  if (view[dir].color == col) return true;
  result({cell:dir, color:Math.abs(col)});
  return false;
}

function rcolOf(what) {
  return Number.isInteger(what)? what : what.color;
}

function colOf(what) {
  return Math.abs(Number.isInteger(what)? what : what.color);
}
function sees(c1,c2) {
  c1 = raw(c1);
  c2 = raw(c2);
  return Math.abs(c1%3-c2%3)<2 && Math.abs(Math.floor(c1/3)-Math.floor(c2/3))<2;
}

function spawn(dir, t) {
  if (Array.isArray(t)) return t.some(c=>spawn(dir, c));
  if (Array.isArray(dir)) return dir.some(c=>spawn(c, t));
  dir = raw(dir);
  return result({cell:dir, type:t});
}
// repairs a single cell
function correct(dir) {
  dir = raw(dir);
  let col = colOf(selectedPt[dir]);
  if (col && view[dir].color != col) {
    color(dir, col);
    return false;
  }
  return true;
}
// if pattern is repaired, returns true, otherwise fixes one cell
// firstdirs is lowercase (if you do want it to be from the patterns POV)
function repair(firstdirs, onlyThose) {
  //log("FD",firstdirs);
  var found = [];
  view.forEach((v, i) => {
    let col = colOf(selectedPt[i]);
    if (col && v.color != col) {
      found.push(i);
    }
  });
  if (found.length == 0) return true;
  if (firstdirs && (firstdirs = firstdirs.map(c=>raw(c))).some(c=>found.includes(c))) {
    let dir = firstdirs.find(c=>found.includes(c));
    let col = colOf(selectedPt[dir]);
    color(dir, col);
    return false;
  }
  if (!onlyThose) {
    let dir = found[random4() % found.length];
    let col = colOf(selectedPt[dir]);
    color(dir, col);
    return false;
  } else return true;
}

function flatten (arr) {
  return arr.reduce((a,b)=>a.concat(b));
}

var selectedHp, selectedVp, selectedPt, selectedRot;

class Pattern {
  constructor(pattern, inherit) {
    this.pt = pattern;
    if (inherit) {
      this.vp = inherit.vp;
      this.hp = inherit.hp;
      this.rot = inherit.rot;
    } else {
      this.vp = 0;
      this.hp = 0;
      this.rot = 0;
    }
  }

  rotateClockwise() {
    var arr = [];
    for (var i = 0; i < this.pt[0].length; i++) {
      var sarr = [];
      for (var j = this.pt.length-1; j >= 0; j--) {
        sarr.push(this.pt[j][i]);
      }
      arr.push(sarr);
    }
    //log(arr);
    var res = new Pattern(arr, this);
    res.rot = (this.rot+1) % 4;
    return res;
  }

  select(x, y, w, h) {
    var res = new Pattern(this.pt.slice(y, y+h).map(c=>c.slice(x, x+w)), this);
    res.hp+= x;
    res.vp+= y;
    return res;
  }

  rots(dir) {
    var pts = [];
    var pt = new Pattern(this.pt, this);
    for (let i = 0; i < this.lengthIn(dir); i++) {
      pts.push(pt);
      pt = pt.rotate(dir);
    }
    return pts;
  }

  map(fn) {
    return new Pattern(this.pt.map(ln=>ln.map(fn)), this);
  }

  lengthIn(dir) {
    if (dir == U || dir == D) return this.pt.length;
    else if (this.pt.length > 0) return this.pt[0].length;
    else return 0;
  }
  rotate(dir) { // moves the center to that direction, shifting the side
    if (dir == R) {
      var res = new Pattern(this.pt.map(c=>((h,...t)=>t.concat(h))(...c)), this);
      res.hp++;
      return res;
    }
    if (dir == L) {
      var res = new Pattern(this.pt.map(a=>a.slice(-1).concat(a.slice(0,-1))), this);
      res.hp++;
      return res;
    }
    if (dir == D) {
      var res = new Pattern(((h,...t)=>t.concat([h]))(...this.pt), this);
      res.vp++;
      return res;
    }
    throw "rotate unimplemented dir!";
  }

  center(dir) { // moves the center to that direction
    if (dir == R) {
      var res = new Pattern(this.pt.map(c=>((h,...t)=>t.concat(0))(...c)), this);
      res.hp++;
      return res;
    }
    if (dir == L) {
      var res = new Pattern(this.pt.map(a=>[0].concat(a.slice(0,-1))), this);
      res.hp++;
      return res;
    }
    if (dir == D) {
      var res = new Pattern(((h,...t)=>t.concat([new Array(h.length)]))(...this.pt), this);
      res.vp++;
      return res;
    }
    throw "center unimplemented dir!";
  }

  setSize(xs, ys) {
    var arr = [];
    for (let y = 0; y < ys; y++) {
      var ca = [];
      for (let x = 0; x < xs; x++) {
        ca.push(this.pt[y % this.pt.length][x % this.pt[0].length]);
      }
      arr.push(ca);
    }
    return new Pattern(arr, this);
  }

  static add(pattern, action, scorer, presetRot) {
    if (Array.isArray(pattern)) pattern = new Pattern(pattern);
    pattern = pattern.setSize(3,3);
    var cpt = pattern.setSize(3,3);
    var orig = cpt.pt;
    for (let i = 0; i < 4; i++) {
      cpt = cpt.rotateClockwise();
      if (!presetRot || presetRot == cpt.rot) {
        cpt.action = action;
        cpt.scorer = scorer;
        cpt.raw = orig;
        cpt.view = allVRots[cpt.rot];
        allPatterns.push(cpt);
      }
    }
  }


  static choose() {
    var maxScore = -1e307;
    var nextScore = -1e308;
    var maxPt;
    allPatterns.forEach((c) => {
      // null  = easy
      // 0     = bad queen
      // false = no match
      // >0    = score
      var falseN = 0;
      var corrects = c.raw.reduce((a,b)=>a.concat(b)).map((guess, index) => {
        var bo = basedOn(c.view[index], guess, true);
        var ant = guess.ant;
        if (ant && basedOn(c.view[index], {ant})) bo+= 1;
        if (bo === 0) return 0;
        if (bo === false) return false;
        if (bo && rcolOf(guess) > 0) return bo;
        var easy = rcolOf(guess)<=0;
        if (easy) {
          falseN++;
          return null;
        }
        return bo;
      });
      var corrstring = corrects.map((chr,i)=>chr>0? (colOf(c.raw[Math.floor(i/3)][i%3])==1? "W" : "#") : chr===null? "-" : " ").join("");
      function match(pt) {
        return new RegExp(pt.replace(/@/g, "[#-W]").replace(/C/g, "[#-]")).test(corrstring);
      }
      var score = corrects.reduce(ADD)*9/(9-falseN);
      if (match(".?(...)?##.##.*")) {
        if (match("(...)?@@@@@@.*|.?@@.@@.@@.?")) score+= foundEnemy? 5 : 3;
        else score+= foundEnemy? 3 : 1;
      } else if (!foundEnemy) score = Math.min(score/2, 5);
      if (c.scorer instanceof Function) score = c.scorer(score, c, corrects, falseN, match);
      if (DEBUG && score > -1) log(
        "scored", score,
        "corr", /*corrects.map(c =>
          (c===false?"F":c===null?"N":c===true?"T":c)
        )*/corrstring,
        "pt", c.raw.map(c=>c.ant? "A"+c.ant.type : c), c.hp, c.vp);
      if (score >= maxScore) {
        nextScore = maxScore;
        maxScore = score;
        c.corrstr = corrstring;
        maxPt = c;
      }
    });
    var flattened = maxPt.pt.reduce((a,b)=>a.concat(b));
    Pattern.hardcorr = flattened.map((guess, index) => rcolOf(guess)<2? 0 : basedOn(view[index], guess)).reduce(ADD);
    Pattern.corrstr = maxPt.corrstr;
    Pattern.corr = flattened.map((guess, index) => basedOn(view[index], guess)).reduce(ADD);
    Pattern.incorr = 9-Pattern.corr;
    Pattern.confidence = maxScore-nextScore;
    selectedRot = maxPt.rot;
    Pattern.action = maxPt.action;
    selectedPt = flattened;
    selectedHp = maxPt.hp;
    Pattern.raw = maxPt.raw;
    Pattern.view = maxPt.view;
    selectedVp = maxPt.vp;
    Pattern.score = maxScore;
    if (DEBUG) log("score", maxScore, "confidence", Pattern.confidence, "corr", Pattern.corr, "hardc", Pattern.hardcorr, "pt", maxPt.pt);//, "fn", maxPt.action+""
  }
}
var allPatterns = [];
function clear() {
  allPatterns = [];
}
function adds(raw, action, scorer, presetRot) { // must get a 3x3 arr
  var pt = raw;
  var hp = raw.hp;
  var vp = raw.vp;
  for (let rot = 0; rot < 4; rot++) {
    let view = allVRots[rot];
    allPatterns.push({pt, action, scorer, rot, hp, vp, view, raw});
    if (rot!=4) pt = rotateCW3(pt);
  }
}
function refPt(...args) {
  clear();
  if (Array.isArray(args[0])) {
    if (args[0].length != 3) args[0] = args[0].slice(0,3);
    if (args[0][0].length != 3) args[0] = args[0].map(c=>c.slice(0,3));
    adds(...args);
  }
  else Pattern.add(...args);
  Pattern.choose();
}

/*
is the 2nd param a subset of the 1st param.
guess can be a number (color), or an object ({color:..,ant:..,..})
guess.ant can be "worker", "queen", "enemy", "enemyworker", "enemyqueen" with obvious meanings. Note that "friend" ≠ me
guess.ant.type can be an array, ORing

true - correct!
false - not correct
0 - notqueen doesn't match (aka very bad)

negativesEqual makes this always return true for negative colors, otherwise it treats negatives as regular colors
*/
function basedOn(real, guess, negativesEqual) {
  if (Array.isArray(real)) return real.some(c=>basedOn(c, guess, negativesEqual));
  if (Number.isInteger(guess)) guess = {color:guess};
  if (guess.notqueen && real.ant && real.ant.friend && real.ant.type==5) return 0;
  if (guess.not) {
    var bo = basedOn(real, guess.not, negativesEqual);
    if (bo) return 0;
  } 
  if (guess.color && Math.abs(guess.color) != real.color && !(negativesEqual && guess.color<0)) return false; // 0 handles itself
  if (guess.obstacle !== undefined) {
    if (guess.obstacle && !real.ant && !(food && real.food && !isQueen)) return false;
    if (!guess.obstacle && (real.ant || (food && real.food && !isQueen))) return false;
  }
  if (guess.badobstacle !== undefined) {
    if (guess.badobstacle && !(real.ant && !real.ant.friend) && !(food && real.food && !isQueen)) return false;
    if (!guess.badobstacle && ((real.ant && !real.ant.friend) || (food && real.food && !isQueen))) return false;
  }
  if (guess.ant) {
    if (!real.ant) return false;
    if (guess.ant == "worker"      &&!( real.ant.friend && real.ant.type!=5)) return false;
    if (guess.ant == "queen"       &&!( real.ant.friend && real.ant.type==5)) return false;
    if (guess.ant == "enemyqueen"  &&!(!real.ant.friend && real.ant.type==5)) return false;
    if (guess.ant == "enemyworker" &&!(!real.ant.friend && real.ant.type!=5)) return false;
    if (guess.ant == "friend" && (!real.ant.friend || real.ant.me)) return false;
    if (guess.ant == "enemy"  &&  real.ant.friend) return false;
    if (Number.isInteger(guess.ant) && real.ant.type != guess.ant) return false;
    if (guess.ant.friend !== undefined && guess.ant.friend !== real.ant.friend) return false;
    if (guess.ant.type !== undefined && !(Array.isArray(guess.ant.type)? guess.ant.type.some(c=>c == real.ant.type) : guess.ant.type == real.ant.type)) return false;
    if (guess.ant.food !== undefined && guess.ant.food !== real.ant.food) return false;
  }
  if (guess.food !== undefined && guess.food !== real.food) return false;
  // log("matched");
  return true;
}

function result (action, force) {
  if (!force) if (toReturn !== undefined) return 0;
  var color = action.color;
  var type = action.type;
  var cell = action.cell;
  if (type < 1 || type > 4) return false;
  if (!(cell >= 0 && cell <= 8)) return false;
  if (color < 1 || color > 8) return false;
  if (!color && ((view[cell].ant && cell != 4) || (isQueen? (view[cell].food && type) : (food && view[cell].food)))) return false; // can't walk onto ant, can't spawn on food, can't move to food with food
  if (!isQueen && type) return false;
  if (!isQueen && !color && food && view[cell].food) return false;
  if (isQueen && !food && type) return false;
  if (type && cell==C) return false;
  if (color && type) return false;

  toReturn = action;
  return true;
}

const WH = 1; // white   
const C1 = 6; // green   HW
const C2 = 5; // red     
const C3 = 8; // black   
const C4 = 2; // yellow  HW
const C5 = 4; // cyan    HW
const C6 = 7; // blue    HW
const C7 = 3; // purple  HW
// C1=GR,C2=BL,C4=YL,C5=DK
const ENEMY = {ant:"enemy"};
const foundEnemy = found(ENEMY);
  //-----------------------------------------------------------------------------------------------------------------------------------------------------\\
 //----------------------------------------------------------------------- MAIN CODE ---------------------------------------------------------------------\\
//---------------------------------------------------------------------------------------------------------------------------------------------------------\\

function log(...args) {
  if (!DEBUG) return;
  toLogRaw.push(args);
  // for (let i of args) {
  //   if (i === undefined) i = "undefined";
  //   var res = "";
  //   if (typeof i === 'string') res = i;
  //   else res = JSON.stringify(i);
  //   toLog+= res + " ";
  // }
  // toLog+= "\n";
}
if (DEBUG) {
  var toLog = "";
  var logMyLogs = false;
  var toLogRaw = [];
  log(type, view.map(c=>c.ant? "A"+c.ant.type : c.color));
}

const Ut = 1;
const Dt = 2;
const Ht = 4;
const Uo = {ant:{type:Ut,friend:true}};
const Do = {ant:{type:Dt,friend:true}};
const Ho = {ant:{type:Ht,friend:true}};
const Mo = {ant:{type:[Ut,Dt],friend:true}};
const Fo = {food:1};
const Qo = {ant:{type:5,friend:true}};
const EQo = {ant:{type:5,friend:false}};
const FRIEND = {ant:"friend"};
const OBSTACLE = {obstacle:true};
const FREE = {obstacle:false};
const BADOBSTACLE = {badobstacle:true};
const STARTINGFOOD = 6;
const LESSENFOOD = 160;
const ENDINGFOOD = 160;
const isMiner = type==Ut || type==Dt;
var friendCount = count(FRIEND);
if (isMiner) {
  var Mu = type==Ut? u : d;
  var Md = type==Ut? d : u;
  var Mur = Mu+1;
  var Mul = Mu-1;
  var Mdr = Md+1;
  var Mdl = Md-1;
}

const foodExt = [C3, C7, WH];
const rawRail = [
  [WH,-1,C7,-1], // 43 03 13 23
  [C6,WH,WH,-1], // 42 02 12 22

  [C4,C1,C2,C2],
  [C2,C4,C5,C5],
  [C3,C5,C1,C3],


//[C3,C1,C4,C4],
//[C4,C4,C5,C2],
//[C5,C2,C1,C3],

//[C3,C1,C2,C5],
//[C3,C3,C1,C5],
//[C2,C4,C5,C2],


//[C2,C1,C3,C5], // 41 01 11 21
//[C1,C5,C5,C4], // 40 00 10 20
//[C5,C4,C2,C3], // 41 01 11 21
  [C6,WH,WH,-1], // 42 02 12 22
  [WH,-1,C7,-1]  // 43 03 13 23
]
.map((ln,row)=>(row<2||row>4)? ln.map(c=>({not:{ant:{friend:true, type:[Ht, 5]}},color:c})) : ln); // queen can't be in the top & bottom 2 rows

function section(ln, action, scorer) {
  if (ln > 0) section(-ln, action, scorer);
  var sct = rawRail.slice(ln+2, ln+5);
  var parts;
  if (Math.abs(ln) != 2) {
    parts = [];
    for (let i = 0; i < 4; i++) {
      var cpt = sct.map(([a,b,c,d])=>[a,b,c]);
      cpt.hp = i;
      cpt.vp = ln;
      parts.push(cpt);
      if (i!=4) sct = sct.map(([a,b,c,d])=>[b,c,d,a]);
    }
  } else {
    var o = sct.map(c=>c.slice(0,3));
    o.vp = ln;
    o.hp = 0;
    parts = [o];
  }
  parts.map(c=>adds(c, action, scorer));
}

function sabotage(where) {
  if (on(where, 1)) repair([where], true);
  else color(where, 1);
}


section(0, ()=>{
  if (isMiner) {
    if (on([r,ur,dr], Fo)  ||  on([u,d], Fo) && on([l,ul,dl], Mo)) { // FAKE RAIL
      color(on([dr,d],Fo)? [dr,u,ul] : [dr,d,dl], C7);
    }
    else if ([l,ul,dl].every(c=>on(c,{ant:{}})) && !random4()) move([r,u,d,ur,dr]); // peer pressure
    /* AV */ else if (found(EQo)) sabotage(find(EQo));
    else if (repair()) {
      if (on(r,Mo) && (random4() > 1 || random4() && friendCount > 3)) move([l,Mul,Mu,Mdl,Md]);
      if (on([Mu,Mur], ENEMY)) move([R, D, DR]); // move somewhere away from enemy
      else if (on(r, Qo) && on([l,ul,dl], {ant:{type:[Ut,Dt],food:1,friend:true}})) move([Mu, Mul, l, Mdl]); // make place for miners with food; Possibly stuck
      else if (on(r, Qo)) move([Mu, Mul]); // don't do stupid things around queen
      else if (on(l, Qo) && !foundEnemy) move([Mul, Mdl, Mu, Md]); //.. I've done stupid things around queen
      else if (on([Mu,Mur], OBSTACLE)) { // up is blocked :/
        if (random4()) move(on(Mu, FRIEND)? [Mur, r] : r);
        else move([r, Mul, Md, Mul, l, Mdl]);
      }
      else move([Mu, r, Mur, Md, Mdr]); // move along
    }
  } else if (isQueen) {
    var HM = Pattern.view.map(c=>+basedOn(c, Ho));
    var helperRows = [HM.slice(0,3),HM.slice(6,9)].map(c=>c.lastIndexOf(1)).map((c,i) => (c==0 && on(i==0? u : d, OBSTACLE)) ? -1 : c);
    var minH = Math.min(helperRows[0],helperRows[1]);
    var maxH = Math.max(helperRows[0],helperRows[1]);
    if (on(r, FRIEND) && [ur,dr].every(c=>on(c, ENEMY))) move([l,ul,dl]);
    if (found(EQo)) { // vampire?
      move(random4()%2? [ur,dr] : [dr,ur]);
      var eQueenRel = (findRel(EQo)-rp)%3;
      if (eQueenRel == 0) move(r,ur,dr);
      spawn(Mu, [u,d,r,ur,dr]);
    }
    if (foundEnemy) // spawn helpers against enemies
      if (food && minH == -1 && count(Ho) < 2) {
        if (helperRows[0] == -1) spawn([u,ur,ul],Ht);
        else                     spawn([d,dr,dl],Ht);
      }
    if ([r,ur,dr].every(c=>on(c, ENEMY))) move([ul,dl,l,u,d]); // OH GOD NO WHY
    if ((minH == -1 || maxH == 2) && on(r, [ENEMY,Ho]) && Pattern.incorr < 2 && count({ant:{}})-1 != count(Ho))
      move(on(ur, ENEMY)? [d,u,dr,ur,ul,dl,l] : [u,d,ur,dr,ul,dl,l]); // initialize transporting around enemy
    if ((!random4() && on(l, OBSTACLE) && on([ul, dl], OBSTACLE)) && Pattern.corr >= 7) move(r); // move forward sometimes if left is 2/3s full
    else if ([r,ur,dr].every(c=>c.ant && !c.ant.friend)) move([l,ul,dl]);
    else if (food && minH > 0 && (
        count(Mo) == 0 && selectedHp != 1 && Pattern.corr != 9 && food < LESSENFOOD
      ||
        //Pattern.corr === 9 && [u,d].every(c=>on(c,Ho)) && selectedHp === 1 && food >= LESSENFOOD && food < ENDINGFOOD && random4() < 2
        Pattern.corr === 9 && selectedHp == 0 && count(Mo) === 0 && food >= LESSENFOOD && food < ENDINGFOOD && random4() < 2
      )) { // spawn miners
      if (random4()%2) spawn([u,ul], Ut);
      else             spawn([d,dl], Dt);
    } else if (repair()) {
      if (food && minH == -1 && count(Ho) < 2) { // spawn helpers
        if (helperRows[0] == -1) spawn([u,ur,ul],Ht);
        else                     spawn([d,dr,dl],Ht);
      }
      else if (selectedHp != 1  ||  selectedHp==1 && /*(*/(maxH==2 || minH == -1 && helperRows.includes(2)) && (!random4() || food < LESSENFOOD || found(Mo))  ||  foundEnemy) move(r); // move forwards
      else if (on(ul, Do) && on(dl,Uo) && on(l, {ant:{}})) move(r); // miners are in wrong places
    }
  } else { // helper
    var repaired = repair();
    var queenRel = (findRel(Qo)-rp)%3;
    var dir = queenRel==0? 0 : 1;
    if (repair()) {
      if (on(r, EQo) && [u,d,ur,dr].map(c=>on(c, ENEMY)? 1 : 0).reduce(ADD) >= 3) move(c); // protect the queen from the evils ahead
      else if (on(r, Qo)) move([u,d,ur,dr,ul,dl,l]);
      else move((on([d,dr,dl], Ho)? [u+dir,d+dir] : [d+dir,u+dir]).concat([u+(1-dir), d+(1-dir)]), !random4());
    }
  }
})

section(1, ()=>{
  const A = selectedVp > 0? d : u; // away
  const I = selectedVp > 0? u : d; // in
  const AR = A+1;
  const AL = A-1;
  const IR = I+1;
  const IL = I-1;

  if (isMiner) {
    var queenRel = (findRel(Qo)-rp)%3;
    if (on([r,IR], Fo)) color([r,IR, I], C7); // FAKE RAIL
    else if ([l,IL].every(c=>on(c,{ant:{}})) && !random4()) move([r,IR]); // peer pressure
    /* AV */ else if (found(EQo)) sabotage(find(EQo));
    else if ((found(EQo) && random4() && Pattern.dist <= 1 && on(find(EQo), 1)) || repair()) {
      if (on(I, Qo)) move(l); // what am I doing here?
      else if (A == Mu) { // my dir!
        if (!food && selectedHp == 0 && (on(r,Ho) && on(IR, Qo)  ||  count(Mo) >= 6)) move(A); // move out!
        else if (on(IR,Qo) && on([l,IL], {ant:{type:[Mu,Md],friend:true,food:1}})) move(C); // waiting in line :D
        else if (on(IR,Qo)) move(C); // waiting in line :D
        else if (random4()) move([r, I, IR]);
        else move([r, I, IR, l, IL]);
      } else { // not my dir
        // TODO fix \\ if (selectedHp == 0 && count(Mo) >= 6 && food) move(A); // fake rail escape
        if (random4()) {
          move([I, IR, IL, l]);
        } else {
          move([r, I, IR, IL, l]);
        }
      }
    }
  } else if (isQueen) {
    if (found(EQo)) { // vampire?
      var eQueenRel = (findRel(EQo)-rp)%3;
      if (eQueenRel==0) move(r, IR);
      spawn(Mu, [r,IR]);
      spawn(Md, I);
    }
    /* AV */ if (food > 70 && (
      [IR,IL,I,l,r].every(c=>on(c,ENEMY)) // completely encased
      || on(IR, EQo) && [r,I].every(c=>on(c, ENEMY)) // getting leeched
      || on(I, EQo) && [r,l,IL,IR].map(c=>get(c)).map(c=>c.ant? (c.ant.friend? 1 : -1) : 0).reduce(ADD) < 0 // leeched
    )) move([A,AR,AL]); // BAD NEWS COMPLETELY DEAD
    if (!random4() || found(EQo) || repair())
      move(random4()? [IR,r,I,l] : [IR,r,I]);
  } else { // helper
    var queenRel = (findRel(Qo)-rp)%3;
    /* AV */ if (on(r,Qo) && on([I,IR], EQo)) move([IR,I,l,IL]);
    if (on(l, Qo)) { if (!random4() || repair()) move(r) } // queen's transporting
    if (on(I, Qo) && on(IR, {ant:"enemyworker"})) { if (!random4() || repair()) move(r) } // queen needs to transport
    // what was this? if ([l,IL,I].every(c=>on(c,OBSTACLE)) && (count(ENEMY) > 1 || find(EQo)) && !random4()) move([r,ur]);
    if ((selectedVp < 0? /...[#-W]{6}/ : /[#-W]{6}.../).test(Pattern.corrstr) && queenRel == 2 && count(Ho) == 1) move(r); // move forward without repairing
    if (!random4() && queenRel == 1 && selectedHp == 1 && on(AL, {ant:{}})) move(r); // something is out; don't repair
    else if (repair([r,l,A,AR,AL])) {
      if (on(r, ENEMY) && on(I, Qo) && [l,IL].every(c=>on(c,FRIEND))) move(IR); // protect from vampire
      if (on(r, ENEMY) && on(IL, Qo) && [l,IR].every(c=>on(c,FRIEND))) move(I);
      if (on(r, ENEMY) && !get(r).ant.food && on(I, Qo)) move(IR);
      if (queenRel == 1 && selectedHp == 1 && on(AL, {ant:{}})) move(r); // something is out
      else if (on([l,r], Ho)) { // move to the other side
        if (found(Qo)) move([I]); // TODO integrate ,IL,IR
        else move([l,r]);
      }
      else if (queenRel == 2) move(r); // move forward
    }
  }
}, (pscore, pt, corrects, falseN, match) => {
  if (match(".?(...)?@@.@@.*") && !foundEnemy) {
    if (!match(pt.vp>0? ".?@@.@@.*" : ".?...@@.@@.*")) pscore/=2;
  }
  return pscore;
})

if (isMiner) {
  section(2, () => {
    const A = selectedVp > 0? d : u; // away
    const I = selectedVp > 0? u : d; // in
    if (on(A,OBSTACLE)) move(I);
    else if (repair()) move(food? I: Mu);
  }, (pscore, pt, corrects, falseN, match) => match(pt.vp>0? "@@@.@...." : "....@.@@@")? match("@@@@@@@@@")? 100 : ((pt.vp>0) == (type==Dt)? 13 : 10) : 0);
  if (type==Dt) foodExt.reverse();
  if (!found(Ho) && !found(Qo)) {
    var lns = [rawRail[0], rawRail[1]].map(c=>c.slice(0,3));
    [[lns[0],lns[1],lns[0]], [lns[1],lns[0],lns[1]]].map(c=>{
      adds(c, () => {
        var onL;
        if (!food && ((onL = on(l, Fo)) || on(r, Fo))) {
          var foodpt = Pattern.raw.map((ln, i) => [ln[0], foodExt[i], ln[2]]);
          refPt(foodpt,undefined,undefined,selectedRot);
          if (repair()) move(onL? l : r);
        }
        else if (repair([l,r,ul,ur,dl,dr], on([Mul, Mur, Mdl, Mdr, l, r], {ant:{friend:true,type:[Ut,Dt],food:1}}) ||  on([Mu, Md], Mo))) {
          move(food? [Md, Mu] : [Mu, Md]);
        }
      }, (pscore, pt, corrects, falseN, match) => {
        var score = 0;
        var dMatch = match("...@.@@@@");
        var uMatch = match("@.@@.@...");
             if ((type==Ut ^ food) && dMatch) score = 15;
        else if ((type==Dt ^ food) && uMatch) score = 15;
        else if (uMatch || dMatch) score = 6;
        if ([0,2,3,5,6,8].some(c=>basedOn(pt.view[c], FRIEND) && !pt.view[c].ant.food)) score = 0;
        return score;
      });
      if (food) {
        var extp = c.map((ln, i) => [ln[0], foodExt[i], ln[2]]);
        [extp.map(([a,b,c])=>[0,a,b]), extp.map(([a,b,c])=>[b,c,0])].forEach((pt,i) => adds(pt, () => {
          move(i? l : r);
        }, (pscore, pt, corrects, falseN, match) => match("@@@@@@@@@")? 100 : 0));
      }
    });
  }
}

Pattern.choose();
var confident = ((Pattern.confidence >= 1 && (Pattern.score > 4 || Pattern.corr >= 4)) || (Pattern.score >= 9 && Pattern.confidence > 0.05)); // && (selectedHp !=  || !found(Qo));
var failAction = () => {
  if (foundEnemy) {
    log(view);
    log("dead around enemy :/");
    logMyLogs = true;
  }
  if (isQueen) {
    if (found(EQo)) {
      move([8-(find(EQo)-rp) + rp]);
      move(random4()%2? U : UR);
    }
    if (foundEnemy) move(random4()%2? U : UR);
  } else {
    // if (!found(Qo) && found(Fo)) move(find(Fo));
    var enemyPlace = find(ENEMY);
    if (enemyPlace !== -1) color(enemyPlace, get(enemyPlace).color==1? C3 : WH);
  }
}
if (!confident) Pattern.action = failAction;

if (isMiner) {
  if ((Pattern.hardcorr >= 4 || Pattern.score > 5) && confident) Pattern.action();
  else {
    failAction();
  }
} else if (isQueen) {
  if ((Pattern.hardcorr >= 6 || food > STARTINGFOOD+2 || friendCount>1 || found(Mo) || Pattern.score > 6 || (false)) && confident) Pattern.action();
  else if (food >= STARTINGFOOD && friendCount == 1) {
    clear();
    Pattern.add([[1,{ant:Ho.ant,color:1},1],
                 [1,1,1],
                 [1,1,1]], ()=>spawn([ur,ul],Ht));
    Pattern.add([[1,1,{ant:Ho.ant,color:1}],
                 [1,1,1],
                 [1,1,1]], ()=>spawn([ur,u],Ht));
    Pattern.choose();
    if (repair()) Pattern.action();
  } else if (food == 0 && friendCount == 0) { // diagonal search
    if (found(Fo)) {
      move(find(Fo));
    } else {
      clear();
      Pattern.add([[WH,WH,WH],
                   [WH,C1,WH],
                   [C1,WH,WH]], ()=>move(ur));
      Pattern.add([[WH,WH,WH],
                   [WH,WH,WH],
                   [C1,WH,WH]], ()=>color(C, C1));
      Pattern.add([[WH,WH,WH],[WH,WH,WH],[WH,WH,WH]], ()=>color(DL, C1));
      Pattern.choose();
      if (Pattern.corr == 9) Pattern.action();
      else move(random4()? [DL,UL,DR,UL] : [D,L,U,R]);
    }
  } else if (food == 1 && friendCount == 0) spawn([U,L,D,R,UL,DL,UR,DR], Ht);
  else if (friendCount == 1) lightSpeed();
  else if (friendCount > 0) {
    var pt = new Pattern(rawRail).select(0,2,4,3).rotate(L).rotate(L).pt;
    pt[1][2] = {color:pt[1][2], ant:{type:Ht, friend:true}};
    refPt(pt);
    repair([c,u,d,ur,dr]);
  } // TODO wtf to do after this
  else Pattern.action(); // eh fuck it
} else if (type == Ht) {
  if (confident && (Pattern.score >= 4 || Pattern.hardcorr >= 5 || friendCount>1)) Pattern.action();
  else if (found(Qo)) lightSpeed();
  else if (Pattern.hardcorr >= 3 && confident) repair();
}

function lightSpeed() {
  var other = find(isQueen? Ho : Qo);
  var orth = other%2;
  if (isQueen || (view[other].ant.food < STARTINGFOOD && count(Ho) == 1)) { // LS
    if (orth && found(Fo)) { // grab easy food
      var fp = find(Fo);
      if (sees(other, fp)) move(fp);
      else {
        refPt([[0,FRIEND,0],
                     [0,0,0],
                     [0,0,0]]);
        move(l);
      }
    }
    clear();
    // Pattern.when(U,find(FRIEND), ()=>isQueen? move(ul) : move(ur)); when I'm not lazy imma make this a replacement of the below
    Pattern.add([[0,FRIEND,0],
                 [0,0,0],
                 [0,0,0]], ()=>isQueen? move(ul) : move(ur));
    Pattern.add([[0,0,FRIEND],
                 [0,0,0],
                 [0,0,0]], ()=>move(u));
    Pattern.choose();
    Pattern.action();
  }
}

if (DEBUG) log("END", type, view.map(c=>c.ant? "A"+c.ant.type : c.color));
if (DEBUG && logMyLogs) {
  //for (let i = 0; i < toLog.length; i+=800)
  //  console.log(toLog.substring(i,i+800));
  for (let i of toLogRaw) console.log(...i);
}
if (toReturn) return toReturn;
else return {cell:4};

Sebelumnya ini adalah Miners on a Rail (lihat riwayat revisi), tetapi telah diubah menjadi Sliding Miners karena kinerjanya lebih baik dan MoaR hanya akan menghalangi keberhasilannya jika ini merupakan entri lain.


Pertama di papan peringkat lagi kali ini ...
trichoplax

14

Pesawat peluncur

Glider beraksiGlider belok kiriGlider berbelok ke kanan

//console.log(JSON.stringify(view))
var TRAIL = 6;
var SPAWN = 3;
var IDLE = 4;
var FOOD_THRESHOLD = 150;
var SPAWN_MIN = 3;
var HIGHWAY_COLORS = [7,6,4,2,3];
var HIGHWAY_THRESHOLD = 70;
var ret = {cell:4};
if(isOnHighway()) {
    var cont = true;
    //== Make best guess to if in a glider formation ==//
    if(view[4].ant.type == 5) {
        if((findWorker(1) >= 0 && findWorker(4) >= 0) || view[4].ant.food < HIGHWAY_THRESHOLD) {
            cont = false;
        }
    }
    else if(view[4].ant.type == 4) {
        if(findWorker(1) >= 0 && findWorker(5) >= 0) {
            cont = false;
        }
    }
    else if(view[4].ant.type == 3) {
        if(findWorker(2) >= 0 && findWorker(5) >= 0) {
            cont = false;
        }
    }
    else if(view[4].ant.type == 2) {
        if(findWorker(5) < 0) {
            var pos3 = findWorker(3);
            if(pos3 >= 0 && view[pos3].ant.food == 0) {
                cont = false;
            }
        }
        else if(findWorker(3) >= 0 || (findWorker(1) >= 0 && view[findWorker(5)].color == SPAWN))
            cont = false;
    }
    else if(view[4].ant.type == 1) {
        if(findWorker(5) < 0) {
            var pos4 = findWorker(4);
            if(pos4 >= 0 && view[pos4].ant.food == 0) {
                cont = false;
            }
        }
        else if(!isHighwayCenter())
            cont = false;
    }
    if(findWorker(5) >= 0) {
        for(var i=0;i<9;i++) {
            if(view[i].ant != null && !view[i].ant.friend && view[i].ant.type == 5) {
                if(view[i].ant.food > 10 || view[i].ant.food == 0)
                    cont = true;
                else
                    cont = false;
            }
        }
    }
    //== End guesswork ==//
    if(cont) {
        ret = highwayRobbery();
        if(view[4].ant.type == 1) {
            //try to repair
            var curIndex = HIGHWAY_COLORS.indexOf(view[4].color);
            var prvCol = HIGHWAY_COLORS[(curIndex+1)%HIGHWAY_COLORS.length];
            var nxtCol1 = HIGHWAY_COLORS[(curIndex+HIGHWAY_COLORS.length-1)%HIGHWAY_COLORS.length];
            var nxtCol2 = HIGHWAY_COLORS[(curIndex+HIGHWAY_COLORS.length-2)%HIGHWAY_COLORS.length];
            var nxtCol3 = HIGHWAY_COLORS[(curIndex+HIGHWAY_COLORS.length-3)%HIGHWAY_COLORS.length];
            var prevAt = -1;
            for(var i=0;i<9;i++) {
                if(i%2 == 1 && view[i].color == prvCol && view[deRotate(i,1)].color == nxtCol1 && view[deRotate(i,-1)].color == nxtCol1) prevAt = i;
            }
            if(prevAt >= 0) {
                // yep, brute force it. Because I'm lazy.
                var goNxt = 8-prevAt;
                if(view[deRotate(goNxt,1)].color == nxtCol3 && view[deRotate(goNxt,-1)].color == prvCol) ret = {cell:goNxt};
                else if(view[deRotate(goNxt,1)].color == prvCol && view[deRotate(goNxt,-1)].color == nxtCol3) ret = {cell:goNxt};
                else if(view[goNxt].color != nxtCol1) ret = {cell:goNxt,color:nxtCol1};
                else if(view[deRotate(goNxt,2)].color != nxtCol2) ret = {cell:deRotate(goNxt,2),color:nxtCol2};
                else if(view[deRotate(goNxt,-2)].color != nxtCol2) ret = {cell:deRotate(goNxt,-2),color:nxtCol2};
                else if(view[deRotate(goNxt,1)].color != nxtCol3) ret = {cell:deRotate(goNxt,1),color:nxtCol3};
                else if(view[deRotate(goNxt,-1)].color != nxtCol3) ret = {cell:deRotate(goNxt,-1),color:nxtCol3};
                else ret = {cell:goNxt};
                ret = sanityCheck(ret);
                return ret;
            }
        }
        if(view[4].ant.type == 5 && isHighwayCenter()) {
            if(ret.cell >= 0) {
                ret = {cell:8-ret.cell};
                if(view[4].color == SPAWN && (view[4].ant.food > 90 || view[4].ant.food % 7 == 0) && getHighestWorker() == 0 && (view[4].ant.food < 140 || view[4].ant.food % 9 == 0) && view[0].color == 2 && view[4].ant.food > 50 && view[4].ant.food < 200) {
            //fine
                    if(view[4].ant.food % 10 < 5)
                        ret = {cell:deRotate(ret.cell,3),type:3};
                }
                if(view[ret.cell].ant != null && !view[ret.cell].ant.friend && view[ret.cell].ant.food == 0 && view[4].ant.food > 0) {
                    if(view[deRotate(ret.cell,1)].ant == null)
                        ret = {cell:deRotate(ret.cell,1),type:3};
                    if(view[deRotate(ret.cell,-1)].ant == null)
                        ret = {cell:deRotate(ret.cell,1),type:3};
                }
            }
            for(var i=0;i<9;i++) {
                if(view[i].ant != null && !view[i].ant.friend && view[i].ant.type == 5) {
                    ret = {cell:8-i};
                }
            }
        }
        if(ret.cell >= 0) {
            for(var i=0;i<9;i++) {
                if(view[i].ant != null && !view[i].ant.friend && view[i].ant.type == 5) {
                    var rr = basicHighwayMove();
                    if(rr.cell >= 0 && view[4].ant.type != 5)
                        ret = {cell:deRotate(rr.cell,-2)};
                }
            }
            if(view[ret.cell].ant != null) {
                var n = HIGHWAY_COLORS.indexOf(view[4].color) + 1;
                var nextMove = HIGHWAY_COLORS[n % HIGHWAY_COLORS.length];
                for(var i=0;i<9;i++) {
                    if(view[i].color == nextMove) {
                        if(view[i].ant == null) {
                            ret = {cell:i};
                            break;
                        }
                    }
                }
                if(view[4].ant.type == 5) ret = {cell:8-ret.cell};
            }
        }
        if(view[4].ant.type == 5) {
            var foodedEnemy = false;
            for(var i=0;i<9;i++) {
                if(getNumWorkers(3) >= 2) break;
                if(i != 4 && view[i].ant != null && view[i].ant.type == 5 && view[i].ant.food > 25) {
                    if(view[deRotate(i,1)].ant == null) {
                        ret = {cell:deRotate(i,1),type:3};
                    }
                    else if(view[deRotate(i,-1)].ant == null) {
                        ret = {cell:deRotate(i,-1),type:3};
                    }
                    else if(i%2 == 1 && view[deRotate(i,2)].ant == null) {
                        ret = {cell:deRotate(i,2),type:3};
                    }
                    else if(i%2 == 1 && view[deRotate(i,-2)].ant == null) {
                        ret = {cell:deRotate(i,-2),type:3};
                    }
                }
                if(view[i].ant != null && !view[i].ant.friend && view[i].ant.type == 3 && view[8-i].ant == null) {
                    if(i == ret.cell) {
                        ret = {cell:deRotate(i,1)}
                    }
                    else {
                        return {cell:8-i};
                    }
                }
                if(view[i].ant != null && !view[i].ant.friend && view[i].ant.food == 0) {
                    foodedEnemy = true;
                }
            }
        }
        var numAnts = 0;
        for(var i=0;i<9;i++) {
            if(view[i].ant != null)
                numAnts++;
        }
        if(numAnts > 2 && sanityCheck(ret).cell == 4) {
            ret = {cell:findOpenSpace(0,1)};
        }
        if(view[4].ant.type == 3) {
            if(getNumWorkers(5) > 0) {
                for(var i=0;i<9;i++) {
                    if(view[i].ant != null && !view[i].ant.friend && view[i].ant.type == 5) {
                        ret = {cell:4};
                    }
                }
            }
        }
        if(view[4].ant.type == 4 && getNumWorkers(1) && isHighwayCenter()) {
            var workerPos = findWorker(1);
            for(var i=0;i<9;i++) {
                if(!areAdjacent(i,workerPos)) ret = {cell:i};
            }
        }
        if(ret.cell == -1) {
            if(isHighwayCenter()) {
                for(var i=0;i<9;i++) {
                    var p1 = deRotate(i,3);
                    var p2 = deRotate(i,-3);
                    if(view[i].color == view[p1].color && view[i].color == view[p2].color) {
                        ret = {cell:8-i};
                    }
                }
                if(view[4].ant.type == 1 || view[4].ant.type == 5) {
                    ret = {cell:8-ret.cell};
                }
            }
        }
        if(ret.cell >= 0)
            return sanityCheck(ret);
    }
}

switch(view[4].ant.type) {
    case 5:
        ret = doQueen();
        break;
    case 1:
    case 2:
        ret = doSweep();
        break;
    case 3:
    case 4:
        ret = doGuide();
        break;
    default:
        break;
}
//basic sanity check
ret = sanityCheck(ret);
return ret;

function sanityCheck(ret) {
    if(!ret || ret.cell < 0 || ret.cell > 8) {
        return {cell:4};
    }
    if(ret.color) {
        return ret;
    }
    if((ret.cell != 4 && view[ret.cell].ant != null) || (view[ret.cell].food > 0 && (view[4].ant.food > 0 && view[4].ant.type < 5))) {
        return {cell:4};
    }
    if(ret.type && (view[ret.cell].ant != null || view[ret.cell].food > 0 || view[4].ant.food == 0 || view[4].ant.type < 5)) {
        return {cell:4};
    }
    return ret;
}

function doQueen() {
    if((view[4].ant.food == SPAWN_MIN || (view[4].ant.food >= SPAWN_MIN && view[4].ant.food < FOOD_THRESHOLD && (view[4].ant.food % 3 == 1 || isOnHighway()))) && getHighestWorker() <= 1 ) {
        //prep for first ant
        var s0 = view[0].ant;
        var s1 = view[1].ant;
        var s2 = view[2].ant;
        var s3 = view[3].ant;
        var s5 = view[5].ant;
        var s6 = view[6].ant;
        var s7 = view[7].ant;
        var s8 = view[8].ant;
        var nullCount = 0 + (s0 == null?1:0) + (s1 == null?1:0) + (s2 == null?1:0) + (s3 == null?1:0) + (s5 == null?1:0) + (s6 == null?1:0) + (s7 == null?1:0) + (s8 == null?1:0);
        var nullCount2 = 0 + (s0 == null || s0.friend?1:0) + (s1 == null || s1.friend?1:0) + (s2 == null || s2.friend?1:0) + (s3 == null || s3.friend?1:0) + (s5 == null || s5.friend?1:0) + (s6 == null || s6.friend?1:0) + (s7 == null || s7.friend?1:0) + (s8 == null || s8.friend?1:0);
        if(nullCount >= 7 && nullCount2 >= 8 && view[1].food == 0 && view[3].food == 0 && view[5].food == 0 && view[7].food == 0) {
            var high = getHighestWorker();
            if (high <= 1 && view[4].color != SPAWN && !isOnHighway()) {
                // 50% chance of delaying the respawn by 1 additional move away from where we exploded
                // reduces the chance of a second, immediate explosion
                var pos1 = findWorker(1);
                if(findFirstTrail() < 2 && view[4].ant.food > SPAWN_MIN+1 && pos1 < 0) return foreverAlone();
                if(pos1 >= 0) {
                    var space = deRotate(pos1,2);
                    if(view[space].ant != null) return {cell:findOpenSpace(0,1)};
                }
                return {cell:4,color:SPAWN};
            }
            //spawn first ant
            else if(view[4].color == SPAWN) {
                var pos1 = findWorker(1);
                if(pos1 < 0)  {
                    pos1 = findFirstTrail();
                    if(pos1 % 2 == 0) pos1 = deRotate(pos1,1);
                    else pos1 = deRotate(pos1,4);
                }
                var space = findOpenSpace(pos1,2);
                var high = getHighestWorker();
                if(space < 0) return {cell:4,color:TRAIL}
                if(high == 0) { //no workers
                    return {cell:space,type:1};
                }
                else if(high < 4) { //1 worker of type:high
                    return {cell:space,type:high+1};
                }
                else { //1 worker of type 4
                    //we have all workers, skip!
                }
            }
        }
        else {
            return foreverAlone();
        }
    }
    else if(view[4].ant.food == 1 && getHighestWorker() == 0 ) {
        var space = findOpenSpace(1,2);
        return {cell:space,type:1};
    }
    else if(view[4].ant.food >= 1 && getHighestWorker() < 4 && findWorker(1) >= 0) {
        //spawn remaining ants
        if(view[4].color == SPAWN && !isHighwayCenter()) {
            var pos1 = findWorker(getHighestWorker());
            var space = deRotate(pos1,2);
            var high = getHighestWorker();
            if(space < 0 || view[space].ant != null) return {cell:findOpenSpace(0,1)};
            if(high == 0) { //no workers
                return {cell:space,type:1};
            }
            else if(high < 4) { //1 worker of type:high
                return {cell:space,type:high+1};
            }
            else { //1 worker of type 4
                //we have all workers, skip!
            }
        }
    }
    if(view[4].color == SPAWN && getNumWorkers(3) == 1 && getNumWorkers(4) == 1) {
        var one = getNumWorkers(1);
        var two = getNumWorkers(2);
        if((one ^ two) == 1 && (findWorker(1) % 2 == 0 || findWorker(2) % 2 == 0))
            return {cell:4,color:1};
    }
    if(getNumWorkers(1) == 0 && getNumWorkers(2) == 0) {
        if(getNumWorkers(4) == 1 && getNumWorkers(3) == 0) {
            var pos4 = findWorker(4);
            if(view[deRotate(pos4,1)].ant == null && view[deRotate(pos4,2)].ant == null && findWorker(4) % 2 == 1) {
                //finish rotate with only one glider arm
                return {cell:4};
            }
        }
        return foreverAlone();
    }
    else if(getNumWorkers(1) >= 1 && getNumWorkers(2) >= 1 && getNumWorkers(3) >= 1 && getNumWorkers(4) >= 1) {
        if(view[4].color != 2 && findWorker(1)%2 == 1 && findWorker(2)%2 == 1) {
            return {cell:4,color:TRAIL};
        }
        //move diagonally
        var pos = findWorker(4);
        pos = deRotate(pos,1);
        var checkpos = view[deRotate(pos,4)];
        if(checkpos.ant != null && checkpos.ant.friend) {
            if(checkpos.ant.type == 2)
                return {cell:4};
            if(checkpos.ant.type == 1)
                return {cell:4};
        }
        if(view[pos].ant) return {cell:4,color:1};
        return {cell:pos};
    }
    else {
        var pos = findWorker(4);
        if(pos < 0) {
            //if gliding along with only a buddy
            pos = findWorker(1);
            if(pos >= 0 && view[deRotate(pos,2)].food > 0 && view[deRotate(pos,1)].food == 0) {
                return {cell:4};
            }
        }
        if(pos < 0) {
            var s1 = view[1].ant;
            var s3 = view[3].ant;
            var s5 = view[5].ant;
            var s7 = view[7].ant;
            //return {cell:999}
            if(s1 == null) {
                if(s3 == null) {
                    return {cell:0};
                }
                if(s5 == null) {
                    return {cell:2};
                }
            }
            if(s7 == null) {
                if(s3 == null) {
                    return {cell:6};
                }
                if(s5 == null) {
                    return {cell:8};
                }
            }
            return {cell:4};
        }
        pos = deRotate(pos,1);
        var checkpos1 = view[pos];
        for(var i=0;i<9;i++) {
            if(i != 4 && view[i].ant != null && view[i].ant.type == 5 && view[i].ant.food > 2) {
                if(i%2==0) {
                    if(view[deRotate(i,1)].ant == null) return {cell:deRotate(i,1),type:3};
                    if(view[deRotate(i,-1)].ant == null) return {cell:deRotate(i,-1),type:3};
                }
                else {
                    if(view[deRotate(i,1)].ant == null) return {cell:deRotate(i,1),type:3};
                    if(view[deRotate(i,-1)].ant == null) return {cell:deRotate(i,-1),type:3};
                    if(view[deRotate(i,2)].ant == null) return {cell:deRotate(i,2),type:3};
                    if(view[deRotate(i,-2)].ant == null) return {cell:deRotate(i,-2),type:3};
                }
                return {cell:4};
            }
        }
        if(checkpos1.ant != null && view[deRotate(pos,1)].ant != null && !view[deRotate(pos,1)].ant.friend) {
            return foreverAlone();
        }

        var checkpos2 = view[deRotate(pos,4)];
        var checkpos3 = view[deRotate(checkpos,1)];
        if(checkpos1.ant != null && checkpos1.ant.friend && checkpos1.ant.type == 1 && checkpos2.ant != null && checkpos2.ant.friend && checkpos2.ant.type == 2 && checkpos3.ant != null && checkpos3.ant.friend && checkpos3.ant.type == 3) {
            //move out of spawn orientation
            return {cell:4};
        }
        if(view[pos].ant != null) {
            if(checkpos2.ant == null && checkpos1.ant == null) {
                return {cell:8-pos};
            }
            if(!view[pos].ant.friend) {
                return foreverAlone();
            }
            if(view[4].color == TRAIL) return foreverAlone();
            return {cell:4,color:TRAIL};
        }
        if(8 - findWorker(3) == findWorker(4)) {
            //finish rotate to the right
            return {cell:4};
        }
        if((view[deRotate(pos,1)].food > 0 || view[deRotate(pos,2)].food > 0) && view[deRotate(pos,1)].ant == null && view[4].color != TRAIL) {
            if(findWorker(1) < 0 || view[deRotate(findWorker(1),1)].food == 0) {
                return {cell:4};
            }
        }
        return {cell:pos};
    }
    return {cell:100+view[4].ant.type}; //oh god
}

//guides sit next to the queen
function doGuide() {
    var queenPos = findWorker(5);
    var ty = view[4].ant.type==3?2:1;
    var dir = view[4].ant.type==3?1:-1;
    if(queenPos >= 0 && queenPos%2 == 1 && view[queenPos].color == SPAWN) {
        if(view[deRotate(queenPos,dir*2)].ant == null) {
            return {cell:4};
        }
    }
    if(queenPos < 0 || findWorker(ty) < 0) {
        if(findWorker(ty) >= 0 && view[0].color != IDLE) return {cell:0,color:IDLE}
        return firebreak();
    }
    var checkpos = view[deRotate(queenPos,-2*dir)];
    if(view[4].ant.type==4 && checkpos.ant != null && checkpos.ant.friend && checkpos.ant.type == 1) {
        //attempt rotate
        return {cell:deRotate(queenPos,-dir)};
    }
    checkpos = view[deRotate(queenPos,4)];
    if(checkpos.ant != null && checkpos.ant.friend && checkpos.ant.type == ty) {
        //attempt rotate
        if(getNumWorkers(ty) == 1) {
            return {cell:4};
        }
    }
    var pos = deRotate(queenPos,dir);
    if(pos >= 0 && view[4].ant.type==3 && findWorker(4) < 0) {
        //wait for rotate
        if(view[4].color == TRAIL) {
            return {cell:4};
        }
    }
    if(pos >= 0 && findWorker(2) >= 0 && view[deRotate(findWorker(2),1)].ant != null) {
        //rotate
        return {cell:4,color:TRAIL};
    }
    if(pos < 0) pos = 4;
    else if(view[pos].ant != null) return {cell:deRotate(queenPos,4)};
    if(pos == 4 && view[queenPos].color == TRAIL) return {cell:queenPos,color:1};
    return {cell:pos};
}

//sweepers sit next to guides
function doSweep() {
    var queenPos = findWorker(5);
    var followType = view[4].ant.type==1?4:3;
    var pos = findWorker(followType);
    if(pos % 2 == 0 && getNumWorkers(followType) > 1) {
        //if there's more than one worker #4, we want to use the best one
        for(var i=pos+1;i<9;i++) {
            if(i != 4 && view[i].ant != null) {
                if(view[i].ant.friend && view[i].ant.type == followType) {
                    pos = i;
                    break;
                }
            }
        }
    }
    if(queenPos >= 0 && queenPos%2 == 1 && view[queenPos].color == SPAWN) {
        var p = findWorker(view[4].ant.type);
        if(p >= 0 && (deRotate(p,1) == queenPos || deRotate(p,-1) == queenPos)) {
            return {cell:8-queenPos};
        }
        return {cell:4};
    }
    if(queenPos >= 0 && pos < 0) {
        //if Worker #1 is the only ant besides the queen:
        //TODO
    //good
        if(view[queenPos].ant.food <= SPAWN_MIN || !(view[queenPos].ant.food < FOOD_THRESHOLD && view[queenPos].ant.food % 3 == 1 && !isOnHighway())) {
            var go = deRotate(queenPos,-1);
            if((view[deRotate(queenPos,-2)].food > 0 || view[deRotate(queenPos,-3)].food > 0 || (queenPos %2 == 1 && view[deRotate(queenPos,-3)].food > 0)) && view[go].food == 0) {
                go = deRotate(queenPos,2);
                //return {cell:4};
            }
            return {cell:go};
        }
        else if(view[queenPos].ant.food < FOOD_THRESHOLD && view[queenPos].ant.food % 3 == 1) {
            return {cell:4};
        }
    }
    if(queenPos >= 0) {
        var dir = view[4].ant.type==1?1:-1;
        //var checkpos = view[deRotate(pos,-dir)];
        var moveTo = deRotate(pos,dir);
        if(moveTo >= 0 && view[moveTo].ant != null && view[moveTo].ant.friend && view[moveTo].ant.type == 5) {
            moveTo = deRotate(pos,-dir);
        }
        if(view[4].ant.type == 2 && findWorker(1) < 0 && view[queenPos].color != TRAIL) {
            moveTo = 4;
        }
        return {cell:moveTo};
    }
    else {
        if(pos < 0) return {cell:4}; //firebreak();
        var dir = view[4].ant.type==1?-1:1;
        var moveTo = deRotate(pos,dir);
        if(view[4].ant.food > 0 && view[moveTo].food > 0) {
            //have food, attempt to give to queen
            moveTo = deRotate(pos,-dir);
        }
        if(view[4].ant.type==1 && pos >= 0 && (view[deRotate(pos,dir*2)].food > 0 || view[deRotate(pos,dir*3)].food > 0)) {
            //attempt rotate
            moveTo = deRotate(pos,-dir);
        }
        if(view[4].ant.type==2 && pos >= 0 && (view[deRotate(pos,dir*2)].food > 0 || view[deRotate(pos,dir*3)].food > 0)) {
            //attempt rotate
            moveTo = deRotate(pos,-dir);
        }
        if(moveTo >= 0 && view[moveTo].ant != null && view[moveTo].ant.type == 5) {
            if(view[moveTo].ant.friend)
                moveTo = deRotate(moveTo,dir*2);
            else
                moveTo = deRotate(pos,-dir);
        }
        return {cell:moveTo};
    }
    return {cell:100+view[4].ant.type};//oh god
}

function foreverAlone() {
    var s0 = view[0].ant;
    var s1 = view[1].ant;
    var s2 = view[2].ant;
    var s3 = view[3].ant;
    var s5 = view[5].ant;
    var s6 = view[6].ant;
    var s7 = view[7].ant;
    var s8 = view[8].ant;
    //good
    if(!(s0 == null && s1 == null && s2 == null && s3 == null && s5 == null && s6 == null && s7 == null && s8 == null) && view[4].color == TRAIL) {
        if (view[0].color == TRAIL && !view[8].ant && view[8].color != TRAIL) return {cell: 8};
        else if (view[2].color == TRAIL && !view[6].ant && view[6].color != TRAIL) return {cell: 6};
        else if (view[6].color == TRAIL && !view[2].ant && view[2].color != TRAIL) return {cell: 2};
        else if (view[8].color == TRAIL && !view[0].ant && view[0].color != TRAIL) return {cell: 0};
        //Can't find color, or path is blocked? try diagonals regardless of color
        else if (!view[0].ant) return {cell: 0};
        else if (!view[2].ant) return {cell: 2};
        else if (!view[6].ant) return {cell: 6};
        else if (!view[8].ant) return {cell: 8};
        //Everything else failed? Stay put.
        else return {cell: 4};
    }
    //good
    if (view[4].color == TRAIL) { //If on colored square, try to move
        var totGreen = 0;
        for (var i = 0; i < 9; i++) { //Look for food
            if (view[i].food) {
                return {cell: i};
            }
            if(view[i].color == TRAIL) totGreen++;
        }
        var ret = getTrailMove();
        if(view[deRotate(ret.cell,1)].color == TRAIL && totGreen <= 4) ret.cell = deRotate(ret.cell,-1);
        else if(view[deRotate(ret.cell,-1)].color == TRAIL && totGreen <= 4) ret.cell = deRotate(ret.cell,1);
        return ret;
    } else { //If not on colored square, look for food, or set current color to 2.
        for (var i = 0; i < 9; i++) { //Look for enemies
            if (i != 4 && view[i].ant != null && !view[i].ant.friend) {
                var r = findOpenSpace(8-i,1);
                if(view[r].color == TRAIL) r = deRotate(r,1);
                return {cell: r};
            }
        }
        return {cell: 4, color:TRAIL};
    }
}

function getTrailMove() {
    if (view[0].color == TRAIL && !view[8].ant && view[8].color != TRAIL) return {cell: 8};
    else if (view[2].color == TRAIL && !view[6].ant && view[6].color != TRAIL) return {cell: 6};
    else if (view[6].color == TRAIL && !view[2].ant && view[2].color != TRAIL) return {cell: 2};
    else if (view[8].color == TRAIL && !view[0].ant && view[0].color != TRAIL) return {cell: 0};
    //Can't find color, or path is blocked? try diagonals regardless of color
    else if (!view[0].ant) return {cell: 0};
    else if (!view[2].ant) return {cell: 2};
    else if (!view[6].ant) return {cell: 6};
    else if (!view[8].ant) return {cell: 8};
    //Everything else failed? Stay put.
    else return {cell: 4};
}

function firebreak() {
    var ret = -1;
    if(findWorker(5) >= 0) {
        return {cell:8-findWorker(5)};
    }
    if(view[4].color != 5) {
        var myView = [0,0,0,0,0,0,0,0,0]
        for(var i=0; i < 9; i++) {
            myView[i] = view[i].color
            if(view[4].ant.food > 0 && view[i].food > 0) {
                myView[i] = 8;
            }
            if(view[i].ant != null && !view[i].ant.friend) return {cell:findOpenSpace(deRotate(i,2),1)};
        }
        var ret = clearAhead(myView);
        if(ret == null)
            return {cell:4,color:5};
        else {
            if(!(view[ret.cell].ant != null && view[ret.cell].ant.friend == false) && (view[4].ant.food == 0 || view[ret.cell].food == 0))
                return ret;
            return {cell:4,color:5};
        }
    }
    if(view[1].color == 5 && view[3].color == 5 && view[5].color == 5 && view[7].color == 5) {
        if(view[0].color != 8) return {cell:0,color:8};
        if(view[1].color != 8) return {cell:1,color:8};
    }
    if(view[1].color == 5 && view[7].color != 5) ret = {cell:7};
    if(view[3].color == 5 && view[5].color != 5) ret = {cell:5};
    if(view[5].color == 5 && view[3].color != 5) ret = {cell:3};
    if(view[7].color == 5 && view[1].color != 5) ret = {cell:1};
    if(view[1].color != 5 && view[3].color != 5 && view[5].color != 5 && view[7].color != 5) ret = {cell:1};
    if((view[1].color == 5 && view[7].color == 5) || (view[3].color == 5 && view[5].color == 5)) ret = {cell:0};
    var loop = 0;
    while(ret.cell >= 0 && ((view[ret.cell].food > 0 && view[4].ant.food > 0) || view[ret.cell].ant != null) && loop < 9) {
        loop++;
        ret.cell = (ret.cell + 2) % 9;
    }
    if(loop < 9 && ret.cell >= 0) return ret;
    return {cell:4};
}

//7,6,4,2,3
//O7,D2

function highwayRobbery() {
    var move = basicHighwayMove();
    if(move.cell >= 0 && view[move.cell].ant != null) {
        var n = HIGHWAY_COLORS.indexOf(view[4].color) + (view[4].color%2==0?1:HIGHWAY_COLORS.length);
        var nextMove = HIGHWAY_COLORS[n % HIGHWAY_COLORS.length];
        for(var i=0;i<9;i++) {
            if(view[i].color == nextMove) {
                return {cell:i};
            }
        }
    }
    return move;
}

function basicHighwayMove() {
    var isQueen = view[4].ant.type == 5;
    if(isHighwayCenter()) {
        var n = HIGHWAY_COLORS.indexOf(view[4].color) + 1;
        var nextMove = HIGHWAY_COLORS[n % HIGHWAY_COLORS.length];
        for(var i=0;i<9;i++) {
            if(view[i].color == nextMove) {
                if(view[i].ant == null)
                    return {cell:i};
                else {
                    return {cell:deRotate(i,1)};
                }
            }
        }
    }
    else {
        if(view[4].color == 7) {
            //move diagonal to yellow (2)
            for(var i=0;i<9;i++) {
                if(i != 4 && i % 2 == 0 && view[i].color == 2) {
                    return {cell:i};
                }
            }
        }
        else {
            //move orthogonal to blue (7)
            for(var i=0;i<9;i++) {
                if(i % 2 == 1 && view[i].color == 7) {
                    //try ortho yellow first
                    for(var j=0;j<9;j++) {
                        if(j % 2 == 1 && view[j].color == 2 && areAdjacent(i,j))
                            return {cell:j};
                    }
                    return {cell:i};
                }
            }
            //if orthogonal blue doens't exist...
            //...try diagonal to magenta
            for(var i=0;i<9;i++) {
                if(i != 4 && i % 2 == 0 && view[i].color == 3) {
                    return {cell:i};
                }
            }
            if(view[4].color != 2) {
                //...try diagonal blue
                for(var i=0;i<9;i++) {
                    if(i % 2 == 0 && view[i].color == 7)
                        return {cell:i};
                }
            }
            //...and orthogonal yellow
            for(var i=0;i<9;i++) {
                if(i % 2 == 1 && view[i].color == 2)
                    return {cell:i};
            }
        }
        var n = HIGHWAY_COLORS.indexOf(view[4].color) + 1;
        var nextMove = HIGHWAY_COLORS[n % HIGHWAY_COLORS.length];
        for(var i=0;i<9;i++) {
            if(view[i].color == nextMove) {
                return {cell:i};
            }
        }
    }
    return {cell:-1};
}

function isOnHighway() {
    var match = 0;
    var nxt = HIGHWAY_COLORS[(HIGHWAY_COLORS.indexOf(view[4].color)+1) % HIGHWAY_COLORS.length];//4
    var prv = HIGHWAY_COLORS[(HIGHWAY_COLORS.indexOf(view[4].color)+HIGHWAY_COLORS.length-2) % HIGHWAY_COLORS.length];//6
    for(var i=0;i<9;i++) {
        if(HIGHWAY_COLORS.indexOf(view[i].color) >=0 && (i == 4 || view[i].color != view[4].color))
            match++;
    }
    if(match >= 5) {
        //7,6,4,2,3


        if((view[1].color == nxt && view[7].color == prv)||(view[1].color == prv && view[7].color == nxt) || 
            (view[3].color == nxt && view[5].color == prv)||(view[3].color == prv && view[5].color == nxt)) {
            return true;
        }
        if((view[1].color == view[8].color && (view[1].color == nxt || view[1].color == prv))||(view[1].color == view[6].color && (view[1].color == nxt || view[1].color == prv)) || 
            (view[3].color == view[2].color && (view[3].color == nxt || view[3].color == prv))||(view[3].color == view[8].color && (view[3].color == nxt || view[3].color == prv))) {
            return true;
        }
        if((view[0].color == view[7].color && (view[0].color == nxt || view[0].color == prv))||(view[2].color == view[7].color && (view[2].color == nxt || view[2].color == prv)) || 
            (view[0].color == view[5].color && (view[0].color == nxt || view[0].color == prv))||(view[6].color == view[5].color && (view[6].color == nxt || view[6].color == prv))) {
            return true;
        }
        if(isHighwayCenter()) {
            return true;
        }
    }
    return false;
}

function isHighwayCenter() {
    if(HIGHWAY_COLORS.indexOf(view[4].color) >=0 && (((view[0].color != view[8].color || view[2].color != view[6].color) && view[4].ant.type == 1) || (view[0].color != view[8].color && view[2].color != view[6].color))){
        var m1 = view[1].color == view[7].color;
        var m2 = view[2].color == view[8].color;
        var m3 = view[0].color == view[6].color;
        var m4 = view[0].color != 1 && view[2].color != 1;
        if((m1?1:0)+(m2?1:0)+(m3?1:0) >= 2 && m4) {
            if(view[3].color != view[5].color && ((view[2].color != view[5].color && view[8].color != view[5].color) || view[4].ant.type == 1) && ((view[3].color != view[0].color && view[3].color != view[6].color) || view[4].ant.type == 1)) {
                return true;
            }
        }
        m1 = view[3].color == view[5].color;
        m2 = view[0].color == view[2].color;
        m3 = view[6].color == view[8].color;
        m4 = view[0].color != 1 && view[6].color != 1;
    //good
        if((m1?1:0)+(m2?1:0)+(m3?1:0) >= 2 && m4) {
            m1 = view[1].color != view[7].color;
            m2 = (view[0].color != view[1].color && view[1].color != view[2].color);
            m3 = (view[6].color != view[7].color && view[7].color != view[8].color);
            if(m1 && m2 && m3) {
                return true;
            }
            if(view[4].ant.type == 1 && ((m1?1:0)+(m2?1:0)+(m3?1:0)) >= 2) {
                return true;
            }
        }
    }
    return false;
}

function deRotateSide(m, amt) {
    return deRotate(m,amt*2);
}

/**Positive amount is clockwise**/
function deRotate(m, amt) {
    var rotationsCW = [1,2,5,8,7,6,3,0];
    var rotationsCCW = [3,6,7,8,5,2,1,0];
    if(m == 4 || m < 0 || m > 8 || amt == 0) return m;
    if(amt > 0)
        return rotationsCW[(rotationsCW.indexOf(m)+amt)%8];
    amt = -amt;
    return rotationsCCW[(rotationsCCW.indexOf(m)+amt)%8];
}

function areAdjacent(A, B) {
    if(A == 4 || B == 4 || A == B) return true;
    if(A % 2 == 0 && B % 2 == 0) return false;
    if(A % 2 == 1 && B % 2 == 0) return areAdjacent(B,A);
    if(A % 2 == 1 && B % 2 == 1) return !(8-A == B || 8-B == A);
    if(A == 0 && (B == 1 || B == 3)) return true;
    if(A == 2 && (B == 1 || B == 5)) return true;
    if(A == 6 && (B == 3 || B == 7)) return true;
    if(A == 8 && (B == 5 || B == 7)) return true;
    return false;
}

function findFirstTrail() {
    var pos = 0;
    var b = 0;
    while(view[pos].color != TRAIL && b < 8) {
        pos=deRotate(pos,1);
        b++;
    }
    return pos;
}

function clearAhead(sides) {
    var c=0;
    for(var i=0;i<9;i++) {
        if(view[i].color == 5) c++;
        if(view[i].color == 5 && i%2 == 0) c+=10;
    }
    if(c == 2) {
        if(view[0].color == 5 || view[2].color == 5 || view[6].color == 5 || view[8].color == 5) {
            return {cell:4,color:5};
        }
        if(view[0].ant == null)
            return {cell:0};
        if(view[2].ant == null)
            return {cell:2};
        if(view[6].ant == null)
            return {cell:6};
        if(view[8].ant == null)
            return {cell:8};
    }
    c = 0;
    sides[4] = 0;
    var toMatch =[{state:[1,1,1,
                          2,0,2,
                          0,1,0]},
                 {state:[0,2,1,
                         1,0,1,
                         0,2,1]},
                 {state:[0,1,0,
                         2,0,2,
                         1,1,1]},
                 {state:[1,2,0,
                         1,0,1,
                         1,2,0]}];
    for(var m=0;m<4;m++) {
        var score=0;
        for(var j=0;j<9;j++) {
            if(j!=4) {
                if(sides[j] == 5 && toMatch[m].state[j] == 1) {
                    score++;
                }
                if(sides[j] != 5 && (toMatch[m].state[j] == 0 || toMatch[m].state[j] == 2)) {
                    score++;
                }
                if(sides[j] == 5 && toMatch[m].state[j] == 2) {
                    score--;
                }
            }
        }
        if(score >= 6) {
            var clearOrder=[1,0,2];
            for(var r=0;r<clearOrder.length;r++) {
                var s = deRotateSide(clearOrder[r],m);
                if(view[s].color == 5) {
                    if(view[s].ant == null)
                        return {cell:s,color:8};
                    else
                        return {cell:4};
                }
            }
        }
    }
    return null;
}

function findOpenSpace(pos, dir) {
    if(pos > 8 || pos < 0) return pos;
    var b = 0;
    while(view[pos].ant != null && b < 8) {
        pos=deRotate(pos,dir);
        b++;
    }
    return pos;
}

function getHighestWorker() {
    var r=0;
    for(var i=0;i<9;i++) {
        if(i != 4 && view[i].ant != null) {
            if(view[i].ant.friend && view[i].ant.type > r) r = view[i].ant.type;
        }
    }
    return r;
}

function getNumWorkers(type) {
    var r=0;
    for(var i=0;i<9;i++) {
        if(i != 4 && view[i].ant != null) {
            if(view[i].ant.friend && view[i].ant.type == type) r++;
        }
    }
    return r;
}

function findWorker(type) {
    for(var i=0;i<9;i++) {
        if(i != 4 && view[i].ant != null) {
            if(view[i].ant.friend && view[i].ant.type == type) return i;
        }
    }
    return -1;
}

422% Selesai
422% karena pembaruannya pada 4/22 Lelucon ini telah berlangsung terlalu lama

Deteksi gerakan yang tidak valid: centang
Pembubutan: periksa
pekerja yang sedang Respawning ketika mereka dipangkas: sebagian besar memeriksa
Menghindari macet pada semut lain makanan lintah: ...

Perbarui 8/1

Berbalik dua kali lebih sering, menghasilkan ~ 15% lebih banyak makanan yang dikumpulkan

Perbarui 8/2

Menambahkan ketahanan vs terjebak.

Perbarui 9/9

Menambahkan kode untuk respawn pekerja 2 dan 3 ketika lengan itu hilang.

Perbarui 9/12

Kembalikan pembaruan sebelumnya. Peluncur itu bisa bergerak maju dengan lengan yang baru lahir, tetapi para pekerja akan bertindak rusak ketika mencoba untuk berbalik dan itu akan meledak.

Selain memperbaikinya, mengkonfigurasi ulang kode respawn-semua-pekerja menjadi lebih diskriminatif: mengurangi tabrakan dengan pekerja "hilang" di dekatnya serta mencegah respawn sepenuhnya di atas 60 makanan: 60 makanan cukup untuk mencetak 3 posisi teratas sebagian besar waktu, yang lebih baik daripada menghabiskan 4 makanan untuk pekerja yang kemungkinan besar baru saja dilepas lagi dalam waktu singkat: sangat sedikit semut yang mendapat skor lebih dari 60 kecuali jika mereka mengesampingkan kemampuan Glider. Juga menambahkan beberapa kode agar pekerja yang hilang dapat merusak jalur semut lainnya.

Beberapa perbaikan lain yang berkaitan dengan situasi di mana glider bisa macet bahkan ketika ada langkah sah yang bisa dibuatnya (mis. Meminta ratu membuang lengan yang macet dan mengulanginya nanti).

Mengkonfigurasi ulang warna untuk menggunakan variabel yang ditentukan dan mengubah warna jejak dari kuning menjadi hijau untuk menghindari "terperangkap" di Miners pada sistem rel Rail (solo ratu akan terus-menerus diarahkan).

Beberapa kode baru dimasukkan dari entri saya yang lain, Black Hole .

Perbarui 9/13

Memperbaiki bug dalam kode pindah-keluar-orrientasi. Pasangan 2s pegangan dikonversi menjadi TRAILs.

Perbarui 1/21

  • Glider sekarang dapat berbelok ke kiri (animasi menunggu).
  • Game awal dipercepat dengan menelurkan satu pekerja segera setelah makanan tersedia.
  • Meluncur dengan satu lengan juga bisa mengubah arah.
  • "Jangan respawn pekerja" ambang batas dinaikkan
  • "Jangan buang-buang makanan dengan mencoba respawn dan gagal" kurangi %5menjadi%3

Perbarui 1/25

  • Memperbaiki beberapa skenario kebuntuan, meningkatkan efisiensi beberapa poin dalam beberapa pertandingan.

Perbarui 2/20

  • Mengatasi beberapa skenario edge / case deadlock, membuat konfigurasi 5-dan 3-sem lebih stabil. Beberapa pengaturan makanan dapat menyebabkan Glider menjadi bingung dan mengalami jalan buntu (berulang kali berputar bolak-balik) atau meledak.
    • Salah satu ledakan adalah hasil dari semut penyapu pindah ke makanan selama langkah terakhir rotasi, kemudian dengan asumsi "sial, saya punya makanan, mengorbankan diri untuk mendapatkannya ke ratu" ketika tidak perlu (langkah standar berlaku ).
    • Salah satu ledakan adalah hasil dari sel di bawah ratu yang sudah menjadi TRAIL(hijau), menyebabkan salah satu gerakan "diam" ratu (ditangani oleh fallback untuk foreverAlone()membuat ratu menjauh dari kelompok. Memperbaiki ini dengan membuat penjaga stasioner memodifikasi warna sel ratu tepat sebelum ratu bertindak.
    • Lebih diinginkan daripada memperbaiki kode ratu, karena ini memungkinkan kita untuk menghindari kebuntuan yang mungkin terjadi dengan mempertahankan mundur ke foreverAlone(), mengeluarkan ratu, bahkan jika kita tidak bisa menyelamatkan sayapnya.
  • foreverAlone()fungsi diperbarui untuk hanya pindah ke makanan jika sel di bawah ratu TRAIL(hijau). Kehilangan efisiensi sangat sedikit.
  • Glider rebuild morebetter menempatkan sel hijau yang berdekatan, perubahan findFirstYellow()dan penggunaannya. Ini, dikombinasikan dengan peluru sebelumnya menghasilkan penghindaran yang lebih baik dari sel-sel yang terlihat sebelumnya dan / atau tempat ledakan baru-baru ini: glider yang baru terbentuk akan - dalam sebagian besar kasus - bergerak ke arah yang tidak sesuai dengan kedatangan ratu. lintasan.
    • Berganti nama fungsi: Kuning belum menjadi warna jejak ratu dalam waktu yang sangat lama.
    • Fungsi juga salah menemukan "sel pertama jejak" karena pemeriksaan logika terbalik: while ==bukanwhile !=

Perbarui 2/25

  • Lebih banyak perbaikan tepi / tabrakan / jalan buntu, secara keseluruhan meningkatkan kinerja.
  • Beberapa perubahan pada perilaku seperti LightSpeed, disarankan oleh Alion (dari perilaku entri sendiri ). Umumnya meningkatkan pengumpulan makanan, tetapi juga menghindari skenario kebuntuan tertentu.

Pembaruan 3/11: Sayap Patah Glider yang berada di jalan raya mengubah perilaku ke sistem "mencuri makanan" seperti Vampir. Terutama ini tidak akan berarti banyak kecuali ratu sendiri di dalam Highway, yang dengan jumlah hijau yang sangat besar, membuat ini ruang yang mustahil untuk melarikan diri.

Pekerja 1 akan berusaha untuk melakukan perbaikan di koridor tengah Highway dan melakukan tebakan tebakan terbaik jika menemukan dirinya di tempat Highway yang BUKAN rel (daripada memperbaiki pusat yang salah). Bepergian dalam arah yang sama dengan ratu (berlawanan dengan pekerja 2, 3, dan 4).

Pekerja 2, 3, dan 4 berupaya melakukan pencurian drive-by pada ratu Highway, lalu kembali ke ratu mereka sendiri, yang menumbuhkan pekerja ketika ...

  • tidak dapat melihat salah satu pekerjanya sendiri
  • sedang duduk di atas magenta
  • memiliki makanan di antara ambang (dengan batas lunak di kedua sisi)
  • dimodifikasi secara acak sederhana

Ini menghindari menciptakan terlalu banyak pekerja (bekerja sampai ~ 100) dan ketika serangkaian pekerja sarat kembali, ada pendapatan makanan bersih (karena tidak ada pemijahan saat melihat pekerja sendiri) akhirnya mendorong Glider di atas ambang batas atas dan pemijahan (secara besar-besaran). berkurang) pekerja baru.

Ini adalah taktik yang aku rencanakan untuk dituliskan ke Vampir, tetapi upaya pertama Vampir untuk memblokir ratu Highway dan hanya berdarah saja berhasil, jadi tidak ada insentif untuk menciptakan perilaku ini. Kode deteksi jalan raya ditulis dari awal dan berbeda dari Vampir, meskipun deteksi cermin (IsHighwayCenter) sangat mirip, hanya karena kesederhanaan polanya.

Perbarui 3/16

Selain dari dua perbaikan bug kecil (lihat riwayat sunting) optimasi dilakukan untuk fungsi Forever Alone (dicuri dari ...? Awalnya) untuk menghindari pelacakan kembali, memungkinkan ratu untuk mengamati lebih banyak sel rata-rata ketika sendirian.

Metode gerakan mundur ditambahkan ketika highwayRobbery()fungsi gagal mengembalikan gerakan yang valid ketika semut berada di pusat jalan raya (dan tidak ada logika lain yang menyesuaikan ini dengan nilai yang masuk akal). Jika itu gagal, semut melakukan standar logika meluncur mereka.

Tweak kecil

  • Ratu tidak lagi mencoba untuk memulai pemijahan ketika dia mendeteksi bahwa dia berada di jalan raya
  • Ratu tidak lagi terbakar melalui Kalori yang mencoba menyelesaikan formasi peluncur yang tidak akan pernah selesai (menelurkan pekerja 4, pekerja 4 tidak melihat pekerja 1, pekerja 4 bergerak menjauh, ulangi)
  • Menyesuaikan nilai-nilai modulo makanan agar coprime dengan siklus warna Highway.
  • Memperbaiki "finish rotate with 1 arm" yang menyebabkan jalan buntu
  • Perbaikan kecil pada isOnHighway()cek
  • Semut yang dibuat bergerak ke kuning ortogonal sebelum pindah ke sel biru ortogonal ketika berada di jalan raya (menghemat 1 belokan menemukan pusat setelah terlepas).

Perbarui 3/26

Tweak lebih kecil.

Perbarui 4/21

Perbaikan kecil.


Secara longgar berbasis pada Steamroller .

Mengumpulkan 4 makanan meninggalkan jejak kuning. Setelah memiliki empat makanan, itu menghasilkan satu dari setiap jenis pekerja di sekitarnya. Kemudian mulai meluncur bersama dengan dorongan maksimum. Animasi bergerak di atas membutuhkan 1 giliran permainan untuk menyelesaikan: karena pekerja 1 diproduksi terlebih dahulu, dijalankan terlebih dahulu, lalu 2, 3, dan 4, sebelum menyelesaikan giliran dengan sang ratu. Animasi berputar membutuhkan 2 putaran, jeda singkat adalah ketika ratu tampil {cell:4}dan makanan bergerak ketika pekerja 3, 4, dan ratu semua tampil {cell:4}untuk bersiap untuk terus meluncur.


Saya suka ide bot ini
Destructible Lemon

1
@DestructibleLemon Terima kasih! Saya sedang berpikir tentang Steamroller / Piercer dan berpikir dalam hati "tunggu, ratu pergi terakhir, pasti saya bisa mendapatkan lima semut untuk bermain bagus ..." meraih selembar kertas, merobek beberapa kotak, dan mulai memindahkannya. Berhasil jadi saya menulis beberapa kode. Harus melakukan banyak penanganan kesalahan dan memperbaiki bug untuk kasus tepi (bot awal akan macet atau mengkonsumsi semua makanan yang bisa dan dilakukan, menambahkan operasi belokan memperkenalkan banyak skenario kasus tepi di mana ia akan menghancurkan pembentukan dan kecelakaan ).
Draco18s

@trichoplax sampai menabrak semut lain?
Destructible Lemon

@DestructibleLemon eeeehhhh ... mungkin. Setidaknya tidak didiskualifikasi ...
Draco18s

3
@ KZhang Oh saya lakukan. Carilah komentar "// upaya putar"
Draco18s

13

Kincir angin

Ini kincir angin ... menunggu Pria dari la Mancha (atau Vampir dari la Mancha?) Yang akan naik untuk menurunkannya.

Windmill, tahap tengah awal permainan

Saya mencoba untuk mengeksplorasi seberapa jauh desain Miners on Rail - sekarang terdegradasi ke dalam sejarah edit jawaban itu - dapat didorong, didorong oleh pengamatan bahwa batu loncatan sebaiknya dicat ke dinding alternatif dari lubang tambang. Dan kemudian tumbuh dan berevolusi ...

Sementara rail kelas berat kami ditambah geometri poros ringan hampir sama dan pola rel terlihat cukup mirip untuk menyebabkan beberapa semut salah satu dari yang lain, implementasinya ditulis dari awal dan jauh lebih pejalan kaki dalam gaya pengkodean, dan memperkenalkan satu kunci baru ide. Perbesar ke hub untuk melihatnya berseliweran .

var AJM=1;var ASM=2;var AE=3;var ASF=4;var AQ=5;var RW=true;var EFCO=false;var THC=1;var TH0=0;var TH1=15;var TH2=17;var TH3=67;var TH4=120;var TH5=390;var THX=15;var THFCO1=9;var THFCO2=26;var THFCO3=75;var RM1=7;var RD1=4;var RM2=19;var RD2=THX;var PW=1;var PY=2;var PP=3;var PC=4;var PR=5;var PG=6;var PB=7;var PK=8;var LN=0;var LCLR=PW;var LT=PB;var LLSF=PP;var LA=PP;var LRL0=PC;var LRL1=PG;var LRL2=LRL0;var LRM0=PR;var LRM1=PB;var LRM1_WRP=PK;var LRM2=PG;var LRR0=PG;var LRR1=PW;var LRR1U=PR;var LRR1V=PY;var LRR1X=PK;var LRR2=PY;var LMX_M0=LCLR;var LMX_M1IN=PC;var LMX_M1OUT=PY;var LMX_M2IN=PP;var LMX_M2OUT=PR;var LMX_M3IN=PB;var LMX_M3OUT=PK;var LMS_WRP=PK;var LMR0=PK;var LML1=PY;var LMR2=PR;var LML3=PC;var LMMF=PG;var LMMH=PP;var LG3=PK;var LG4=PR;var LG5=PK;var LG6=PB;var LP0=LCLR;var LPB=PC;var LPG=PY;var LPG1=PR;var LPX=PP;var FALSE_X9=[false,false,false,false,false,false,false,false,false];
var UNDEF_X9=[undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined];
var QCPERD=6;var LCL_QC_RESET=LCLR;var LCRQC=[PY,PP,PC,PR,PG,PK];var LCRQCVAL=Array.from(FALSE_X9);var LCRQC_VALUE=Array.from(UNDEF_X9);for (var i=0; i<QCPERD; i++){LCRQCVAL[LCRQC[i]]=true;LCRQC_VALUE[LCRQC[i]]=i;}var SCPERD=7;var LCL_SC_RESET=LCLR;var LCRSC=[PY,PP,PC,PR,PG,PB,PK];var LCRSCVAL=Array.from(FALSE_X9);var LCRSC_VALUE=Array.from(UNDEF_X9);for (i=0; i<SCPERD; i++){LCRSCVAL[LCRSC[i]]=true;LCRSC_VALUE[LCRSC[i]]=i;}var LCRPHR=Array.from(FALSE_X9);LCRPHR[LPG]=true;LCRPHR[LPG1]=true;var LCRPHASES=Array.from(LCRPHR);LCRPHASES[LPX]=true;var LCRGRM1=Array.from(FALSE_X9);LCRGRM1[LRM1]=true;LCRGRM1[LRM1_WRP]=true;var LCRGRM_ALL=Array.from(FALSE_X9);LCRGRM_ALL[LRM0]=true;LCRGRM_ALL[LRM1]=true;LCRGRM_ALL[LRM1_WRP]=true;LCRGRM_ALL[LRM2]=true;var LCRGRR1_OUT=Array.from(FALSE_X9);LCRGRR1_OUT[LRR1V]=true;LCRGRR1_OUT[LRR1X]=true;var LCRGRR1B=Array.from(LCRGRR1_OUT);LCRGRR1B[LRR1U]=true;var LCRGRR1=Array.from(LCRGRR1B);LCRGRR1[LRR1]=true;var LCRMX_IO=Array.from(FALSE_X9);LCRMX_IO[LMX_M1IN]=true;LCRMX_IO[LMX_M1OUT]=true;LCRMX_IO[LMX_M2IN]=true;LCRMX_IO[LMX_M2OUT]=true;LCRMX_IO[LMX_M3IN ]=true;LCRMX_IO[LMX_M3OUT]=true;var LCRMX=Array.from(LCRMX_IO);LCRMX[LMX_M0]=true;var LCRMX_IN=Array.from(FALSE_X9);LCRMX_IN[LMX_M1IN]=true;LCRMX_IN[LMX_M2IN]=true;LCRMX_IN[LMX_M3IN ]=true;var LCRMX_OUT=Array.from(FALSE_X9);LCRMX_OUT[LMX_M1OUT]=true;LCRMX_OUT[LMX_M2OUT]=true;var LCRMM_FOOD=Array.from(FALSE_X9);LCRMM_FOOD[LCLR]=true;LCRMM_FOOD[LMMF]=true;var LCRMM_HOME=Array.from(FALSE_X9);LCRMM_HOME[LCLR]=true;LCRMM_HOME[LMMH]=true;var LCRMS=Array.from(FALSE_X9);LCRMS[LCLR]=true;LCRMS[LMS_WRP]=true;var LCRFRLL0=Array.from(FALSE_X9);LCRFRLL0[LCLR]=true;LCRFRLL0[LMR0]=true;LCRFRLL0[LMR2]=true;LCRFRLL0[LRM0]=true;LCRFRLL0[LRM2]=true;var LCRFRLL1=Array.from(FALSE_X9);LCRFRLL1[LCLR]=true;LCRFRLL1[LMR0]=true;LCRFRLL1[LMR2]=true;LCRFRLL1[LRR0]=true;LCRFRLL1[LRM1]=true;LCRFRLL1[LRR2]=true;var LCRFRLL2=Array.from(FALSE_X9);LCRFRLL2[LCLR]=true;LCRFRLL2[LMR0]=true;LCRFRLL2[LMR2]=true;LCRFRLL2[LRM0]=true;LCRFRLL2[LRR1V]=true;var TN=8;var POSC=4;var NOP={cell:POSC};var AIMU=1;var AIML=3;var AIMR=5;var AIMD=7;var FWD_CELLS=[[ true,true,false,true,true,false,false,false,false ],[ true,true,true,true,true,true,false,false,false ],[ false,true,true,false,true,true,false,false,false ],[ true,true,false,true,true,false,true,true,false ],[ true,true,true,true,true,true,true,true,true ],[ false,true,true,false,true,true,false,true,true ],[ false,false,false,true,true,false,true,true,false ],[ false,false,false,true,true,true,true,true,true ],[ false,false,false,false,true,true,false,true,true ]];var PTNOM=-9;var PTHOME=[LRM0,LRL0,LRM0,LRL0,LN,LRL0,LN,LN,LRM0];var PTGARDEN=[LG6,LG5,LG4,LN,LN,LG3,LN,LRL0,LRL1];var PTFRM0=[LRL1,LRM1,LCRGRR1,LRL0,LRM0,LRR0,LRL2,LRM2,LRR2];var PTFRM1=[LRL2,LRM2,LRR2,LRL1,LCRGRM1,LCRGRR1,LRL0,LRM0,LRR0];var PTFRM2=[LRL0,LRM0,LRR0,LRL2,LRM2,LRR2,LRL1,LCRGRM1,LCRGRR1];var PTGRM0=[LRL1,LCRGRM1,LCRGRR1,LRL0,LRM0,LRR0,LRL2,LRM2,LRR2];var PTGRM1=PTFRM1;var PTGRM2=PTFRM2;var PTGRM2B=[LRL0,LRM0,LRR0,LRL2,LRM2,LRR2,LRL1,LCRGRM1,LCRGRR1B];var PTGRM1_WRP=[LRL2,LRM2,LRR2,LRL1,LRM1_WRP,LRR1X,LRL0,LRM0,LRR0];var PTFRL0=[LCRFRLL1,LRL1,LRM1,LCRFRLL0,LRL0,LRM0,LCRFRLL2,LRL2,LRM2];var PTFRL1=[LCRFRLL2,LRL2,LRM2,LCRFRLL1,LRL1,LCRGRM1,LCRFRLL0,LRL0,LRM0];var PTFRL0H=[LN,LRL1,LRM1,LN,LRL0,LRM0,LN,LN,LN];var PTFRL1G=[LCRFRLL2,LRL2,LRM2,LG3,LRL1,LCRGRM1,LCRPHASES,LRL0,LRM0];var PTFRL2=[LCRFRLL0,LRL0,LRM0,LCRFRLL2,LRL2,LRM2,LCRFRLL1,LRL1,LCRGRM1];var PTGRL0=[LCRFRLL1,LRL1,LCRGRM1,LCRFRLL0,LRL0,LRM0,LCRFRLL2,LRL2,LRM2];var PTGRL1=PTFRL1;var PTGRL2=PTFRL2;var PTGRR0=[LCRGRM1,LCRGRR1,LCLR,LRM0,LRR0,LMR0,LRM2,LRR2,LCRMX];var PTGRR2=[LRM0,LRR0,LMR0,LRM2,LRR2,LCRMX,LCRGRM1,LCRGRR1,LCLR];var PTGRR1=[LRM0,LCRGRM1,LRM2,LRR0,LCRGRR1,LRR2,LMR0,LCLR,LCRMX];var PTMS0R_IN=[LRR0,LRR1U,LRR2,LMR0,LCLR,LCRMX_IN,LCLR,LCLR,LML1];var PTMS0R_OUT=[LRR0,LRR1U,LRR2,LMR0,LCLR,LCRMX_IO,LCLR,LCLR,LML1];var PTMS0R_OUT1=[LRR0,LCRGRR1_OUT,LRR2,LMR0,LCLR,LCRMX,LCLR,LCLR,LML1];var PTMS0=[LCLR,LCRMM_HOME,LML3,LMR0,LCRMM_FOOD,LCLR,LCLR,LCLR,LML1];var PTMS1=[LMR0,LCRMM_HOME,LCLR,LCLR,LCRMM_FOOD,LML1,LMR2,LCLR,LCLR];var PTMS2=[LCLR,LCRMM_HOME,LML1,LMR2,LCRMM_FOOD,LCLR,LCLR,LCLR,LML3];var PTMS3=[LMR2,LCRMM_HOME,LCLR,LCLR,LCRMM_FOOD,LML3,LMR0,LCLR,LCLR];var PTMS1_IN=[LMR0,LCRMM_HOME,LCRMX_IN,LCLR,LCRMM_FOOD,LML1,LMR2,LCLR,LCLR];var PTMS1_IO=[LMR0,LCRMM_HOME,LCRMX_IO,LCLR,LCRMM_FOOD,LML1,LMR2,LCLR,LCLR];var PTMS0_OUT=[LCLR,LCLR,LML3,LMR0,LCLR,LCRMX_IO,LCLR,LCLR,LML1];var PTMS0_WRAPPING=[LCLR,LCRMM_HOME,LML3,LMR0,LCRMM_FOOD,LCRMS,LRL0,LRL1,LRL2];var PTGRL1_WRP=[LMR0,LCLR,LMS_WRP,LRL0,LRL1,LRL2,LRM0,LRM1_WRP,LRM2];var PTMS0FL=[LMMH,LML3,LN,LMMF,LCLR,LN,LCLR,LML1,LN];var PTMS1FL=[LMMH,LCLR,LN,LMMF,LML1,LN,LCLR,LCLR,LN];var PTMS2FL=[LMMH,LML1,LN,LMMF,LCLR,LN,LCLR,LML3,LN];var PTMS3FL=[LMMH,LCLR,LN,LMMF,LML3,LN,LCLR,LCLR,LN];var PTMS0FR=[LN,LCLR,LMMH,LN,LMR0,LMMF,LN,LCLR,LCLR];var PTMS1FR=[LN,LMR0,LMMH,LN,LCLR,LMMF,LN,LMR2,LCLR];var PTMS2FR=[LN,LCLR,LMMH,LN,LMR2,LMMF,LN,LCLR,LCLR];var PTMS3FR=[LN,LMR2,LMMH,LN,LCLR,LMMF,LN,LMR0,LCLR];var CCW=[6,7,8,5,2,1,0,3,6,7,8,5,2,1,0,3,6,7,8,5,2,1];
var xn=-1;var fwdWrong=[];var rearWrong=[];var here=view[POSC];var mC=here.color;var myself=here.ant;var mT=myself.type;var mF=myself.food;var mS=(mT==AE||(mT!=AQ&&mF>0));if (EFCO&&(mT==AQ)){if (mF<=THFCO1){QCPERD=5;} else if (mF<=THFCO2){QCPERD=4;} else if (mF<=THFCO3){QCPERD=5;}}var dOK=[true,true,true,true,true,true,true,true,true];
var uo=true;var sL=[0,0,0,0,0,0,0,0,0];var sD=[0,0,0,0,0,0,0,0,0];var sN=[0,0,0,0,0,0,0,0,0];var sT=[0,0,0,0,0,0,0,0,0];var fdL=0;var fdD=0;var fdT=0;sT[mC]++;for (i=0; i<TN; i+=2){var cell=view[CCW[i]];sD[cell.color]++;sN[cell.color]++;sT[cell.color]++;if (cell.food>0){fdD++;fdT++;if (mS){dOK[CCW[i]]=false;uo=false;}}}for (i=1; i<TN; i+=2){var cell=view[CCW[i]];sL[cell.color]++;sN[cell.color]++;sT[cell.color]++;if (cell.food>0){fdL++;fdT++;if (mS){dOK[CCW[i]]=false;uo=false;}}}var aF=[0,0,0,0,0,0];var aLF=[0,0,0,0,0,0];var aUF=[0,0,0,0,0,0];var fT=0;var mQ=0;var aE=[0,0,0,0,0,0];var aLE=[0,0,0,0,0,0];var aUE=[0,0,0,0,0,0];var eT=0;for (i=0; i<TN; i++){var cell=view[CCW[i]];if (cell.ant){if (cell.ant.friend){aF[cell.ant.type]++;fT++;if (cell.ant.type==AQ){xn=i&6;mQ=i&1;}if (cell.ant.food>0){aLF[cell.ant.type]++;} else {aUF[cell.ant.type]++;}} else {aE[cell.ant.type]++;eT++;if (cell.ant.food>0){aLE[cell.ant.type]++;} else {aUE[cell.ant.type]++;}}dOK[CCW[i]]=false;uo=false;}}switch (mT){case AQ:return (rQSs());case ASF:if (mQ==1){return (rSSs());} else if (aF[AQ]>0){return (rGSs());} else {return (rLSSy());}case AE:return (rESs());case AJM:case ASM:if (aE[AQ]>0){return (rDSs());} else if (mF>0){return (rLSs());} else {return (rUSs());}default:return NOP;}function rQSs (){switch (aF[ASF]){case 0:return (rQScrSy());case 1:for (var i=0; i<TN; i++){var cell=view[CCW[i]];if (cell.ant&&cell.ant.type==ASF){xn=i&6;if (i&1){if (mF<=THX){return (rQLsSy());} else {return (rQLvSy());}} else {return (rQSgSy());}}}break;case 2:for (i=0; i<TN; i+=2){var cell0=view[CCW[i]];var cell1=view[CCW[i+1]];if ((cell0.ant&&(cell0.ant.type==ASF))&&(cell1.ant&&(cell1.ant.type==ASF))){xn=i;return (rQOSy());}}return (rQCSy());default:return (rQCSy());}return NOP;}function rSSs (){if (view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&(view[CCW[xn+3]].ant.type==ASF)){return (rSOSy());} else if (view[CCW[xn+1]].ant.food<=THX){return (rSLSy());} else {return (rSESy());}}function rGSs (){var secCell=view[CCW[xn+7]];if (secCell.ant&&(secCell.ant.friend==1)&&(secCell.ant.type==ASF)){return (rGOSy());} else {return (rGSSy());}return NOP;}function rESs (){if (aF[AQ]>0){return (rEHyS());} else if (aF[AJM] +aF[ASM]>0){return (rEBRSy());} else {return (rEASy());}return NOP;}function rDSs(){if (aF[AQ]>0){return (rDHSy());} else {for (var i=0; i<TN; i++){if (view[CCW[i]].ant&&(view[CCW[i]].ant.type==AQ)){if (i&1){if ((view[CCW[i+1]].ant&&view[CCW[i+1]].ant.friend&&view[CCW[i+2]].ant&&view[CCW[i+2]].ant.friend)||(view[CCW[i-1]].ant&&view[CCW[i-1]].ant.friend&&view[CCW[i+6]].ant&&view[CCW[i+6]].ant.friend)||(view[CCW[i+2]].ant&&view[CCW[i+2]].ant.friend&&view[CCW[i+6]].ant&&view[CCW[i+6]].ant.friend)){if (dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else if (dOK[CCW[i+3]]){return {cell:CCW[i+3]};} else if (dOK[CCW[i+5]]){return {cell:CCW[i+5]};}}} else {if (view[CCW[i+1]].ant&&view[CCW[i+1]].ant.friend&&
view[CCW[i+7]].ant&&view[CCW[i+7]].ant.friend){if (dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else if (dOK[CCW[i+3]]){return {cell:CCW[i+3]};} else if (dOK[CCW[i+5]]){return {cell:CCW[i+5]};} else if (dOK[CCW[i+6]]){return {cell:CCW[i+6]};} else if (dOK[CCW[i+2]]){return {cell:CCW[i+2]};}}if ((i<=2)&&dOK[CCW[i+7]]){return {cell:CCW[i+7]};}if ((i>=4)&&dOK[CCW[i+1]]){return {cell:CCW[i+1]};}}if (fT==0){if (view[CCW[i]].color!=PP){return {cell:CCW[i],color:PP};} else if (mC!=LCLR){return {cell:POSC,color:LCLR};}}}}}return NOP;}function rUSs (){if ((aF[AQ]>0)&&!LCRQCVAL[view[CCW[xn+mQ]].color]){return (rUHSy());} else if ((fT+eT>=4)&&(aF[AJM]+aF[ASM] +aF[AE]>=3)){return (rUCRSy());} else if (aF[AQ]>0){return (rUHSy());} else if (aF[ASF]>0){if (aF[ASF]>1){return (rM2R1Sy());} else {return (rURHSy());}} else if (aF[AE]>0){return (rUBRSy());} else if (spcRL1()){return (rULRL1Sy());} else if (spcRR0()){return (rULRR0Sy());} else if (spcRR2()){return (rULRR2Sy());} else if (spcMS()){return (rUDSSy());} else if (spcRL02()){return (rULRL02Sy());} else if (spcRM()){return (rUTRRSy());} else if (spcRR1()){return (rUPSSy());} else if (spcMS0R()){return (rUESSy());} else if (spcMS0W()){return (rUSWSy());}return (rLostMSy(true));}function rLSs (){if ((fT>=3)&&(fT+eT>=4)){return (rLCRSy());} else if (aF[ASF]>0){if (aF[ASF]>1){return (rM2R1Sy());} else {return (rLRHSy());}} else if (spcMFL()){return (rLLLWSy());} else if (spcMFR()){return (rLLRWSy());} else if (spcRL1()){return (rLLRL1Sy());} else if (spcRR0()){return (rLLRR0Sy());} else if (spcRR2()){return (rLLRR2Sy());} else if (spcMS0R()){return (rLLSSy());} else if (spcMS0ROut()){return (rLLVSSy());} else if (spcMS()&&(aF[AE]==0)){return (rLASSy());} else if (spcRL02()){return (rLLRL02Sy());} else if (spcRM()){return (rLTRRSy());} else if (spcRR1()){return (rLDSSy());} else if (aF[AE]>0){return (rLFRSy());}return (rLostMSy(true));}function rQScrSy(){if (uo){if (fdT>0){return (rQSETc());} else if (mF>=THC){for (var i=0; i<TN; i+=2){if ((view[CCW[i]].color==LT)||(view[CCW[i+1]].color==LT)){return {cell:CCW[i+1],type:ASF};}}return {cell:1,type:ASF};} else if (mC!=LT){if ((mC==LCLR)||(sN[LCLR]>=TN-1)){return {cell:POSC,color:LT};} else {return (rQSTCTc());}} else if ((sN[LCLR]>=4)&&(sN[LT]==1)){for (var i=0; i<TN; i+=2){if ((view[CCW[i]].color==LT)||(view[CCW[i+1]].color==LT)){return {cell:CCW[i+4]};}}} else if (sN[LCLR]==TN){return {cell:0};} else {return (rQSATc());}} else {if ((fdT>0)&&(eT>0)&&(eT==aE[AQ])){return (rQSSTc());} else {return (rQSEvTc());}}return NOP;}function rQSgSy(){if (fdT>0){if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else {for (var i=2; i<TN-1; i++){if (dOK[CCW[xn+i]]&&(view[CCW[xn+i]].food>0)){return {cell:CCW[xn+i]};}}for (var i=2; i<TN-1; i++){if (dOK[CCW[xn+i]]){     return {cell:CCW[xn+i]};}}return NOP;}} else if ((mF>TH0)&&(mC==LCL_QC_RESET)){if (dOK[CCW[xn+7]]){return { cell:CCW[xn+7],type:AE};} else if (view[CCW[xn]].color==LPB){if (dOK[CCW[xn+3]]){return { cell:CCW[xn+3],type:AE};} else if (dOK[CCW[xn+5]]){return { cell:CCW[xn+5],type:AE};} else if (dOK[CCW[xn+6]]){return { cell:CCW[xn+6],type:AJM};} else if (dOK[CCW[xn+2]]){return { cell:CCW[xn+2],type:AJM};} else if (dOK[CCW[xn+4]]){return { cell:CCW[xn+4],type:AJM};} else if (dOK[CCW[xn+1]]){return { cell:CCW[xn+1],type:ASF};}}}return NOP;}function rQOSy(){if ((aE[AQ]>0)&&(mF>0)){for (var i=2; i<TN; i++){if (view[CCW[xn+i]].ant&&(view[CCW[xn+i]].ant.type==AQ)&&!view[CCW[xn+i]].ant.friend){var j=(xn&4) ? 1 : -1;if (dOK[CCW[xn+i-j]]){return {cell:CCW[xn+i-j],type:AJM};} else if (dOK[CCW[xn+i+j]]){return {cell:CCW[xn+i+j],type:AJM};} else if (i==5){var i1=5-2*j;var i2=5+2*j;if (dOK[CCW[xn+i1]]&&!(view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend&&
view[CCW[xn+6]].ant&&view[CCW[xn+6]].ant.friend&&
view[CCW[xn+i2]].ant&&view[CCW[xn+i2]].ant.friend)){return {cell:CCW[xn+i1],type:AJM};} else if (dOK[CCW[xn+i2]]&&!(view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend&&
view[CCW[xn+6]].ant&&view[CCW[xn+6]].ant.friend&&
view[CCW[xn+i1]].ant&&view[CCW[xn+i1]].ant.friend)){return {cell:CCW[xn+i2],type:AJM};}} else if ((i==3)&&dOK[CCW[xn+5]]&&!(view[CCW[xn+2]].ant&&view[CCW[xn+2]].ant.friend&&
view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend)){return {cell:CCW[xn+5],type:AJM};} else if ((i==7)&&dOK[CCW[xn+5]]&&!(view[CCW[xn+6]].ant&&view[CCW[xn+6]].ant.friend)){return {cell:CCW[xn+5],type:AJM};}}}} else if ((mF>0)&&(view[CCW[xn+7]].color==LA)&&dOK[CCW[xn+7]]){return {cell:CCW[xn+7],type:AJM};} else if (view[CCW[xn+1]].ant.food>0){if ((mF>0)&&dOK[CCW[xn+2]]){return {cell:CCW[xn+2],type:AJM};}} else if ((aLF[AJM]+aLF[ASM]>0)&&(mF>0)&&(sN[LA]>0)){for (var i=2; i<TN; i++){var c=CCW[xn+i];if (dOK[c]&&(view[c].color==LA)){return {cell:c,type:AJM};}}} else if (eT>0){var bandits=aUE[1]+aUE[2]+aUE[3]+aUE[4];if ((mF>THX)&&((bandits>=2)||((bandits>=1)&&view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend&&(view[CCW[xn+5]].color==LA)&&(view[CCW[xn+7]].ant&&view[CCW[xn+7]].ant.friend&&
(view[CCW[xn+7]].color==LA))||(view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&
(view[CCW[xn+3]].color==LA))))){if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};}}if (mF<RD1){if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};}} else if ((bandits>=1)&&(mF>0)){if ((mF>THX)&&(mF % RM2==RD2+xn/2)){if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};}}for (var i=2; i<TN; i++){var c=CCW[xn+i];var c1,c2;if ((xn==2)||isSc0(view[CCW[xn+1]].color)){c1=CCW[xn+i-1];c2=CCW[xn+i+1];} else if ((xn==6)||isSc1(view[CCW[xn+1]].color)){c1=CCW[xn+i+1];c2=CCW[xn+i-1];} else {break;}if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.food==0)){if (dOK[c1]&&!(view[c2].ant&&view[c2].ant.friend&&view[c2].ant.food>0)){return {cell:c1,type:AJM};} else if (dOK[c2]&&!(view[c1].ant&&view[c1].ant.friend&&view[c1].ant.food>0)){return {cell:c2,type:AJM};}break;}}}}if (!(LCRQCVAL[mC])){return {cell:POSC,color:LCRQC[1]};} else if ((view[CCW[xn]].color==LPX)&&isSc0(view[CCW[xn+1]].color)){if ((mF<=TH0)||(mF % RM1==RD1)){return (rQHTc());} else if (mF<=TH2){if (dOK[CCW[0]]){return {cell:CCW[0],type:AJM};} else {return (rQHTc());}} else {var destCycle=[2,4,6,4,6,2,6,2,4];var destination=destCycle[mF % 9];if (!dOK[CCW[xn+destination]]){destination=destination % 6+2;}if (!dOK[CCW[xn+destination]]){destination=destination % 6+2;}if (!dOK[CCW[xn+destination]]){return (rQHTc());}if (mF<=TH3){if (xn<=2){return {cell:CCW[xn+destination],type:AJM};} else {return (rQHTc());}} else if (mF<=TH4){if (xn<=2){return {cell:CCW[xn+destination],type:((xn>0) ? AJM : ASM)};} else {return (rQHTc());}} else if (mF<=TH5){if (xn==0){return {cell:CCW[xn+destination],type:ASM};} else {return (rQHTc());}} else {return (rQHTc());}}} else {return {cell:POSC,color:incQc(mC)};}return NOP;}function rQLsSy(){if (mF>=TH1){if (mC!=LCLR){return {cell:POSC,color:LCLR};} else {for (var i=0; i<TN; i+=2){if (view[CCW[i]].color!=LCLR){return {cell:CCW[i],color:LCLR};}}}if ((eT==0)&&(fT==1)){return {cell:CCW[xn+3]};}}if ((eT==0)&&(fT==1)){if (view[CCW[xn+2]].food>0){return {cell:CCW[xn+2]};} else if ((view[CCW[xn+3]].food +view[CCW[xn+4]].food>0)&&(view[CCW[xn+1]].color!=LLSF)){return NOP;} else {return {cell:CCW[xn+2]};}} else if (dOK[CCW[xn+2]]&&(dOK[CCW[xn+3]]||(view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend))){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn]]&&dOK[CCW[xn+7]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else {return NOP;}}function rQLvSy(){if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};}return NOP;}function rQCSy(){return NOP;}function rSOSy(){if (!(LCRSCVAL[mC])){return {cell:POSC,color:LCRSC[1]};} else if (isSc0(mC)&&isQc0(view[CCW[xn+1]].color)&&
(view[CCW[xn+3]].color==LPG)){return {cell:CCW[xn+3],color:LPX};} else {return {cell:POSC,color:incSc(mC)};}return NOP;}function rSLSy(){if ((eT==0)&&(fT==1)){if (view[CCW[xn]].food>0){return {cell:CCW[xn]};} else if (view[CCW[xn+7]].food +view[CCW[xn+6]].food>0){return {cell:POSC,color:LLSF};} else {return {cell:CCW[xn]};}} else if ((eT>0)&&view[CCW[xn+2]].ant&&!view[CCW[xn+2]].ant.friend){return {cell:POSC,color:LLSF};} else {if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};}}return NOP;}function rSESy(){if (view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend&&(view[CCW[xn+5]].ant.type==ASF)){if (dOK[CCW[xn]]){return {cell:CCW[xn]};}} else if ((mC==LRR0)&&(view[CCW[xn+5]].color==LG6)){if (dOK[CCW[xn]]){return {cell:CCW[xn]};}} else {if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn]]){return {cell:CCW[xn]};}}return NOP;}function rGSSy(){if (view[CCW[xn]].color!=LCL_QC_RESET){return {cell:CCW[xn],color:LCL_QC_RESET};}if (mC!=LPB){return {cell:POSC,color:LPB};}return (rGGTc());}function rGOSy(){if (aE[AQ]>0){var c=CCW[xn+2];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+1]].ant){return {cell:CCW[xn+1],color:LA};}c=CCW[xn+3];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+1]].ant){return {cell:CCW[xn+1],color:LA};}}if (!LCRPHR[mC]){if ((mC==LPX)&&isSc0(view[CCW[xn+7]].color)){return NOP;} else if (eT==0){return {cell:POSC,color:LPG};}} else if (isSc1(view[CCW[xn+7]].color)&&isQc2(view[CCW[xn]].color)){switch (mC){case LPG:return {cell:POSC,color:LPG1};case LPG1:return {cell:POSC,color:LPG};default:return {cell:POSC,color:LPG};}}if ((eT>0)&&!LCRPHR[mC]&&(xn&4)){return {cell:POSC,color:LPG};} else {return (rGGTc());}}function rLSSy(){if (mC!=LCLR){return {cell:POSC,color:LCLR};}return NOP;}function rEHyS(){if (mQ==1){var ptrn=PTFRL0H;var msm=patC(ptrn,AIMU,0,1);if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (LCRQCVAL[view[CCW[xn+mQ]].color]&&dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};}}return NOP;}function rEBRSy(){if (aF[ASF]>0){return (rELGTc());} else {return (rEBRTc());}}function rEASy(){return NOP;}function rUHSy(){if ((mQ==0)&&(view[CCW[xn]].ant.food<RD1)&&view[CCW[xn+1]].ant&&view[CCW[xn+1]].ant.friend&&(view[CCW[xn+1]].ant.type==ASF)){var cc=[5,6,7,4,2];for (var i=0; i<cc.length; i++){var c=CCW[xn+cc[i]];if (dOK[c]){return {cell:c};}}return NOP;}if ((eT>0)&&(aE[AQ]+aUE[1]+aUE[2] +aUE[3]+aUE[4]>0)){var common;if (mQ==0){common=[1,7];} else {common=[0,2,3,7];}for (var i=0; i<common.length; i++){var c=CCW[xn+common[i]];if (view[c].ant&&!view[c].ant.friend&&((view[c].ant.type==AQ)||(view[c].ant.food==0))){if ((aE[AQ]==0)&&(mC!=LA)){return {cell:POSC,color:LA};} else {return NOP;}}}}if (mQ==0){if (mC!=LRM0){return {cell:POSC,color:LRM0};} else if (view[CCW[xn+3]].color!=LRR0){return {cell:CCW[xn+3],color:LRR0};} else if (view[CCW[xn+7]].color!=LRL0){return {cell:CCW[xn+7],color:LRL0};} else if (view[CCW[xn+5]].color!=LRM1){return {cell:CCW[xn+5],color:LRM1};} else if (view[CCW[xn+6]].color!=LRL1){return {cell:CCW[xn+6],color:LRL1};} else if ((!LCRGRR1[view[CCW[xn+4]].color])&&!(view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend)){return {cell:CCW[xn+4],color:LRR1};}if (LCRQCVAL[view[CCW[xn]].color]||(view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend&&
(view[CCW[xn+5]].ant.food>0))||(view[CCW[xn+6]].ant&&view[CCW[xn+6]].ant.friend&&
(view[CCW[xn+6]].ant.food>0))){if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};}}} else {var ptrn=PTFRL0H;var msm=patC(ptrn,AIMU,0,1);if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (LCRQCVAL[view[CCW[xn+mQ]].color]){if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};}}}return NOP;}function rUBRSy(){if (spcRL1()){return (rULRL1Sy());} else if (spcRL02()){return (rULRL02Sy());} else if (spcRM()){return (rUFCRTc());}for (var i=TN-1; i>=0; i--){if (view[CCW[i+1]].ant&&view[CCW[i+1]].ant.friend&&
(view[CCW[i+1]].ant.type==AE)&&dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;}function rUTRRSy(){return (rUCRTc());}function rULRL1Sy(){var ptrn=PTGRL1;var msm=patC(ptrn,AIMR,0,1);if (xn>=0){if ((view[CCW[xn+6]].color==LMS_WRP)&&(view[CCW[xn+7]].color==LCLR)&&(view[CCW[xn]].color==LMR0)&&(view[CCW[xn+3]].color!=LRM1_WRP)){return {cell:CCW[xn+3],color:LRM1_WRP};} else if ((view[CCW[xn+3]].color!=LRM1_WRP)&&view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else {return NOP;}} else if (spcRM()){return (rUTRRSy());}return (rLostMSy(false));}function rULRL02Sy(){var ptrn;var msm;if (sL[LRM0]>0){ptrn=PTGRL0;msm=patC(ptrn,AIMR,1,1);}if (xn<0){ptrn=PTGRL2;msm=patC(ptrn,AIMR,1,1);}if (xn>=0){if (view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else {return NOP;}} else if (spcRM()){return (rUTRRSy());}return (rLostMSy(false));}function rULRR0Sy(){var ptrn=PTGRR0;var msm=patC(ptrn,AIML,2,1);if (xn>=0){return (runUMLeaveRRTactic());} else if (spcRM()){return (rUTRRSy());}return (rLostMSy(false));}function rULRR2Sy(){var ptrn=PTGRR2;var msm=patC(ptrn,AIML,2,1);if (xn>=0){return (runUMLeaveRRTactic());}    return (rLostMSy(false));}function rUPSSy(){var ptrn=PTGRR1;var msm=patC(ptrn,AIMD,3,2);if (xn>=0){var c=CCW[xn+1];if (view[c].ant&&view[c].ant.friend&&(view[c].ant.food>0)){if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else {return NOP;}}if ((view[CCW[xn+2]].color==LMX_M3OUT)&&(view[CCW[xn]].color==LMR0)&&(view[CCW[xn+1]].color==LCLR)&&(mC!=LRR1X)){return {cell:POSC,color:LRR1X};} else if ((mT==AJM)&&(mC==LRR1U)&&(view[CCW[xn]].color==LMR0)&&(view[CCW[xn+1]].color==LCLR)&&LCRMX_OUT[view[CCW[xn+2]].color]){return {cell:POSC,color:LRR1V};} else if ((mC==LRR1X)||((mT==AJM)&&(mC==LRR1V))||((mC==LRR1U)&&(view[CCW[xn]].color==LMR0)&&(view[CCW[xn+1]].color==LCLR)&&LCRMX_IN[view[CCW[xn+2]].color])){if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}} else if (mC!=LRR1U){return {cell:POSC,color:LRR1U};} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {if (dOK[c]){return {cell:c};} else if (view[c].ant&&view[c].ant.friend){if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}} else {return NOP;}}}return (rLostMSy(false));}function rUESSy(){var ptrn=PTMS0R_IN;var msm=patC(ptrn,AIMD,4,2);if (xn>=0){return (rUESTc(ptrn,msm));}return (rLostMSy(false));}function rUDSSy(){var ptrn;var msm;if ((sL[LML3]>=1)&&(sD[LMR2]+sD[LMR0]>=1)){ptrn=PTMS3;msm=patC(ptrn,AIMD,3,2);}if ((xn<0)&&(sL[LMR2]>=1)&&(sD[LML1]+sD[LML3]>=1)){ptrn=PTMS2;msm=patC(ptrn,AIMD,3,2);}if (xn>=0){if ((msm<0)&&(view[CCW[xn]].color==LRM0)&&(view[CCW[xn+1]].color==LRR0)&&(view[CCW[xn+2]].color==LMR0)){if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else {return NOP;}}return (rUDSTc(ptrn,msm));}if ((xn<0)&&(sL[LML1]>=1)&&(sD[LMR0]+sD[LMR2]>=1)){ptrn=PTMS1_IN;msm=patC(ptrn,AIMD,3,4);if (xn<0){ptrn=PTMS1;msm=patC(ptrn,AIMD,3,2);}}if (xn>=0){return (rUDSTc(ptrn,msm));}if ((sD[LML3]+sL[LMR0]>=2)&&(sD[LRL0] >=2)&&(sD[LML1]==0)){ptrn=PTMS0_WRAPPING;msm=patC(ptrn,AIMD,0,1);if (xn>=0){return (rUWRTc(ptrn,msm));}}if ((sL[LMR0]>=1)&&(sD[LML3]+sD[LML1]>=1)){ptrn=PTMS0;msm=patC(ptrn,AIMD,3,2);if (xn>=0){return (rUDSTc(ptrn,msm));}}if (spcRR1()){ptrn=PTGRR1;msm=patC(ptrn,AIMD,3,2);if (xn>=0){if (mC==LRR1){return {cell:POSC,color:LRR1U};}return NOP;}}if (spcMS0R()){ptrn=PTMS0R_IN;msm=patC(ptrn,AIMD,4,2);if (xn>=0){return (rUESTc(ptrn,msm));}}return (rLostMSy(false));}function rUSWSy(){var ptrn=PTMS0_WRAPPING;var msm=patC(ptrn,AIMD,0,1);if (xn>=0){return (rUWRTc(ptrn,msm));}return (rLostMSy(false));}function rURHSy(){return (rMNGTc());}function rUCRSy(){for (var i=TN; i>=1; i--){if (view[CCW[i]].ant&&dOK[CCW[i-1]]){return {cell:CCW[i-1]};}}return NOP;}function rLLLWSy(){var ptrn;var msm;if (mC==LML1){ptrn=PTMS1FL;msm=patC(ptrn,AIML,0,1);if (xn>=0){return (rLLLWTc());}} else if (mC==LML3){ptrn=PTMS3FL;msm=patC(ptrn,AIML,0,1);if (xn>=0){return (rLLLWTc());}} else if (sL[LML1]+sL[LML3]>=2){ptrn=PTMS0FL;msm=patC(ptrn,AIML,0,1);if (xn<0){ptrn=PTMS2FL;msm=patC(ptrn,AIML,0,1);}if (xn>=0){return (rLLLWTc());}} else if (spcMFR()){return (rLLRWSy());} else if (spcMS()){return (rLASSy());}return (rLostMSy(false));}function rLLRWSy(){var ptrn;var msm;if (mC==LMR0){ptrn=PTMS0FR;msm=patC(ptrn,AIMR,0,1);if (xn>=0){return (rLLRWTc());}} else if (mC==LMR2){ptrn=PTMS2FR;msm=patC(ptrn,AIMR,0,1);if (xn>=0){return (rLLRWTc());}} else if (sL[LMR0]+sL[LMR2]>=2){ptrn=PTMS1FR;msm=patC(ptrn,AIMR,0,1);if (xn<0){ptrn=PTMS3FR;msm=patC(ptrn,AIMR,0,1);}if (xn>=0){return (rLLRWTc());}} else if (spcMS()){return (rLASSy());}return (rLostMSy(false));}function rLASSy(){var ptrn;var msm;if ((sL[LML3]>=1)&&(sD[LMR2]+sD[LMR0]>=1)){ptrn=PTMS3;msm=patC(ptrn,AIMU,3,2);}if ((xn<0)&&(sL[LMR2]>=1)&&(sD[LML1]+sD[LML3]>=1)){ptrn=PTMS2;msm=patC(ptrn,AIMU,3,2);}if ((xn<0)&&(sL[LML1]>=1)&&(sD[LMR0]+sD[LMR2]>=1)){ptrn=PTMS1_IO;msm=patC(ptrn,AIMU,0,1);if (xn<0){ptrn=PTMS1;msm=patC(ptrn,AIMU,3,2);}}if (xn>=0){return (rLASTc(ptrn,msm));}if ((sD[LML3]+sL[LMR0]>=2)&&(sD[LRL0] >=2)&&(sD[LML1]==0)){ptrn=PTMS0_OUT;msm=patC(ptrn,AIMU,0,1);if (xn>=0){return {cell:CCW[xn+3],color:LCLR};}ptrn=PTMS0_WRAPPING;msm=patC(ptrn,AIMD,0,1);if (xn>=0){return (rLWRTc(ptrn,msm));}}if ((sL[LMR0]>=1)&&(sD[LML3]+sD[LML1]>=1)){ptrn=PTMS0;msm=patC(ptrn,AIMU,3,2);if (xn>=0){return (rLASTc(ptrn,msm));}}if (spcRM()){return (rLTRRSy());}return (rLostMSy(false));}function rLLSSy(){var ptrn=PTMS0R_OUT;var msm=patC(ptrn,AIMU,0,1);if (xn>=0){} else {ptrn=PTMS0R_IN;msm=patC(ptrn,AIMU,4,2);if (xn>=0){ptrn=PTMS0R_OUT;msm=patC(ptrn,AIMU,0,1);}}if (xn>=0){return (rLLSTc(ptrn,msm));} else if (spcMS()){return (rLASSy());} else {return (rLostMSy(false));}return NOP;}function rLLVSSy(){var ptrn=PTMS0R_OUT1;var msm=patC(ptrn,AIMU,0,1);if (xn>=0){if (view[CCW[xn+3]].color==LCLR){return {cell:CCW[xn+3],color:((mT==ASM) ? LMX_M2OUT : LMX_M1OUT)};
} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend&&
dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}} else if (spcMS()){return (rLASSy());}return (rLostMSy(false));}function rLDSSy(){var ptrn=PTGRR1;var msm=patC(ptrn,AIMU,1,1);if (xn>=0){if ((view[CCW[xn]].color==LMR0)&&(view[CCW[xn+1]].color==LCLR)){if ((mC==LRR1X)&&(view[CCW[xn+2]].color!=LMX_M3OUT)){return {cell:CCW[xn+2],color:LMX_M3OUT};}if ((view[CCW[xn+2]].color==LMX_M3OUT)&&(mC!=LRR1X)){return {cell:POSC,color:LRR1X};} else if ((LCRMX_OUT[view[CCW[xn+2]].color])&&
(mC!=LRR1V)){return {cell:POSC,color:LRR1V};} else if ((LCRMX_IN[view[CCW[xn+2]].color])&&(mC!=LRR1U)){return {cell:POSC,color:LRR1U};} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}}if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}}return (rLostMSy(false));}function rLTRRSy(){return (rLCRTc());}function rLFRSy(){for (var i=1; i<TN; i+=2){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==AE)&&dOK[CCW[i+2]]){return {cell:CCW[i+2]};}}return NOP;}function rLRHSy(){var ptrn=PTFRL1G;var msm=patC(ptrn,AIMR,0,1);if (xn>=0){return (rLRLTc());}return (rMNGTc());}function rLLRL1Sy(){var ptrn=PTGRL1;var msm=patC(ptrn,AIMR,0,1);if (xn>=0){return (rLRLTc());} else if (spcRM()){return (rLTRRSy());}return (rLostMSy(false));}function rLLRL02Sy(){var ptrn;var msm;if (sL[LRM0]>0){ptrn=PTGRL0;msm=patC(ptrn,AIMR,1,1);}if (xn<0){ptrn=PTGRL2;msm=patC(ptrn,AIMR,1,1);}if (xn>=0){return (rLRLTc());}return (rLostMSy(false));}function rLLRR0Sy(){var ptrn=PTGRR0;var msm=patC(ptrn,AIML,2,1);if (xn>=0){return (rLRRTc());} else if (spcRM()){return (rLTRRSy());}return (rLostMSy(false));}function rLLRR2Sy(){var ptrn=PTGRR2;var msm=patC(ptrn,AIML,2,1);if (xn>=0){return (rLRRTc());} else if (spcRM()){return (rLTRRSy());}return (rLostMSy(false));}function rLCRSy(){for (var i=TN; i>=1; i--){if (!dOK[CCW[i]]&&dOK[CCW[i-1]]){return {cell:CCW[i-1]};}}return NOP;}function rM2R1Sy(){for (var i=0; i<TN; i++){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==ASF)&&view[CCW[i+1]].ant&&view[CCW[i+1]].ant.friend&&
(view[CCW[i+1]].ant.type==ASF)){if (i&1){} else {}if (dOK[CCW[i+7]]){return {cell:CCW[i+7]};} else {return NOP;}}}return (rLostMSy(true));}function rLostMSy(totally){if ((fdT>0)&&(mF==0)){for (var i=0; i<TN; i++){if ((view[CCW[i]].food>0)&&dOK[CCW[i]]){return {cell:CCW[i]};}}}if (totally&(fT==0)){if (((mC==PY)&&(sN[PY]==0))||((mC==PR)&&(sN[PR]==0))){return {cell:POSC,color:PP};}if (((mC==PG)&&(sN[PG]==0))||((mC==PC)&&(sN[PC]==0))||((mC==PB)&&(sN[PB]==0))||((mC==PP)&&(sN[PP]==0))){return {cell:POSC,color:PW};} else if ((sT[PG]==0)&&(((mC==PK)&&(sN[PK]==0))||((mC==PY)&&(sN[PY]==0))||((mC==PR)&&(sN[PR]==0)))){return {cell:POSC,color:PW};} else if ((mC!=PW)&&(sN[mC]>=4)){return {cell:POSC,color:PW};}}if ((mC==PG)&&(sL[PG]>=2)){return {cell:POSC,color:PW};} else if (((mC==PK)||(mC==PR))&&(sN[PK]+sN[PR]>=3)){return {cell:POSC,color:PW};}if (sN[PW]<=4){var preferredColors =[PG,PB,PC,PP,PY,PR,PK];for (var ci=0; ci<preferredColors.length; ci++){var c=preferredColors[ci];if (mC==c){break;}if (sN[c]>0){for (var i=1; i<TN; i++){if ((view[CCW[i]].color==c)&&dOK[CCW[i]]){return {cell:CCW[i]};}}}}}if (RW){for (var i=1; i<TN; i+=2){if (dOK[CCW[i]]){return {cell:CCW[i]};}}for (i=0; i<TN; i+=2){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;} else {return NOP;}}function rDHSy(){if (mQ==0){var c=CCW[xn+2];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+1]].ant){return {cell:CCW[xn+1],color:LA};}c=CCW[xn+6];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+7]].ant){return {cell:CCW[xn+7],color:LA};}} else {var c=CCW[xn+4];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+3]].ant){return {cell:CCW[xn+3],color:LA};}c=CCW[xn+6];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)&&!view[CCW[xn+7]].ant){return {cell:CCW[xn+7],color:LA};}c=CCW[xn+5];if (view[c].ant&&!view[c].ant.friend&&(view[c].ant.type==AQ)&&(view[c].ant.food>0)){if (!view[CCW[xn+3]].ant){return {cell:CCW[xn+3],color:LA};} else if (!view[CCW[xn+7]].ant){return {cell:CCW[xn+7],color:LA};}}}return NOP;}function rQSETc(){if (mC!=LT){return {cell:POSC,color:LT};}for (var i=0; i<TN; i++){if (view[CCW[i]].food>0){return {cell:CCW[i]};}}return NOP;}function rQSSTc(){for (var i=0; i<TN; i++){if ((view[CCW[i]].food>0)&&(dOK[CCW[i]])){return {cell:CCW[i]};}}return NOP;}function rQSTCTc(){if ((mC!=LCLR)&&(sN[mC]>=4)){if (sN[LT]==0){return {cell:POSC,color:LT};} else if (sN[LT]>=3){return {cell:POSC,color:LT};} else {for (var i=0; i<TN; i++){if ((view[CCW[i]].color==LT)&&(view[CCW[i+2]].color!=LT)){return {cell:CCW[i+2],color:LT};}}return NOP;}} else if (sN[LT]==1){for (var i=0; i<TN; i++){if ((view[CCW[i]].color==LT)&&(view[CCW[i+4]].color!=LCLR)){if (view[CCW[i+1]].color==LCLR){return { cell:CCW[i+1]};} else if (view[CCW[i+7]].color==LCLR){return { cell:CCW[i+7]};} else {return {cell:POSC,color:LT};}}}return {cell:POSC,color:LT};} else {return {cell:POSC,color:LT};}return NOP;}function rQSATc(){for (var i=0; i<TN; i++){if ((view[CCW[i]].color==LCLR)&&(view[CCW[i+1]].color==LCLR)&&(view[CCW[i+2]].color==LCLR)){if ((view[CCW[i+3]].color==LCLR)&&(view[CCW[i+4]].color==LCLR)){return {cell:CCW[i+2]};}return {cell:CCW[i+1]};}}for (i=TN-1; i>=0; i--){if (view[CCW[i]].color!=LT){return {cell:CCW[i]};}}for (i=0; i<TN; i++){if (view[CCW[i]].color!=LT){return {cell:CCW[i],color:LCLR};}}return {cell:0,color:LCLR};}function rQSEvTc(){if (sN[LT]>0){for (var i=0; i<TN; i++){if (view[CCW[i]].color==LT){xn=i&6;}}if ( dOK[CCW[xn+7]]&&dOK[CCW[xn]]&&dOK[CCW[xn+1]]&&dOK[CCW[xn+2]]&&dOK[CCW[xn+3]] ){return {cell:CCW[xn+1]};} else if (dOK[CCW[xn+5]]&&dOK[CCW[xn+6]]&&dOK[CCW[xn+7]]&&dOK[CCW[xn]]&&dOK[CCW[xn+1]]){return {cell:CCW[xn+7]};} else if (dOK[CCW[xn+3]]&&dOK[CCW[xn+4]]&&dOK[CCW[xn+5]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+5]]&&dOK[CCW[xn+6]]&&dOK[CCW[xn+7]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+1]]&&dOK[CCW[xn+2]]&&dOK[CCW[xn+3]]){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn+7]]&&dOK[CCW[xn]]&&dOK[CCW[xn+1]]){return {cell:CCW[xn]};} else {for (i=0; i<TN; i++){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;}} else {for (var i=0; i<TN; i++){if (dOK[CCW[i]]&&dOK[CCW[i+1]]&&dOK[CCW[i+2]]&&dOK[CCW[i+3]]&&dOK[CCW[i+4]]){return {cell:CCW[i+2]};}}for (i=0; i<TN; i++){if (dOK[CCW[i]]&&dOK[CCW[i+1]]&&dOK[CCW[i+2]]){return {cell:CCW[i+1]};}}for (i=0; i<TN; i++){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;}return NOP;}function rQHTc(){var ptrn=PTHOME;var msm=patC(ptrn,POSC,0,1);if (msm!=0){var cc=fwdWrong[0];return {cell:cc.v,color:ptrn[cc.p]};} else {return NOP;}}function rGGTc(){var ptrn=PTGARDEN;var msm=patC(ptrn,POSC,0,1);if (msm!=0){var cc=fwdWrong[0];return {cell:cc.v,color:ptrn[cc.p]};} else {return NOP;}}function rUFCRTc(){var ptrn;var msm;if (mC==LRM0){ptrn=PTFRM0;msm=patC(ptrn,AIMU,4,1);if ((xn<0)&&(eT>0)){ptrn=PTFRM1;msm=patC(ptrn,AIMU,4,1);}if ((xn<0)&&(eT>0)){ptrn=PTFRM2;msm=patC(ptrn,AIMU,4,1);}} else if (mC==LRM2){ptrn=PTFRM2;msm=patC(ptrn,AIMU,4,1);if ((xn<0)&&(eT>0)){ptrn=PTFRM0;msm=patC(ptrn,AIMU,4,1);}if ((xn<0)&&(eT>0)){ptrn=PTFRM1;msm=patC(ptrn,AIMU,4,1);}} else if (mC==LRM1){ptrn=PTFRM1;msm=patC(ptrn,AIMU,4,1);if ((xn<0)&&(eT>0)){ptrn=PTFRM2;msm=patC(ptrn,AIMU,4,1);}if ((xn<0)&&(eT>0)){ptrn=PTFRM0;msm=patC(ptrn,AIMU,4,1);}} else if (mC==LRM1_WRP){ptrn=PTGRM1_WRP;msm=patC(ptrn,AIMR,1,1);}if ((xn<0)&&spcRR1()){return (rUPSSy());}if (xn<0){return (rLostMSy(false));}if (msm==0){if (fdL>0){if ((view[CCW[xn+7]].food>0)&&dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else if ((view[CCW[xn+3]].food>0)&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};}}if ((mC==LRM1)&&(view[CCW[xn+3]].color!=LRR1X)&&(view[CCW[xn+3]].color!=LRR1U)&&!((mT==AJM)&&(view[CCW[xn+3]].color==LRR1V))&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};}if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};}return NOP;}for (var i=0; i<TN; i++){var ce=CCW[xn+i];if (view[ce].ant&&view[ce].ant.friend&&(view[ce].ant.type==AE)){if ((2<=i)&&(i<=4)){var msmSaved=msm;var fwdWrongSaved=Array.from(fwdWrong);var rearWrongSaved=Array.from(rearWrong);xn=xn % 4+4;msm=patC(ptrn,POSC,0,1);if (msm<msmSaved){xn=xn % 4+4;msm=msmSaved;fwdWrong=fwdWrongSaved;rearWrong=rearWrongSaved;} else {}break;}}}if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}return NOP;}function rUCRTc(){var ptrn;var msm;if (mC==LRM0){ptrn=PTGRM0;msm=patC(ptrn,AIMU,4,2);if ((xn<0)&&spcRR1()){return (rUPSSy());}if ((xn<0)&&(eT>0)){ptrn=PTGRM1;msm=patC(ptrn,AIMU,4,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM2;msm=patC(ptrn,AIMU,4,2);}} else if (mC==LRM2){ptrn=PTGRM2;msm=patC(ptrn,AIMU,4,2);if ((xn<0)&&(eT>0)){ptrn=PTGRM0;msm=patC(ptrn,AIMU,4,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM1;msm=patC(ptrn,AIMU,4,2);}} else if (mC==LRM1){ptrn=PTGRM1;msm=patC(ptrn,AIMU,4,2);if ((xn<0)&&(eT>0)){ptrn=PTGRM2;msm=patC(ptrn,AIMU,4,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM0;msm=patC(ptrn,AIMU,4,2);}} else if (mC==LRM1_WRP){ptrn=PTGRM1_WRP;msm=patC(ptrn,AIMR,1,1);}if ((xn<0)&&spcRR1()){return (rUPSSy());}if (xn<0){return (rLostMSy(true));}if (msm==0){if (fdL>0){if ((view[CCW[xn+7]].food>0)&&dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else if ((view[CCW[xn+3]].food>0)&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};}}if ((mC==LRM1)&&(view[CCW[xn+3]].color!=LRR1X)&&(view[CCW[xn+3]].color!=LRR1U)){if ((((mT==AJM)&&(view[CCW[xn+3]].color==LRR1))||((mT==ASM)&&(view[CCW[xn+3]].color==LRR1V)))&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};}}if (mC==LRM0&&(view[CCW[xn+5]].color==LRM1_WRP)&&!(view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend)){return {cell:CCW[xn+5],color:LRM1};}var c=CCW[xn+5];if (dOK[c]){return {cell:c};} else if (view[c].ant&&view[c].ant.friend){var evade=false;if (view[c].ant.food>0){evade=true;} else if (view[CCW[xn+1]].ant&&view[CCW[xn+1]].ant.friend&&(view[CCW[xn+1]].ant.food==0)){evade=true;}if (evade){if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else {return NOP;}} else {return NOP;}}} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}return NOP;}function rUESTc(ptrn,msm){switch (view[CCW[xn+3]].color){case LMX_M0:return {cell:CCW[xn+3],color:LMX_M1IN};case LMX_M1OUT:if (mT==ASM){return {cell:CCW[xn+3],color:LMX_M2IN};}break;case LMX_M2OUT:if (mT==ASM){return {cell:CCW[xn+3],color:LMX_M3IN};}break;case LMX_M3OUT:break;case LMX_M1IN:case LMX_M2IN:case LMX_M3IN:if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if ((msm==0)&&dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else {break;}default:break;}if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};}return NOP;}function runUMLeaveRRTactic(){if (view[CCW[xn+7]].ant&&view[CCW[xn+7]].ant.friend&&dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else {return NOP;}}function rUDSTc(ptrn,msm){var c=CCW[xn+1];if ((msm==0)&&(fdL>0)&&(view[CCW[xn+3]].food+view[CCW[xn+7]].food>0)){if (mC!=LMMF){return {cell:POSC,color:LMMF};} else if (view[CCW[xn+5]].color!=LMMH){return {cell:CCW[xn+5],color:LMMH};} else if ((view[CCW[xn+3]].food>0)&&dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if ((view[CCW[xn+7]].food>0)&&dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};}} else if ((msm<0)&&!(view[c].ant&&view[c].ant.friend)){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (msm>=0){if (dOK[c]){return {cell:c};} else {if (view[c].ant&&view[c].ant.friend){if (view[c].ant.food>0){if ((view[CCW[xn]].color==LCLR)&&dOK[CCW[xn]]){return {cell:CCW[xn]};} else if ((view[CCW[xn+2]].color==LCLR)&&dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else {return NOP;}} else {var c=CCW[xn+5];if (view[c].ant&&view[c].ant.friend&&(view[c].ant.food==0)){if (view[c].color==LMMH){if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else {return NOP;}} else if (mC!=LMMH){return {cell:POSC,color:LMMH};} else {return NOP;}} else {return NOP;}}} else {return NOP;}}}return NOP;}function rUWRTc(ptrn,msm){if (view[CCW[xn+3]].color!=LMS_WRP){return {cell:CCW[xn+3],color:LMS_WRP};} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};}return NOP;}function rLLLWTc(){if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else {return NOP;}}function rLLRWTc(){if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else {return NOP;}}function rLWRTc(ptrn,msm){if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else {return NOP;}}function rLASTc(ptrn,msm){var c=CCW[xn+5];if ((msm<0)&&!(view[c].ant&&view[c].ant.friend)){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (mC==LMMF){return {cell:POSC,color:LCLR};} else if (view[CCW[xn+5]].color==LMMH){return {cell:CCW[xn+5],color:LCLR};} else if (msm>=0){if (dOK[c]){return {cell:c};} else if ((view[c].food>0)&&(eT==0)){if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};}}}return NOP;}function rLLSTc(ptrn,msm){if (msm<0){if (view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend){return NOP;}var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}switch (view[CCW[xn+3]].color){case LMX_M1IN:return {cell:CCW[xn+3],color:LMX_M1OUT};case LMX_M2IN:return {cell:CCW[xn+3],color:LMX_M2OUT};case LMX_M3IN:default:return {cell:CCW[xn+3],color:LMX_M3OUT};case LMX_M1OUT:case LMX_M2OUT:case LMX_M3OUT:if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};}break;}return NOP;}function rLCRTc(){var ptrn;var msm;var trust=(aF[AE]>0 ? 1 : 0);if (mC==LRM0){ptrn=PTGRM0;msm=patC(ptrn,AIMD,3,2-trust);if ((xn<0)&&(eT>0)){ptrn=PTGRM1;msm=patC(ptrn,AIMD,3,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM2B;msm=patC(ptrn,AIMD,3,2);}} else if (mC==LRM2){ptrn=PTGRM2B;msm=patC(ptrn,AIMD,3,2-trust);if ((xn<0)&&(eT>0)){ptrn=PTGRM0;msm=patC(ptrn,AIMD,3,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM1;msm=patC(ptrn,AIMD,3,2);}} else if (mC==LRM1){ptrn=PTGRM1;msm=patC(ptrn,AIMD,3,2-trust);if ((xn<0)&&(eT>0)){ptrn=PTGRM2B;msm=patC(ptrn,AIMD,3,2);}if ((xn<0)&&(eT>0)){ptrn=PTGRM0;msm=patC(ptrn,AIMD,3,2);}} else if (mC==LRM1_WRP){ptrn=PTGRM1;msm=patC(ptrn,AIMD,3,2);if (xn>=0){if (view[CCW[xn+3]].color!=LRR1X){return {cell:CCW[xn+3],color:LRR1X};} else if (!(view[CCW[xn+7]].ant&&view[CCW[xn+7]].ant.friend)){return {cell:POSC,color:LRM1};}}}if (xn<0){if (spcRR1()){return (rLDSSy());}return (rLostMSy(true));}if (msm==0){var c=CCW[xn+1];if (dOK[c]){return {cell:c};} else if (view[c].ant&&view[c].ant.friend){var evade=false;if (view[c].ant.food==0){evade=true;} else if (view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend&&(view[CCW[xn+5]].ant.food>0)){evade=true;}if (evade){if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else {return NOP;}} else {return NOP;}}} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}return NOP;}function rLRLTc(){if (view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend&&
dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else {return NOP;}}function rLRRTc(){if (view[CCW[xn+7]].ant&&view[CCW[xn+7]].ant.friend&&dOK[CCW[xn]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else {return NOP;}}function rMNGTc(){for (var i=0; i<TN; i++){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==ASF)){if (view[CCW[i]].color==LCLR){if (i&1){if ((view[CCW[i+3]].color==LG5)&&dOK[CCW[i+3]]){return {cell:CCW[i+3]};}} else {if ((view[CCW[i+4]].color==LG6)&&dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else if ((view[CCW[i+3]].color==LG5)&&dOK[CCW[i+3]]){return {cell:CCW[i+3]};}}return (rLostMSy(true));} else if (dOK[CCW[i+1]]){return {cell:CCW[i+1]};}}}return NOP;}function rELGTc(){var ptrn=PTFRL1G;var msm;for (var i=0; i<TN; i+=2){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==ASF)){xn=i;break;}}msm=patC(ptrn,AIMU,0,1);if (xn<0){return NOP;} else if ((msm==0)&&dOK[CCW[xn+5]]&&((view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend)||
(view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend))){return {cell:CCW[xn+5]};} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (msm>0){var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {return NOP;}return NOP;}function rEBRTc(){var ptrn;var msm;if (mC==LRL0){for (var i=0; i<TN; i+=2){var c=CCW[i];if ((view[c].color==LRM0)&&view[c].ant&&view[c].ant.friend&&((view[CCW[i]].ant.type==AJM)||(view[CCW[i]].ant.type==ASM))){ptrn=PTFRL2;msm=patC(ptrn,AIMU,1,1);if (xn>=0){break;}}}if (xn<0){ptrn=PTFRL0;msm=patC(ptrn,AIMU,1,1);}if (xn<0){ptrn=PTFRL2;msm=patC(ptrn,AIMU,1,1);}if ((xn<0)&&(eT>0)){ptrn=PTFRL1;msm=patC(ptrn,AIMU,1,1);}if (xn<0){return rECLRETc();}} else if (mC==LRL1){ptrn=PTFRL1;msm=patC(ptrn,AIMU,1,1);if ((xn<0)&&(eT>0)){ptrn=PTFRL2;msm=patC(ptrn,AIMU,1,1);}if ((xn<0)&&(eT>0)){ptrn=PTFRL0;msm=patC(ptrn,AIMU,1,1);}if (xn<0){return rECLRETc();}} else if ((mC==LRR2)&&(sL[LRL1]>=1)&&(sL[LRL0]==0)){return {cell:POSC,color:LRL0};}if ((msm==0)&&dOK[CCW[xn+5]]&&((view[CCW[xn+3]].ant&&view[CCW[xn+3]].ant.friend)||
(view[CCW[xn+4]].ant&&view[CCW[xn+4]].ant.friend))){return {cell:CCW[xn+5]};} else if (msm<0){var cc=fwdWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else if (msm>0){var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};} else {return NOP;}return NOP;}function rECLRETc(){var ptrn;var msm;for (var i=3; i<TN+2; i+=2){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
((view[CCW[i]].ant.type==AJM)||(view[CCW[i]].ant.type==ASM))){xn=i-3;if (mC==LRL0){ptrn=PTFRL0;msm=patC(ptrn,AIMR,1,0.3);if (msm==PTNOM){ptrn=PTFRL2;msm=patC(ptrn,AIMR,1,0.3);}} else if (mC==LRL1){ptrn=PTFRL1;msm=patC(ptrn,AIMR,1,0.3);}if (msm>0){var cc=rearWrong[0];return {cell:cc.v,color:fixup(ptrn[cc.p])};}return NOP;}}return NOP;}function patC(ptrn,targetCell,qG,wt){if (xn>=0){return (patCO(ptrn,targetCell,qG,wt,xn));} else {var msm;for (var o=0; o<TN; o+=2){msm=patCO(ptrn,targetCell,qG,wt,o);if (xn>=0){return msm;}}return PTNOM;}}function patCO(ptrn,targetCell,qG,wt,ortn){var fwdFCs=FWD_CELLS[targetCell];var totDscs=0;fwdWrong=[];rearWrong=[];if ((Array.isArray(ptrn[POSC])&&!ptrn[POSC][mC])||
((ptrn[POSC]>0)&&(mC!=ptrn[POSC]))){if (fwdFCs[POSC]){fwdWrong.push({p:POSC,v:POSC});totDscs+=1;} else {rearWrong.push({p:POSC,v:POSC});totDscs+=wt;}}if ((xn<0)&&(totDscs>qG)){return PTNOM;}var jFrom=0;switch (targetCell){case AIMU:jFrom=4;break;case AIML:jFrom=6;break;case AIMR:jFrom=2;break;case AIMD:case POSC:default:break;}for (var j=jFrom; j<TN+jFrom; j++){var posP=CCW[j];var posV=CCW[ortn+j];var c=view[posV].color;if ((Array.isArray(ptrn[posP])&&!ptrn[posP][c])||
((ptrn[posP]>0)&&(c!=ptrn[posP]))){if (fwdFCs[posP]){fwdWrong.push({p:posP,v:posV});totDscs+=1;} else {rearWrong.push({p:posP,v:posV});totDscs+=wt;}}if ((xn<0)&&(totDscs>qG)){return PTNOM;}}if ((xn<0)){xn=ortn;}if (fwdWrong.length==0){return (totDscs);} else {return (-totDscs);}}function isQc0(color){return (LCRQCVAL[color]&&(LCRQC_VALUE[color]==0));
}function isQc2(color){return (LCRQCVAL[color]&&(LCRQC_VALUE[color]==2));
}function incQc(color){if (LCRQCVAL[color]){if (LCRQC_VALUE[color]>=QCPERD){return LCRQC[0];} else {return (LCRQC[(LCRQC_VALUE[color]+1) % QCPERD]);
}} else {return undefined;}}function isSc0(color){return (LCRSCVAL[color]&&(LCRSC_VALUE[color]==0));
}function isSc1(color){return (LCRSCVAL[color]&&(LCRSC_VALUE[color]==1));
}function incSc(color){if (LCRSCVAL[color]){return (LCRSC[(LCRSC_VALUE[color]+1) % SCPERD]);
} else {return undefined;}}function spcMS(){return (((mC==LCLR)||((mF+fdL>0)&&(mC==LMMF))||((mF>0)&&(mC==LMMH)))&&(sN[LMR0]+sN[LML1] +sN[LMR2]+sN[LML3]>=2)&&(sN[LCLR]>=3)&&(sN[LMMF] +sN[LMMH] +sN[PB]<=3)); }function spcRM(){return (LCRGRM_ALL[mC]&&(sT[LRL0]+sT[LRL1]>=3)&&(sN[LRL0]>=1)&&(sN[LRR0]+sN[LRR2]>=2)&&(sT[LRM0]+sT[LRM1_WRP] +sT[LRM1]+sT[LRM2]>=2)&&(sT[LCLR]<=4));}function spcRL1(){return ((mC==LRL1)&&(sL[LRL0]>=2)&&(sD[LRM0]>=1)&&(sL[LRM1_WRP]+sL[LRM1]>=1)&&(sD[LRM2]>=1));}function spcRL02(){return ((mC==LRL0)&&(sL[LRL1]+sL[LRL2]>=2)&&(sN[LRM0]>=1)&&(sD[LRM1_WRP]+sD[LRM1]>=1));}function spcRR0(){return ((mC==LRR0)&&(sL[LRM1]==0)&&(sD[LRM1]+sD[LRM1_WRP]>=1)&&(sL[LMR0]>=1)&&(sL[LRR2]>=1));}function spcRR1(){return (LCRGRR1[mC]&&(sN[LRR0]>=2)&&(sL[LRR2]>=1)&&(sD[LRM0]>=1)&&(sN[LCLR]<=3)&&(sL[LRM1] +sL[LRM1_WRP]>=1));}function spcRR2(){return ((mC==LRR2)&&(sD[LCLR]>=1)&&(sD[LRM0]>=1)&&(sD[LRM1]+sD[LMR0]>=2)&&(sL[LRR0]>=2));}function spcMS0R(){return((mC==LCLR)&&(sL[LMR0]>=1)&&(sL[LRR1U]>=1)&&(sD[LRR2]>=1)&&(sD[LRR0]>=1));}function spcMS0ROut(){return ((mC==LCLR)&&(sL[LMR0]+sL[LRR1V]>=2)&&(sD[LRR2]>=2)&&(sD[LRR0]>=1));}function spcMS0W(){return ((mC==LCLR)&&(sD[LRL0]>=3)&&(sL[LRL1]>=1)&&(sL[LMS_WRP]>=2)&&(sN[LCLR]>=2));}function spcMFL(){return ((sL[LMMF]>=1)&&(sD[LMMH]>=1)&&(sT[LCLR]>=2)&&(sT[LML1]+sT[LML3]>=1));}function spcMFR(){return ((sL[LMMF]>=1)&&(sD[LMMH]>=1)&&(sT[LCLR]>=2)&&(sT[LMR0]+sT[LMR2]>=1));}function fixup(ptrnCell){if (Array.isArray(ptrnCell)){for (var i=1; i<=9; i++){if (ptrnCell[i]){return i;}}return LCLR;} else {return ptrnCell;}}

(Pada trichoplax dan saran dzaima ini - yang banyak terima kasih - ini telah uglified agar sesuai dalam PPCG batas ukuran, dengan mengorbankan pembacaan!. Edit: The lengkap asli, berlimpah berkomentar dan dengan bermakna nama variabel dan fungsi, sekarang tersedia di GitHub .)

Setelah perebutan awal yang biasa, Ratu Kincir Angin meluncurkan tiga rel, bertujuan untuk memiliki area yang lebih dekat untuk menjelajahi lebih menyeluruh dan mengurangi waktu perjalanan kereta api, dan menambahkan redundansi. Ada taman kecil di sisi keempat, dengan beri merah, biru, dan hitam.

Kami membuang Miner di Rail's Repairer. (Rel yang sangat panjang adalah berkah campuran ... cenderung menarik vampir.) Sebaliknya, kami memiliki seorang Insinyur di setiap rail. Tujuan awal mereka adalah untuk memberi tahu para penambang apakah mereka memperpanjang rel (yaitu ketika mereka bisa melihat insinyur) versus memperbaikinya (ketika mereka tidak bisa), tetapi ini agak terkubur ketika kode berevolusi. Mereka sekarang menangani beberapa tugas kecil, seperti membantu mencegah rel tumbuh kelanjutan terbalik.

Poros sel 3 sel lebar 1000 sel dalam diperkirakan mengandung 3 makanan rata-rata, dan hanya 4% dari semua poros kedalaman ini tidak akan mengandung apapun. Setelah kami memperkirakan dari jumlah makanan yang ditimbun bahwa rel telah tumbuh cukup lama sehingga layak, ratu akan mulai menelurkan penambang senior yang akan memeriksa kembali poros yang sebelumnya dieksplorasi oleh penambang junior. Seperti di Miners on a Rail, kami siap menangani poros yang membungkus arena ke sisi belakang (kiri) rel.

Ratu Kincir Angin juga mempekerjakan dua staf lain: A Gardener, dan seorang Sekretaris. Mereka adalah jenis semut yang sama, melakukan satu hal atau yang lain tergantung pada lokasi mereka relatif terhadap ratu dan satu sama lain. Mereka membantu sang ratu untuk mengatur penciptaan para insinyur dan beberapa penambang junior pertama, dan kemudian membantu sang ratu dalam menjalankan sebuah jam: sebuah osilator yang akan membunyikan setiap 85 gerakan (saat tidak terganggu).

Jam induk ini, bersama-sama dengan jumlah makanan yang ditimbun dan orientasi acak 1-dari-4 yang kami terima, memisahkan penciptaan penambang dari kedatangan makanan, dan mengatur tingkat di mana penambang selanjutnya akan muncul. Ketika sang ratu baru saja menetap, semua makanan yang masuk dengan cepat diubah menjadi lebih banyak penambang sampai tingkat makanan yang masuk mencapai sekitar 9 per 1000 gerakan (yang membutuhkan sekitar selusin penambang produktif). Kemudian siklus jam menjadi faktor pembatas dan kami mulai menimbun makanan. Kemudian seiring dengan meningkatnya jumlah makanan, kami mengurangi laju pemijahan hingga paling banyak 6 penambang baru per 1000 gerakan dan kemudian paling banyak 3, dan akhirnya berhenti berproduksi sama sekali. Mekanisme ratchet mencegah ratu mengeluarkan terlalu banyak makanan saat pemijahan ketika semua rel terhalang atau rusak.

Bahkan tanpa kerusakan besar, efisiensi penambangan akan menurun banyak karena setiap permainan dibuka. Awalnya, kami mengharapkan penambang mengebor dengan kecepatan setengah cahaya dan kembali dengan kecepatan rendah untuk mengirimkan 1 makanan setiap 1000 gerakan rata-rata. Menjelang akhir, ini lebih seperti 0,12-0,14 makanan per penambang per 1000 gerakan. Bepergian dengan rel yang lebih panjang, mengecat pola poros yang sebagian besar berwarna putih ke atas kanvas yang sebagian besar bukan putih, dan memperbaiki poros dan rel semuanya membutuhkan waktu; penambang terjebak, atau hilang, atau diikat dalam pertempuran paintball dengan lawan.

Penambang kami berupaya mengekstraksi diri dari kemacetan lalu lintas utama.

Dan ada sistem kekebalan yang belum sempurna untuk menghadapi pengganggu yang tidak ramah.

Mencoba melakukan steamroll di halaman belakang kami tidak disarankan. Staf kami tidak akan terhibur.

Kelemahan utama adalah bahwa ratu Kincir Angin membutuhkan 8 makanan untuk membeli staf awalnya dari saku, sehingga fase berebut agak lama, memberikan pesaing lain yang serupa untuk memulai. Jika sarang kita dibanjiri dan rel dihancurkan sebelum kita menimbun makanan, hari itu akan berakhir buruk bagi kita. Kemudian, dan dengan setidaknya satu kereta berjalan atau diperbaiki, kita biasanya dapat terus berjalan, atau setidaknya berpegang pada (sebagian besar) apa yang sudah kita miliki.

Implementasi memperlakukan sel tetangga sebagai bernomor berlawanan arah jarum jam, mulai dari sudut, dengan array ( CCW) untuk menerjemahkan angka-angka ini ke dalam viewsubskrip pengontrol . Ketika kita memiliki kebutuhan (dan mampu) untuk menjabarkan rasa Utara kita, kita mengatur kompas kita, sebuah subscript dasarCCW. Fungsi semut selalu dimulai dengan mencatat sekelilingnya, khususnya merekam spektrum (seberapa sering setiap warna terjadi), dan kemudian bercabang berdasarkan jenis dan situasi semut di sepanjang pohon keputusan strategi dan taktik multi-level. Hal ini memungkinkan untuk menangani sejumlah kasus khusus yang aneh sambil menangani situasi yang paling umum dengan sangat cepat. Pohon itu memiliki hampir 200 daun yang melukis sel atau mengambil langkah atau membuat semut, dan lebih dari 70 yang tidak melakukan apa-apa - disaring dari 2 ^ 27 kemungkinan pola warna dikalikan dengan kemungkinan semut dalam pandangan, makanan dalam pandangan, dan makanan dilakukan.

Inilah rendering ASCII dari geometri hub:

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   | ^ |MR2|   | v |MR2|   | ^ |   |   |   |   |   |   |   |   |
|   |   |   |   | i |   |   | a | r |  rail 2   |   |   |   |   |   |   |   |
+---+---+---+---+-n-+---+---+-c-+---+---+---+---+---+---+---+---+---+---+---+
|   |   |   |ML1|   |   |ML1| a |   |RL0|RM0|RR0|   |   |   |   |   |   |   |
|   |   |   | y | u |   | y | t |   | c | r | g |   |   |   |   |   |   |   |
+---+---+---+---+-s-+---+---+-e-+---+---+---+---+---+---+---+---+---+---+---+
|   |   |   |MX | e |MR0|MX | d |MR0|RL2|RM2|RR2|MX |ML1|   |ML3|   |ML1|   |
|   |   |   | c |   | k | y |   | k | c | g | y | c | y |   | c |   | y |   |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|   |RR1|RR0|RR2|RR1|RR0|RR2|RR1|RR0|RL1|RM1|RR1|   | shaft in use  |  >|   |
|  r|   | g | y | r | g | y | y | g | g | b | r |   |   |   |   |   |   |   |
+--a+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| <i|RM1|RM0|RM2|RM1|RM0|RM2|RM1|RM0|RL0|RM0|RR0|MR0|   |MR2|   |MR0|   |   |
|  l| b | r | g | b | r | g | b | r | c | r | g | k |   | r |   | k |   |   |
+-- +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|  3|RL1|RL0|RL2|RL1|RL0|RL2|RL1|RL0|*Q*|RL0|RL1|RL2|RL0|RL1|RL2|RL0|RL1|   |
|   | g | c | c | g | c | c | g | c |clk| c | g | c | c | g | c | c | g |r  |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+a--+
|   |   |   |   |   |   |   |G3 |Grd|Sec|RM0|RM1|RM2|RM0|RM1|RM2|RM0|RM1|i >|
|   |   |   |   |   |   |   | k |r/y|clk| r | b | g | r | b | g | r | b |l  |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ --+
|   |   |   |   |   |   |   |G4 |G5 |G6 |RR0|RR1|RR2|RR0|RR1|RR2|RR0|RR1|1  |
|   |   |   |   |   |   |   | r | k | b | g | y | y | g | r | y | g |   |   |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |   |MR0|   |MX |MR0|   |MX |   |   |   |
|   |   |   |   |   |   |   |   |   |   | k |   | y | k |   | c |   |   |   |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

(menunjukkan warna MX dan RR1 selama dan setelah penurunan pertama oleh penambang junior).

Ada lusinan ujung longgar. (Misalnya, kita membersihkan makanan dari rel karena bisa menjadi penghambat bagi para penambang sarat, tetapi beberapa makanan di luar rel tidak akan dimakan karena kompleksitas tambahan sepertinya tidak sepadan.) Yang cukup baik adalah musuh yang lebih baik ...

v1.1 memperbaiki penyebab diskualifikasi dan menambahkan beberapa peningkatan kecil.

v1.2 menambahkan beberapa perbaikan dan peningkatan lainnya.

v1.3 menambahkan vardeklarasi pencampuran sebelumnya untuk membuat ini bekerja pada pengontrol mode ketat seperti Dave ; tidak ada perubahan fungsional.

v1.4 memperbaiki kesalahpahaman antara ratu dan tukang kebun tentang penggunaan bawang putih (saat Vampir yang tiba di sepanjang rail3 akan mengetahuinya), menyembuhkan bug definisi-pola yang bodoh, dan memperbaiki beberapa kasing tepi.

v1.5 mengajarkan Windmill sebuah trik baru - ingin melihat seekor semut kawin?

v1.6 memungkinkan penambang mencorat-coret pola menjadi hamparan kehijauan yang luas, memurnikan alarm pencuri di rumah, dan berurusan dengan ratu musuh di tempat lain dengan cara yang sedikit lebih elastis, ditambah sedikit perbaikan.

v1.7 selain dari perbaikan ketahanan kecil menggunakan fase startup gaya Lightspeed , bukan untuk menyelesaikan lebih awal tetapi untuk menyelesaikan dengan persediaan makanan yang lebih besar. (Kami membutuhkan 7 penambang untuk melebihi tingkat pengembalian makanan yang diharapkan dari tandem lamppeed, jadi tidak ada gunanya beralih ke penambangan sebelum kami mampu membelinya.)

v1.8 memperbaiki jalan buntu dalam logika fase Lightspeed dan, yang lebih penting, menyembuhkan bug pemijahan yang diperkenalkan di v1.6 yang mengakibatkan diskualifikasi.

v1.9 memperbaiki bug diskualifikasi eksotis lainnya, sementara menangani beberapa kasus kemacetan di poros, dan upaya untuk berurusan dengan penemuan vampir terbaru.

v2.0 memungkinkan sang ratu untuk meninggalkan hub yang ada sama sekali saat membutuhkan, beralih ke gaya Lightspeed yang berebut, dan dengan keberuntungan mengatur kembali nanti untuk menemukan pabrik baru di tempat lain, jauh dari situs aslinya. Eksperimen dengan Food Controlled Oscillator, variabel kecepatan, di sisi lain, tidak menghasilkan perbaikan yang meyakinkan. Mengirim lebih banyak penambang lebih awal agak akan meningkatkan tingkat kecelakaan beberapa pesaing, tetapi juga tingkat kita sendiri. Kode FCO tetap ada tetapi telah dinonaktifkan untuk saat ini.

v2.1 membahas tiga kasus tepi di mana pekerja lebih baik pindah daripada tetap tinggal.


Alasan mengapa poros Miners adalah seperti itu adalah karena penghapus jejak: dengan desain saya, sangat tidak mungkin bahwa penghapus jejak menemukan poros akan menghasilkannya menelusuri kembali ke rel, sedangkan milik Anda memiliki setidaknya 50% peluang memimpin penghapus jejak ke rel. Saya sedang mencari pola poros yang lebih baik untuk setidaknya setengah jam tanpa menemukan satu ..
dzaima

Ya, itu intinya. Di sisi lain, Trail-penghapus bisa bosan dengan rel dan berjalan pergi bersama zigzag back-to-back dinding poros ... Pola bolak lebih kuat ketika pengeboran ke medan yang sulit (meskipun tidak sempurna).
GNiklasch

Penambang di Kereta kadang-kadang benar-benar mendapat manfaat dari Penghapus Jejak: Memungkinkan penambang kereta untuk mengeksplorasi kembali poros tanpa benar-benar berniat. - Dan rel dan poros dapat diperbaiki. Kecelakaan dapat dan akan terjadi. Opsi pemulihan sangat penting. MoaR sudah mengesankan!
GNiklasch

1
Saya suka penjelasannya. Terutama menggunakan satu tipe pekerja untuk menghasilkan dua perilaku yang berbeda tergantung pada konteks lokal.
trichoplax

1
@trichoplax Whoops - terima kasih untuk yang hadir! Menyebabkan diketahui (berlangganan dari dua), lih edisi # 6 . Saya akan menggunakan kesempatan ini untuk memeriksa beberapa perbaikan kecil lainnya. Akan memperbarui jawaban saya setelah beberapa pengujian regresi dan memperkecil kode.
GNiklasch

12

Lubang hitam

var COLOR=8
var COLOR2=7
var COLOR3=2
var LOCKDOWN=8
var orthogonals = [1, 3, 7, 5]
var isQueen = view[4].ant.type==5
var rotationsCW = [1,2,5,8,7,6,3,0]
var rotationsCCW = [3,6,7,8,5,2,1,0]
var matchStates = [
    {state:[0,0,0,
            0,1,0,
            0,0,0],move:0,back:0,fill:true},
    {state:[1,0,0,
            0,1,0,
            0,0,0],move:1,back:3,fill:true},
    {state:[0,1,0,
            1,0,0,
            0,0,0],move:0,back:0,fill:false},
    {state:[0,1,0,
            0,0,1,
            0,0,0],move:2,back:2,fill:false},
    {state:[0,0,1,
            0,0,1,
            0,1,0],move:8,back:2,fill:false},
    //5:
    {state:[1,0,0,
            1,0,0,
            0,1,0],move:2,back:6,fill:false},
    {state:[0,1,0,
            0,1,0,
            0,0,0],move:2,back:0,fill:false},
    {state:[1,0,0,
            1,1,0,
            0,0,0],move:1,back:6,fill:false},
    {state:[0,1,1,
            0,1,0,
            0,0,0],move:5,back:0,fill:false},
    {state:[0,1,0,
            1,1,0,
            0,0,0],move:2,back:6,fill:false},
    //10:
    {state:[1,1,0,
            0,1,0,
            0,0,0],move:2,back:3,fill:false},
    {state:[1,1,0,
            1,1,0,
            0,0,0],move:2,back:6,fill:false},
    {state:[0,1,1,
            0,1,1,
            0,0,0],move:8,back:0,fill:false},
    {state:[1,1,1,
            0,1,0,
            0,0,0],move:5,back:3,fill:false},
    {state:[1,0,0,
            1,1,0,
            1,0,0],move:1,back:7,fill:false},
    //15:
    {state:[0,1,0,
            1,1,1,
            0,0,0],move:8,back:6,fill:false},
    {state:[1,1,1,
            1,1,0,
            0,0,0],move:5,back:6,fill:false},
    {state:[1,0,0,
            1,1,0,
            1,1,0],move:1,back:8,fill:false},
    {state:[1,1,0,
            1,1,0,
            1,0,0],move:2,back:7,fill:false},
    {state:[1,1,1,
            0,1,1,
            0,0,0],move:8,back:3,fill:false},
    //20:
    {state:[1,1,1,
            0,0,1,
            0,0,1],move:7,back:3,fill:true},
    {state:[1,1,1,
            1,0,0,
            1,0,0],move:5,back:7,fill:true},
    {state:[1,0,0,
            1,0,0,
            1,1,1],move:1,back:5,fill:true},
    {state:[0,0,1,
            0,0,1,
            1,1,1],move:3,back:1,fill:true},
    {state:[1,1,1,
            0,1,1,
            0,0,1],move:7,back:3,fill:true},
    //25:
    {state:[1,1,1,
            1,1,0,
            1,0,0],move:5,back:7,fill:true},
    {state:[1,0,0,
            1,1,0,
            1,1,1],move:1,back:5,fill:true},
    {state:[1,1,1,
            1,1,1,
            0,0,0],move:8,back:6,fill:true},
    {state:[1,1,1,
            1,1,1,
            1,0,0],move:8,back:7,fill:true},
    {state:[1,1,1,
            1,1,0,
            1,1,0],move:5,back:8,fill:true},
    {state:[1,1,1,
            1,1,1,
            1,1,0],move:8,back:8,fill:true},
    //30:
    {state:[1,1,1,
            1,1,1,
            1,0,1],move:7,back:7,fill:true},
    {state:[1,1,1,
            1,1,0,
            1,1,1],move:5,back:5,fill:true},
    {state:[1,0,1,
            1,1,1,
            1,1,1],move:1,back:1,fill:true},
    {state:[1,1,1,
            0,1,1,
            1,1,1],move:3,back:3,fill:true},
    {state:[1,1,1,
            1,1,1,
            1,1,1],move:9,back:9,fill:false},
    //35:
]
function matchesColor(c) {
    return c==COLOR || c==COLOR2 || c==COLOR3 || (view[4] == COLOR3 && c == LOCKDOWN)
}
function matchesNonLineColor(c) {
    return c==COLOR || c==COLOR2
}
function isAnyColor(c) {
    var r=0
    for(var i=0;i<9;i++) {
        if(view[i].color == c) r++
    }
    return r
}
function howManyAnts() {
    var r=0;
    for(var i=0;i<9;i++) {
        if(view[i].ant != null) r++
    }
    return r
}
function deRotate(m, amt) {
    if(m == 4 || m < 0 || m > 8 || amt == 0) return m
    if(amt > 0)
        return rotationsCW[(rotationsCW.indexOf(m)+amt)%8]
    amt = -amt
    return rotationsCCW[(rotationsCCW.indexOf(m)+amt)%8]
}
function deRotateSide(m, amt) {
    return deRotate(m,amt*2)
}
function matchWhileLost(sides) {
    var c=0;
    for(var i=0;i<9;i++) {
        if(view[i].color == COLOR3) c++
        if(view[i].color == COLOR3 && i%2 == 0) c+=10
    }
    if(c == 2) {
        if(view[0].color == COLOR3 || view[2].color == COLOR3 || view[6].color == COLOR3 || view[8].color == COLOR3) {
            return {cell:4,color:COLOR3}
        }
        if(view[0].ant == null)
            return {cell:0}
        if(view[2].ant == null)
            return {cell:2}
        if(view[6].ant == null)
            return {cell:6}
        if(view[8].ant == null)
            return {cell:8}
    }
    c = 0
    sides[4] = 0
    var toMatch =[{state:[1,1,1,
                         2,0,2,
                         0,1,0]},
                 {state:[0,2,1,
                         1,0,1,
                         0,2,1]},
                 {state:[0,1,0,
                         2,0,2,
                         1,1,1]},
                 {state:[1,2,0,
                         1,0,1,
                         1,2,0]}]
    for(var m=0;m<4;m++) {
        var score=0
        for(var j=0;j<9;j++) {
            if(j!=4) {
                if(sides[j] == COLOR3 && toMatch[m].state[j] == 1) {
                    score++
                }
                if(sides[j] != COLOR3 && (toMatch[m].state[j] == 0 || toMatch[m].state[j] == 2)) {
                    score++
                }
                if(sides[j] == COLOR3 && toMatch[m].state[j] == 2) {
                    score--
                }
            }
        }
        if(score >= 6) {
            var clearOrder=[1,0,2]
            for(var r=0;r<clearOrder.length;r++) {
                var s = deRotateSide(clearOrder[r],m)
                if(view[s].color == COLOR3) {
                    if(view[s].ant == null)
                        return {cell:s,color:COLOR}
                    else
                        return {cell:4}
                }
            }
        }
    }
    return null
}
function matchBlueStyle(sides) {
    return null
}
function bestMatch(sides) {
    var c=0;
    for(var i=0;i<9;i++) {
        if(sides[i] > 1) c++
    }
    if(!isQueen && view[4].ant.food > 0) {
        c++
        sides[4] = 8
    }
    if(c <= 1) {
        return {state:matchStates[0],rot:0,fill:matchStates[0].fill}
    }
    c = 0
    while(!matchesColor(sides[0]) && !matchesColor(sides[1]) && c < 4) {
        var s2 = [0,0,0,0,0,0,0,0,0]
        s2[0] = sides[2]
        s2[1] = sides[5]
        s2[2] = sides[8]
        s2[3] = sides[1]
        s2[5] = sides[7]
        s2[6] = sides[0]
        s2[7] = sides[3]
        s2[8] = sides[6]
        sides = s2
        c++
    }
    while(c < 8 && (matchesColor(sides[0]) || matchesColor(sides[1])) && matchesColor(sides[8])) {
        var s2 = [0,0,0,0,0,0,0,0,0]
        s2[0] = sides[2]
        s2[1] = sides[5]
        s2[2] = sides[8]
        s2[3] = sides[1]
        s2[5] = sides[7]
        s2[6] = sides[0]
        s2[7] = sides[3]
        s2[8] = sides[6]
        sides = s2
        c++
    }
    var bestState = null
    var bestMatchScore = -1
    for(var i = 0; i < matchStates.length; i++) {
        var score=0
        for(var j=0;j<9;j++) {
            if(j!=4) {
                if(matchesColor(sides[j]) && matchStates[i].state[j] == 1) {
                    score++
                }
                if(!matchesColor(sides[j]) && matchStates[i].state[j] == 0) {
                    score++
                }
            }
        }
        if(score >= bestMatchScore) {
            //console.log("state " + i + ": " + score);
            bestMatchScore = score
            bestState = matchStates[i]
        }
    }
    return {state:bestState,rot:c,fill:bestState.fill,score:bestMatchScore}
}
function getHighestWorker() {
    var r=0;
    for(var i=0;i<9;i++) {
        if(i != 4 && view[i].ant != null) {
            if(view[i].ant.friend && view[i].ant.type > r) r = view[i].ant.type
        }
    }
    return r
}
function pathLost() {
    var i, j
    var safe = []
    for(var q=0;q<9;q++) {
        if(q != 4 && view[q].ant != null && view[q].ant.friend && (view[q].ant.type > view[4].ant.type && view[4].ant.food == 0 && view[q].ant.type < 5)) {
            if(!matchesColor(view[4].color)) return {cell:4,color:COLOR}
            return {cell:4}
        }
    }
    if (matchesNonLineColor(view[4].color)) {
        var myView = [0,0,0,0,0,0,0,0,0]
        for(var i=0; i < 9; i++) {
            myView[i] = view[i].color
            if(!isQueen && view[4].ant.food > 0 && view[i].food > 0) {
                myView[i] = COLOR;
            }
        }
        var ret = matchWhileLost(myView)
        if(ret == null)
            return {cell:4, color:COLOR3}
        else {
            if(!(view[ret.cell].ant != null && view[ret.cell].ant.friend == false) && (view[4].ant.food == 0 || view[ret.cell].food == 0 || isQueen))
                return ret
        }
    }
    for (i=0; i<view.length; i++) {
        if (view[i].ant === null && (view[4].ant.food == 0 || view[i].food == 0 || isQueen)) {
            safe.push(i);
        }
    }
    for (i=0; i<4; i++) {
        j = (i+2) % 4
        if (matchesNonLineColor(view[orthogonals[i]].color) && view[orthogonals[j]].color == COLOR3) {
            if (view[orthogonals[i]].ant == null) {
                return {cell:orthogonals[i]}
            } else if (safe.length > 0) {
                return {cell:safe[0]}
            } else if (view[0].ant === null && (view[4].ant.food == 0 || view[0].food == 0 || isQueen)) {
                return {cell:0}
            }
        }
    }
    if (view[1].ant === null && (view[4].ant.food == 0 || view[1].food == 0 || isQueen)) {
        return {cell:1}
    } else {
        if(!matchesColor(view[4].color)) return {cell:4,color:COLOR}
        return {cell:4}
    }
}
function isAllyAdjacentTo(view, place) {
    var i = deRotate(place, 1)
    var j = deRotate(place, -1)
    if(view[i].ant != null && view[i].ant.friend && view[i].ant.type < 5) return 1
    if(view[j].ant != null && view[j].ant.friend && view[j].ant.type < 5) return 1
    if(orthogonals.indexOf(place) >= 0) {
        i = deRotate(place, 2)
        j = deRotate(place, -2)
        if(view[i].ant != null && view[i].ant.friend && view[i].ant.type < 5) return 2
        if(view[j].ant != null && view[j].ant.friend && view[j].ant.type < 5) return 2
    }
    return 0
}
function findOpenSpace(pos, dir) {
    if(pos > 8 || pos < 0) return pos
    if(view[pos].ant != null && view[pos].ant.friend && view[4].ant.food == 0) {
        pos=deRotate(pos,4)
    }
    //var inc = dir>0?1:-1
    var b = 0
    while(view[pos].ant != null && b < 8) {
        pos=deRotate(pos,dir)
        b++
    }
    return pos
}
//end functions
function getReturn() {
    var colToPlace=COLOR
    var blueAmt = isAnyColor(COLOR2)
    var myView = [0,0,0,0,0,0,0,0,0]
    for(var i=0; i < 9; i++) {
        myView[i] = view[i].color
        if(!isQueen && view[4].ant.food > 0 && view[i].food > 0) {
            myView[i] = COLOR;
        }
        if(!isQueen && view[4].ant.food == 0 && view[i].ant != null && view[i].ant.food > 0) {
            if(!matchesColor(view[4].color)) return {cell:4,color:COLOR}
            return {cell:4};
            //myView[i] = COLOR;
        }
        if(view[i].ant != null && !view[i].ant.friend) {
            myView[i] = COLOR;
        }
    }
    if(isQueen) {
        for(var i=0; i < 9; i++) {
            if(i != 4 && !matchesColor(view[i].color) && view[i].ant != null) {
                myView[i] = COLOR
            }
        }
    }
    //console.log("view:")
    //console.log(myView)
    //console.log("1")
    var match = bestMatch(myView)
    if(match.state.move != 9) {
        var ctY = 0
        var lastY = -1
        var ctW = 0
        var lastW = -1
        for(var i=0; i < 9; i++) {
            if(view[i].color == COLOR3) {
                myView[i] = 8
                ctY++
                lastY = i
            }
            else if(!matchesColor(view[i].color)) {
                ctW++
                lastW = i
            }
        }
        if(ctY > 0 && isQueen && view[4].ant.food > 0 && ctW >= 1) {
            if(view[4].color != COLOR3 && matchesColor(view[4].color))
                return {cell:4,color:COLOR3}
            var tt = deRotate(lastW,-1)
            if(view[tt].color != COLOR2)
                return {cell:tt,color:COLOR2}
            lastW = findOpenSpace(lastW,1)
            return {cell:lastW}
        }
        else if(ctY >= 2 && ctW >= 3)
            match = bestMatch(myView)
        else if(ctY > 0 && view[lastY].ant == null && ctW >= 3) {
            return {cell:lastY,color:1}
        }
    }
    //console.log("2")
    if(!isQueen) {
        for(var i=0; i < 9; i++) {
            if(view[i].ant != null && view[i].ant.type == 5 && view[i].ant.food > 0 && view[i].ant.food <= 2) {
                if(view[4].ant.type == 4)
                    return {cell:4,color:COLOR2}
                return {cell:4}
            }
        }
    }
    //console.log("3")
    if(blueAmt > 0 && view[4].color != COLOR3 && match.state.move != 9) {
        //console.log("Some blue")
        var mb = match.state.back
        mb = deRotateSide(mb,match.rot)
        if(!isQueen || view[4].ant.food <= 2) {
            var a = deRotate(mb,1)
            var b = deRotate(mb,-1)//TODO should be -1
            //console.log("mb: " + mb + "," + a + "," + b)
            if(mb != 9 && (view[mb].color == COLOR2 || view[4].color == COLOR2 || view[a].color == COLOR2 || view[b].color == COLOR2)) {
                //blue behind
                //console.log("Blue behind")
                colToPlace = COLOR2
            }
            else {
                //console.log("No blue behind")
                //console.log(match)
                var myView2 = [0,0,0,0,0,0,0,0,0]
                //construct a view without blue in it
                for(var i=0; i < 9; i++) {
                    myView2[i] = view[i].color == COLOR2?1:view[i].color
                }
                var match2 = bestMatch(myView2)
                if(match2.state.move == 9 || match2.state == matchStates[0]) {
                    //zero or one black
                    //console.log("<= 1 Black")
                    //console.log(myView2)
                    //console.log(match2.state)
                    colToPlace = COLOR2
                }
                else if(view[4].ant.type != 4) {
                    var mf = match2.state.move
                    mf = deRotateSide(mf,match2.rot)
                    //console.log("mf: " + mf)
                    if(mf != 9 && view[mf].color == COLOR2 && view[mf].ant == null) {
                        //about to move onto blue
                        //console.log("Moving onto blue")
                        //console.log(view)
                        //console.log(myView2)
                        return {cell:mf,color:1}
                    }
                    var clearOrder=[1,3,5,7,0,2,6,8]
                    for(var r=0;r<clearOrder.length;r++) {
                        var s = deRotateSide(clearOrder[r],0)
                        if(view[s].color == COLOR2 && (view[s].ant == null || !view[s].ant.friend || (isQueen && view[4].ant.food == 0))) {
                            //console.log("DIE BLUE SCUM")
                            //console.log(view)
                            //console.log(myView2)
                            return {cell:s,color:1}
                        }
                        else if(isQueen && view[s].ant != null && view[s].ant.friend) {
                            //console.log("Blue Queen")
                            //console.log(view)
                            //console.log(myView2)
                            return {cell:4,color:COLOR2}
                        }
                    }
                }
            }
        }
        //console.log("Nothing happened")
    }
    //console.log("4")
    if(view[4].ant.type <= 2) {
      for(var i=0; i < 9; i++) {
        if(view[i].ant != null && !view[i].ant.friend) {
          var canSeeAlly = isAllyAdjacentTo(view,i)
          if(canSeeAlly == 0) {
            if(view[i].color == LOCKDOWN) {
              var a = deRotate(i, 1)
              var b = deRotate(i, -1)
              if(view[a].color != LOCKDOWN) return {cell:a,color:LOCKDOWN}
              if(view[b].color != LOCKDOWN) return {cell:b,color:LOCKDOWN}
              if(orthogonals.indexOf(i) >= 0) {
                a = deRotate(i, 2)
                b = deRotate(i, -2)
                if(view[a].color != LOCKDOWN) return {cell:a,color:LOCKDOWN}
                if(view[b].color != LOCKDOWN) return {cell:b,color:LOCKDOWN}
              }
            }
            else {
              return {cell:i,color:LOCKDOWN}
            }
            if(view[4].color == LOCKDOWN || view[4].color == COLOR) {
              var ii = deRotate(i,4)
              ii = findOpenSpace(ii,1)
              return {cell:ii}
            }
            return {cell:4,color:COLOR}
          }
          else if(canSeeAlly == 2) {
            var m = deRotate(i, 2)
            var j = deRotate(i, -2)
            if(view[m].ant != null && view[m].ant.friend && view[m].ant.type < 5) return {cell:m,color:LOCKDOWN}
            if(view[j].ant != null && view[j].ant.friend && view[j].ant.type < 5) return {cell:j,color:LOCKDOWN}
          }
          else if(view[4].color == LOCKDOWN || view[4].color == 2) {

          }
          else {
            return {cell:4,color:2}
          }
        }
      }
      for(var i=0; i < 9; i++) {
        if(view[i].ant != null && view[i].ant.friend && (view[i].ant.type > view[4].ant.type && view[4].ant.food == 0)) {
          if(match.state.move == 9)
            return {cell:4}
          if(view[i].ant.type == 5)
            return {cell:4}
          var m = findOpenSpace(i,1)
          if(view[m].ant == null)
            return {cell:m}
          return {cell:4,color:2}
        }
      }
    }
    else if(view[4].ant.type <= 4) {
      for(var i=0; i < 9; i++) {
        if(view[i].ant != null && !view[i].ant.friend) {
          var canSeeAlly = isAllyAdjacentTo(view,i)
          if(canSeeAlly == 0) {
            if(view[i].color == LOCKDOWN) {
              var a = deRotate(i, 1)
              var b = deRotate(i, -1)
              if(view[a].color != LOCKDOWN) return {cell:a,color:LOCKDOWN}
              if(view[b].color != LOCKDOWN) return {cell:b,color:LOCKDOWN}
              if(orthogonals.indexOf(i) >= 0) {
                a = deRotate(i, 2)
                b = deRotate(i, -2)
                if(view[a].color != LOCKDOWN) return {cell:a,color:LOCKDOWN}
                if(view[b].color != LOCKDOWN) return {cell:b,color:LOCKDOWN}
              }
            }
            else {
              return {cell:i,color:LOCKDOWN}
            }
            if(view[4].color == LOCKDOWN || view[4].color == COLOR) {
              var ii = deRotate(i,4)
              ii = findOpenSpace(ii,1)
              return {cell:ii}
            }
            return {cell:4,color:COLOR}
          }
          else if(canSeeAlly == 2) {
                var m = deRotate(i, 2)
                j = deRotate(i, -2)
                if(view[m].ant != null && view[i].ant.friend && view[m].ant.type < 5) return {cell:m,color:LOCKDOWN}
                if(view[j].ant != null && view[j].ant.friend && view[j].ant.type < 5) return {cell:j,color:LOCKDOWN}
          }
          else if(view[4].color == LOCKDOWN || view[4].color == 2) {

          }
          else {
            return {cell:4,color:2}
          }
        }
      }
    }
    else if(view[4].ant.food > 4) {
        for(var i=0; i < 9; i++) {
            if(view[i].ant != null && !view[i].ant.friend) {
                var canSeeAlly = isAllyAdjacentTo(view,i)
                if(canSeeAlly == 0) {
                    var m = findOpenSpace(i,1)
                    if(view[m].ant == null)
                        return {cell:m,type:1}
                    return {cell:4,color:3}
                }
            }
        }
        var high = getHighestWorker()
        if(high >= 3 && view[4].ant.food % 2 == 1 && view[4].ant.food < 40) {
            var typeToSpawn = 1
            if(view[4].ant.food < 10 && high == 4 && view[4].ant.food % 4 == 1) {
                typeToSpawn = 3
            }
            var m = findOpenSpace(0,1)
            var canSeeAlly = isAllyAdjacentTo(view,m)
            if(canSeeAlly == 0 && view[m].ant == null)
                return {cell:m,type:typeToSpawn}
        }
    }
    //console.log("5")
    var m = match.state.move
    if(isQueen && view[4].ant.food > 0 && view[4].ant.food <= 2 && isAnyColor(COLOR2) == 0 && isAnyColor(COLOR3) == 0 && m < 9) {
        var high = getHighestWorker()+1
        var num = howManyAnts();
        //high += Math.max(num-2,0)
        if(high < 5) {
            m = deRotate(m,match.rot+4) //get space behind
            m = findOpenSpace(m,1) //make sure its open
            if(view[m].ant == null && view[m].food == 0)
                return {cell:m,type:high}
            return {cell:4}
        }
        else {
            //return {cell:9}
            colToPlace = COLOR2
        }
    }
    if(!isQueen && view[4].ant.food > 0 /*&& view[4].ant.type >= 3*/) {
        //console.log("type 3")
        m = match.state.back
        //console.log(m)
        colToPlace = COLOR
    }
    if(view[4].ant.type == 3) {
        colToPlace = COLOR
    }
    //console.log("6")
    if(!matchesColor(view[4].color) && !(!isQueen && view[4].ant.food)) {
        //console.log("6a")
        /*for(var j=0; j < 9; j++) {
            if(j != 4 && view[j].ant != null && view[j].ant.friend && view[j].ant.food > 0 && j != match.back) {
                m = match.state.move
                if(m < 9) {
                    m = findOpenSpace(m,1)
                    if(view[m].ant == null)
                        return {cell:m}
                    return {cell:4}
                }
                return {cell:4,color:colToPlace}
            }
        }*/
        if(isQueen && view[4].color == LOCKDOWN) {
          m = deRotateSide(m,match.rot)
          m = findOpenSpace(m,1)
          return {cell:m}
        }
        return {cell:4,color:colToPlace}
    }
    if(match.fill && !matchesColor(view[4].color)) {
        return {cell:4,color:colToPlace}
    }
    if(m >= 9) {
        if(!matchesColor(view[4].color)) {
            return {cell:4,color:COLOR}
        }
        //console.log("lost! " + view[4].ant.food);
        //console.log(pathLost());
        return pathLost()
    }
    //console.log("7")
    //console.log("m0: " + m + "+" + match.rot)
    m = deRotateSide(m,match.rot)
    //console.log("m1: " + m)
    m = findOpenSpace(m,1)
    //console.log("m2: " + m)
    if(view[4].ant.food > 0 && !matchesColor(view[4].color) /*&& (view[4].ant.type >= 3)*/) {
        var anyFood = false
        for(var x=0;x<9;x++) {
            if(view[x].food > 0) anyFood = true;
        }
        if(!anyFood)
            return {cell:4,color:colToPlace}
    }
    //console.log("m3: " + m)
    m = findOpenSpace(m,1)
    //console.log("m4: " + m)
    if((!isQueen && view[4].ant.food > 0 && view[m].food > 0) && view[m].ant == null) return {cell:4}
    return {cell:m}
}
var ret = getReturn()
ret = sanityCheck(ret)
return ret
function sanityCheck(ret) {
    if(!ret || ret.cell < 0 || ret.cell > 8 || (ret.cell != 4 && (ret.color == null || ret.color == 0) && view[ret.cell].ant != null) || (view[ret.cell].food > 0 && (view[4].ant.food > 0 && view[4].ant.type < 5))) {
        return {cell:4}
    }
    if(ret.type && (view[ret.cell].ant != null || view[ret.cell].food > 0)) {
        return {cell:4}
    }
    return ret;
}

Ini adalah fungsi semut yang masif. Ia melakukan ini:

Lubang hitam

Fungsionalitasnya sangat sederhana. Bagian atas dari blok kode mendefinisikan matchStatesobjek yang digunakan semut untuk mengidentifikasi ke arah mana mereka menghadap, dan dengan demikian, mereka mengorbit di sekitar area yang diketahui dieksplorasi. Kemudian beberapa fungsi pembantu (mencocokkan warna, menghitung semut, dll).

bestMatch()mengambil tampilan yang dilihat oleh semut (bisa berubah) dan menemukan kecocokan terbaik dalam matchStatesdan mengembalikan kecocokan terbaik.

Queeny melakukan satu hal ketika dia bergerak, menempatkan hitam:

  • Buat pekerja sampai dia pergi untuk membuat pekerja yang akan menjadi ratu, kemudian beralih ke menempatkan biru. Semut mana pun yang menempatkan warna yang melihat warna biru di tempat-tempat terdekat justru sebaliknya.
  • Sang ratu, jika dia melihat warna biru, menimbun makanan.

Pekerja tipe 1 dan 2 bertingkah seperti ratu sampai mereka menemukan makanan, kemudian mereka mengabaikan warna sampai mereka berjalan mengelilingi lingkaran dan memberikan makanan mereka kepada ratu.

Pekerja tipe 3 dan 4 bertindak seperti ratu sampai mereka menemukan makanan, kemudian mereka bekerja mundur di sekitar lingkaran (masih menempatkan warna) sampai mereka memberikan makanan mereka kepada ratu.

Semut mana pun yang menemukan dirinya sendiri kehilangan pathLost()panggilan yang merupakan algoritma garis lurus yang lebih pintar (itu adalah fungsi jalur lurus pintar dari meta dengan beberapa tweak).

Mereka tweak adalah:

  • Semut tipe 1 bertindak secara acak dan berusaha menghapus jalur (kebanyakan tidak diperlukan, tetapi semut tipe 1 tidak berharga dalam jangka panjang, dan ini membersihkan papan diagonal)
  • Non-ratu tidak akan bertindak jika mereka dapat melihat ratu
  • Kapan saja semut dapat melihat dan masih menentukan orientasi lintasannya yang bertemu lintasan sebelumnya di depannya, hapus lintasan itu, memastikan semut berhasil kembali:

Contoh crossover

Di luar itu, sebagian besar sisanya hanya penanganan kesalahan untuk memastikan bahwa tidak ada semut melakukan operasi ilegal (pindah ke semut lain, pindah ke makanan, memijahkan semut pada makanan ...) meskipun kesalahan terbesar menangani potongan kode turun di bagian bawah :

if(view[4].ant.food > 0 && !matchesColor(view[4].color) && (view[4].ant.type >= 3) || surroundingColor > 6) {
  var anyFood = false
  for(var x=0;x<9;x++) {
    if(view[x].food > 0) anyFood = true;
  }
  if(!anyFood)
    return {cell:4,color:colToPlace}
}

Semut tipe 3 dan 4 yang berjalan mundur tidak akan menempatkan warna di sekitar makanan yang masih ada di tanah (makanan diperlakukan seperti ubin berwarna untuk tujuan orientasi jalur). Selain itu, Semut Tipe 1 atau 2 yang berpikir bahwa mereka telah dikurung dalam (<= 2 ruang kosong dalam tampilan) akan menurunkan warna. Untuk 'pulau' kecil, mereka akhirnya membuat diri mereka tersesat, bukannya terperangkap secara permanen.

Makanan maksimum yang dapat diperoleh dari fraksi ini hanya dibatasi oleh seberapa cepat ia mengubah warna serta durasi maksimum permainan (minimum 10rb). Lebih banyak pekerja tidak selalu bermanfaat, tetapi penting untuk mendapatkan lebih awal. Pekerja tipe 3 dan 4 adalah yang paling efisien (mengambil 6 langkah lebih dekat dengan ratu setiap 6 langkah permainan) tetapi menciptakan mereka terlalu dini menyebabkan jumlah pekerja lebih sedikit. Jadi penempatan awal memiliki dampak besar, tetapi karena area yang dipetakan oleh gerombolan selalu tumbuh dengan beberapa ruang yang tidak terlihat, ia akan meraih setiap bagian terakhir meskipun tidak ada semut yang akan mengambil risiko tersesat untuk mengambil sepotong.

Perbarui 7/23

Memperhatikan beberapa masalah dalam kasus tepi tertentu, seperti ini:

Ratu ingin pindah ke musuh

Dan membuat perubahan kecil untuk menjelaskannya. Pada dasarnya, perlakukan semut musuh dan pekerja yang sarat sebagai ubin berwarna.

Perbarui 7/26

Fox, aku bahkan tidak tahu lagi.

  • Tweak penanganan hilang-kuning-jejak-kesalahan untuk menjadi lebih kuat
  • Tweaked friend-path-collision handling menjadi lebih kuat
  • Menambahkan kode penetral Trail Eraser
  • Menambahkan kode deteksi dan penanganan "jalur biru" [BETA]
  • Made queen terus menghasilkan pekerja setelah masuk ke mode menimbun (kadang-kadang, setidaknya)
  • Sanitasi pemindahan tidak valid
  • Kode spageti lain-lain
  • Dihapus console.log
  • Herobrine dihapus
  • Menambahkan disk akresi

Disk akresi baru

Tanpa jejak kuning:

Minus yang kuning

Perbarui 7/26 BAGIAN 2

  • Benar-benar mengubah "apa yang harus saya lakukan dengan biru?" kode
    • Disk akresi dihapus
  • Tambahkan garam dan merica
  • Memperbaiki masalah dengan "ke arah mana saya menghadap?" deteksi
    • Bentuk lingkaran dihapus
    • Menambahkan bentuk persegi
      • Sepertinya membosankan sekarang
  • Imunitas yang dihapus dari Trail-Eraser
  • Menambahkan kecerdasan dalam kode "Saya hilang", mengurangi semut yang macet

Perbarui 7/31

  • Menambahkan kembali kode anti-Trail-Eraser (hilang dalam pembaruan "hapus biru")
  • Fungsi cek kewarasan mencegah sel pewarna di bawah semut lain
  • Perbaikan anti-penghapus yang lebih baik: tidak perlu lagi 3 pekerja untuk menghadapi satu penghapus

Perbarui 8/4

Tweak kecil.

  • Warna LOCKDOWN sekarang hitam
  • Semua semut berjalan "mundur" untuk mengirimkan makanan. Mekanika-mekanik itu akan menyebabkan lebih sedikit semut yang terperangkap dalam "gelembung" yang ditinggalkan oleh Trail-Eraser
  • Penanganan semut yang hilang dengan lebih baik tidak saling macet
  • Mengurangi ambang "hanya penimbunan" menjadi 40

Kelemahan

  • Penghapusan.
  • Perusakan warna.

Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
Martin Ender

12

Vampire Mk.8 (Re-Vamped)

Ini telah berubah menjadi wiki komunitas sehingga siapa pun dapat memperbaruinya untuk menargetkan korban lain. Ia menggunakan konsep lingkungan untuk memisahkan kode penargetan yang berbeda. Jika Anda ingin melakukan perubahan, jalankan beberapa turnamen untuk memastikan kode baru Anda tidak menurunkan skor rata-rata!


Semua jawaban saya membagikan fungsi pembantu tingkat rendah yang sama. Cari "Logika tingkat tinggi dimulai di sini" untuk melihat kode spesifik untuk jawaban ini.

// == Shared low-level helpers for all solutions ==
var QUEEN = 5

var WHITE = 1
var COL_MIN = WHITE
var COL_LIM = 9

var CENTRE = 4

var NOP = {cell: CENTRE}

var DIR_FORWARDS = false
var DIR_REVERSE = true
var SIDE_RIGHT = true
var SIDE_LEFT = false

function sanity_check(movement) {
    var me = view[CENTRE].ant
    if(!movement || (movement.cell|0) !== movement.cell || movement.cell < 0 || movement.cell > 8) {
        return false
    }
    if(movement.type) {
        if(movement.color) {
            return false
        }
        if((movement.type|0) !== movement.type || movement.type < 1 || movement.type > 4) {
            return false
        }
        if(view[movement.cell].ant || view[movement.cell].food) {
            return false
        }
        if(me.type !== QUEEN || me.food < 1) {
            return false
        }
        return true
    }
    if(movement.color) {
        if((movement.color|0) !== movement.color || movement.color < COL_MIN || movement.color >= COL_LIM) {
            return false
        }
        if(view[movement.cell].color === movement.color) {
            return false
        }
        return true
    }
    if(view[movement.cell].ant && movement.cell != 4) {
        return false
    }
    if(view[movement.cell].food + me.food > 1 && me.type !== QUEEN) {
        return false
    }
    return true
}

function as_array(o) {
    if(Array.isArray(o)) {
        return o
    }
    return [o]
}

function best_of(movements) {
    var m
    for(var i = 0; i < movements.length; ++ i) {
        if(typeof(movements[i]) === 'function') {
            m = movements[i]()
        } else {
            m = movements[i]
        }
        if(sanity_check(m)) {
            return m
        }
    }
    return null
}

function play_safe(movement) {
    // Avoid disqualification: no-op if moves are invalid
    return best_of(as_array(movement)) || NOP
}

var RAND_SEED = (() => {
    var s = 0
    for(var i = 0; i < 9; ++ i) {
        s += view[i].color * (i + 1)
        s += view[i].ant ? i * i : 0
        s += view[i].food ? i * i * i : 0
    }
    return s % 29
})()

var ROTATIONS = [
    [0, 1, 2, 3, 4, 5, 6, 7, 8],
    [6, 3, 0, 7, 4, 1, 8, 5, 2],
    [8, 7, 6, 5, 4, 3, 2, 1, 0],
    [2, 5, 8, 1, 4, 7, 0, 3, 6],
]

function areAdjacent(A, B) {
    if(A == 4 || B == 4 || A == B) return true
    if(A % 2 == 0 && B % 2 == 0) return false
    if(A % 2 == 1 && B % 2 == 0) return areAdjacent(B,A)
    if(A % 2 == 1 && B % 2 == 1) return !(8-A == B || 8-B == A)
    if(A == 0 && (B == 1 || B == 3)) return true
    if(A == 2 && (B == 1 || B == 5)) return true
    if(A == 6 && (B == 3 || B == 7)) return true
    if(A == 8 && (B == 5 || B == 7)) return true
    return false
}

function try_all(fns, limit, wrapperFn, checkFn) {
    var m
    fns = as_array(fns)
    for(var i = 0; i < fns.length; ++ i) {
        if(typeof(fns[i]) !== 'function') {
            if(checkFn(m = fns[i])) {
                return m
            }
            continue
        }
        for(var j = 0; j < limit; ++ j) {
            if(checkFn(m = wrapperFn(fns[i], j))) {
                return m
            }
        }
    }
    return null
}

function identify_rotation(testFns) {
    // testFns MUST be functions, not constants
    return try_all(
        testFns,
        4,
        (fn, r) => fn(ROTATIONS[r]) ? ROTATIONS[r] : null,
        (r) => r
    )
}

function near(a, b) {
    return (
        Math.abs(a % 3 - b % 3) < 2 &&
        Math.abs(Math.floor(a / 3) - Math.floor(b / 3)) < 2
    )
}

function try_all_angles(solverFns) {
    return try_all(
        solverFns,
        4,
        (fn, r) => fn(ROTATIONS[r]),
        sanity_check
    )
}

function try_all_cells(solverFns, skipCentre) {
    return try_all(
        solverFns,
        9,
        (fn, i) => ((i === CENTRE && skipCentre) ? null : fn(i)),
        sanity_check
    )
}

function try_all_cells_near(p, solverFns) {
    return try_all(
        solverFns,
        9,
        (fn, i) => ((i !== p && near(p, i)) ? fn(i) : null),
        sanity_check
    )
}

function ant_type_at(i, friend) {
    return (view[i].ant && view[i].ant.friend === friend) ? view[i].ant.type : 0
}

function friend_at(i) {
    return ant_type_at(i, true)
}

function foe_at(i) {
    return ant_type_at(i, false)
}

function foe_near() {
    for(var i = 0; i < 9; ++ i) {
        if(i !== 4 && view[i].ant && !view[i].ant.friend) {
            return true
        }
    }
    return false
}

function ant_type_near(p, friend) {
    for(var i = 0; i < 9; ++ i) {
        if(i !== 4 && ant_type_at(i, friend) && near(i, p)) {
            return true
        }
    }
    return false
}

function move_agent(agents) {
    var me = view[CENTRE].ant
    var buddies = [0, 0, 0, 0, 0, 0]
    for(var i = 0; i < 9; ++ i) {
        ++ buddies[friend_at(i)]
    }

    for(var i = 0; i < agents.length; i += 2) {
        if(agents[i] === me.type) {
            return agents[i+1](me, buddies)
        }
    }
    return null
}

function grab_nearby_food() {
    return try_all_cells((i) => (view[i].food ? {cell: i} : null), true)
}

function go_anywhere() {
    return try_all_cells((i) => ({cell: i}), true)
}

function colours_excluding(cols) {
    var r = []
    for(var i = COL_MIN; i < COL_LIM; ++ i) {
        if(cols.indexOf(i) === -1) {
            r.push(i)
        }
    }
    return r
}

function generate_band(start, width) {
    var r = []
    for(var i = 0; i < width; ++ i) {
        r.push(start + i)
    }
    return r
}

function colour_band(colours) {
    return {
        contains: function(c) {
            return colours.indexOf(c) !== -1
        },
        next: function(c) {
            return colours[(colours.indexOf(c) + 1) % colours.length]
        },
        prev: function(c) {
            return colours[(colours.indexOf(c) + colours.length - 1) % colours.length]
        }
    }
}

function random_colour_band(colours) {
    return {
        contains: function(c) {
            return colours.indexOf(c) !== -1
        },
        next: function() {
            return colours[RAND_SEED % colours.length]
        }
    }
}

function fast_diagonal(colourBand) {
    var m = try_all_angles([
        // Avoid nearby checked areas
        (rot) => {
            if(
                !colourBand.contains(view[rot[0]].color) &&
                colourBand.contains(view[rot[5]].color) &&
                colourBand.contains(view[rot[7]].color)
            ) {
                return {cell: rot[0]}
            }
        },

        // Go in a straight diagonal line if possible
        (rot) => {
            if(
                !colourBand.contains(view[rot[0]].color) &&
                colourBand.contains(view[rot[8]].color)
            ) {
                return {cell: rot[0]}
            }
        },

        // When in doubt, pick randomly but avoid doubling-back
        (rot) => (colourBand.contains(view[rot[0]].color) ? null : {cell: rot[0]}),

        // Double-back when absolutely necessary
        (rot) => ({cell: rot[0]})
    ])

    // Lay a colour track so that we can avoid doubling-back
    // (and mess up our foes as much as possible)
    if(!colourBand.contains(view[CENTRE].color)) {
        var prevCol = m ? view[8-m.cell].color : WHITE

        var colours = [0, 0, 0, 0, 0, 0, 0, 0, 0]
        for(var i = 0; i < 9; ++ i) {
            ++ colours[view[i].color]
        }

        return {cell: CENTRE, color: colourBand.next(prevCol)}
    }

    return m
}

function checkAllNearEnvirons(colours, buddies) {
        var nearMoves = [victims.length]
        for(var e = 0; e < victims.length; e++) {
                var env = victims[e]
                nearMoves[e] = null
                if(env.near_nest(colours)) {
                        nearMoves[e] = env.near_nest_move(colours, buddies)
                }
        }
        return best_of(nearMoves)
}

function follow_edge(obstacleFn, side) {
    // Since we don't know which direction we came from, this can cause us to get
    // stuck on islands, but the random orientation helps to ensure we don't get
    // stuck forever.

    var order = ((side === SIDE_LEFT)
        ? [0, 3, 6, 7, 8, 5, 2, 1, 0]
        : [0, 1, 2, 5, 8, 7, 6, 3, 0]
    )
    return try_all(
        [obstacleFn],
        order.length - 1,
        (fn, i) => (fn(order[i+1]) && !fn(order[i])) ? {cell: order[i]} : null,
        sanity_check
    )
}

function start_dotted_path(colourBand, side, protectedCols) {
    var right = (side === SIDE_RIGHT)
    return try_all_angles([
        (rot) => ((
            !protectedCols.contains(view[rot[right ? 5 : 3]].color) &&
            !colourBand.contains(view[rot[right ? 5 : 3]].color) &&
            !colourBand.contains(view[rot[right ? 2 : 0]].color) &&
            !colourBand.contains(view[rot[1]].color)
        )
            ? {cell: rot[right ? 5 : 3], color: colourBand.next(WHITE)}
            : null)
    ])
}

function lay_dotted_path(colourBand, side, protectedCols) {
    var right = (side === SIDE_RIGHT)
    return try_all_angles([
        (rot) => {
            var ahead = rot[right ? 2 : 0]
            var behind = rot[right ? 8 : 6]
            if(
                colourBand.contains(view[behind].color) &&
                !protectedCols.contains(view[ahead].color) &&
                !colourBand.contains(view[ahead].color) &&
                !colourBand.contains(view[rot[right ? 6 : 8]].color)
            ) {
                return {cell: ahead, color: colourBand.next(view[behind].color)}
            }
        }
    ])
}

function follow_dotted_path(colourBand, side, direction) {
    var forwards = (direction === DIR_REVERSE) ? 7 : 1
    var right = (side === SIDE_RIGHT)

    return try_all_angles([
        // Cell on our side? advance
        (rot) => {
            if(
                colourBand.contains(view[rot[right ? 5 : 3]].color) &&
                // Prevent sticking / trickery
                !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
                !colourBand.contains(view[rot[0]].color) &&
                !colourBand.contains(view[rot[2]].color)
            ) {
                return {cell: rot[forwards]}
            }
        },

        // Cell ahead and behind? advance
        (rot) => {
            var passedCol = view[rot[right ? 8 : 6]].color
            var nextCol = view[rot[right ? 2 : 0]].color
            if(
                colourBand.contains(passedCol) &&
                nextCol === colourBand.next(passedCol) &&

                // Prevent sticking / trickery
                !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
                !colourBand.contains(view[rot[right ? 0 : 2]].color)
            ) {
                return {cell: rot[forwards]}
            }
        }
    ])
}

function escape_dotted_path(colourBand, side, newColourBand) {
    var right = (side === SIDE_RIGHT)
    if(!newColourBand) {
        newColourBand = colourBand
    }

    return try_all_angles([
        // Escape from beside the line
        (rot) => {
            var approachingCol = view[rot[right ? 2 : 0]].color
            if(
                !colourBand.contains(view[rot[right ? 8 : 6]].color) ||
                !colourBand.contains(approachingCol) ||
                colourBand.contains(view[rot[7]].color) ||
                colourBand.contains(view[rot[right ? 6 : 8]].color)
            ) {
                // not oriented, or in a corner
                return null
            }
            return best_of([
                {cell: rot[right ? 0 : 2], color: newColourBand.next(approachingCol)},
                {cell: rot[right ? 3 : 5]},
                {cell: rot[right ? 0 : 2]},
                {cell: rot[right ? 6 : 8]},
                {cell: rot[right ? 2 : 0]},
                {cell: rot[right ? 8 : 6]},
                {cell: rot[right ? 5 : 3]}
            ])
        },

        // Escape from inside the line
        (rot) => {
            if(
                !colourBand.contains(view[rot[7]].color) ||
                !colourBand.contains(view[rot[1]].color) ||
                colourBand.contains(view[CENTRE].color)
            ) {
                return null
            }
            return best_of([
                {cell: rot[3]},
                {cell: rot[5]},
                {cell: rot[0]},
                {cell: rot[2]},
                {cell: rot[6]},
                {cell: rot[8]}
            ])
        }
    ])
}

function latch_to_dotted_path(colourBand, side) {
    var right = (side === SIDE_RIGHT)

    return try_all_angles([
        (rot) => {
            var approachingCol = view[rot[right ? 2 : 0]].color
            if(
                colourBand.contains(approachingCol) &&
                view[rot[right ? 8 : 6]].color === colourBand.next(approachingCol) &&
                !colourBand.contains(view[rot[right ? 5 : 3]].color)
            ) {
                // We're on the wrong side; go inside the line
                return {cell: rot[right ? 5 : 3]}
            }
        },

        // Inside the line? pick a side
        (rot) => {
            var passedCol = view[rot[7]].color
            var approachingCol = view[rot[1]].color
            if(
                !colourBand.contains(passedCol) ||
                !colourBand.contains(approachingCol) ||
                colourBand.contains(view[CENTRE].color)
            ) {
                return null
            }
            if((approachingCol === colourBand.next(passedCol)) === right) {
                return best_of([{cell: rot[3]}, {cell: rot[6]}, {cell: rot[0]}])
            } else {
                return best_of([{cell: rot[5]}, {cell: rot[2]}, {cell: rot[8]}])
            }
        }
    ])
}


// == High-level logic begins here ==


var TARGET_COLOURS_ZIG = colour_band([4, 5, 7, 8])
var TARGET_COLOURS_FIREFLY = colour_band([2, 5, 8])
var GROUND_COLOURS_BH = colour_band([2, 7, 8])
var SAFE_COLOURS = random_colour_band([8])

var THIEF = 1
var BOUNCER = 2
var LANCE = 4
var LANCE_TIP = 3

var INITIAL_GATHER = 12

function colour_band_prev(band, base) {
    if(!band.contains(base)) {
        return band.next(WHITE)
    }
    var cur = band.next(base)
    var c
    while((c = band.next(cur)) !== base) {
        cur = c
    }
    return cur
}

function white_near(p) {
    for(var i = 0; i < 9; ++ i) {
        if(near(i, p) && view[i].color === WHITE) {
            return true
        }
    }
    return false
}

function white_near(p, min) {
    var c = 0
    for(var i = 0; i < 9; ++ i) {
        if(near(i, p) && view[i].color === WHITE) {
            if(++c >= min) return true
        }
    }
    return false
}

var TARGET_ARRANGEMENT_RAIL = [
    [8,4,5,8,5,2,4,2,6],
    [8,5,2,4,2,6,6,4,5],
    [4,2,6,6,4,5,8,4,5],
    [6,4,5,8,4,5,8,5,2]
]
var TARGET_NEAR_RAIL = [
    [2,4,0,5,8,0,4,8,0,1], //Not Valid for Worker #1
    [2,6,0,4,5,0,4,5,0,0],
    [4,6,0,2,4,0,5,8,0,0],
    [4,8,0,4,6,0,2,4,0,0],
    [4,5,0,5,2,0,2,6,0,1], //NV 1
    [4,5,0,4,5,0,5,2,0,5], //NV Q
    [5,2,0,2,6,0,4,5,0,0],
    [5,8,0,4,8,0,4,6,0,5]  //NV Q
]
var TARGET_COLOURS_RAIL = colour_band([4,5,2,4])
var rail_miners = {
    name:function() { return "rail_miners"; },
    near_nest: function(colours) {
        var bestScore = 0
        var enemyQueen = false
        // check every rotation for each 3x3 rail possibility
        TARGET_NEAR_RAIL.forEach(function (arrangement) {
            ROTATIONS.forEach(function (rot){
                var sevenVal = 1
                var score = 0
                for(var i = 0; i < 9; i++) {
                    score += arrangement[i] == view[rot[i]].color?1:0
                    score += (arrangement[i] == 0 && view[rot[i]].color == 7)?sevenVal:0
                    score += (arrangement[i] == 0 && !(view[rot[i]].color == 7 || view[rot[i]].color == 1))?-1:0
                    if(arrangement[i] == 0 && view[rot[i]].color == view[rot[i-2]].color) score -= 2
                    if(view[rot[i]].color) sevenVal = 0
                    enemyQueen |= view[i].ant && view[i].ant.type == QUEEN && !view[i].ant.friend
                    if(view[i].ant != null && view[i].ant.friend && view[i].ant.type == THIEF && view[i].color == WHITE) score++
                }
                if(score > bestScore && arrangement[9] != view[4].ant.type) {
                    bestScore = score
                }
            })
        })
        if(bestScore >= (5 - (enemyQueen && view[4].ant.type == 1?1:0))) {
            if(highway.likely_nest(colours)) return false
            return true
        }
        return false
    },
    worth_leeching: function(myFood, buddies) {
        var numFours = 0
        var foodNeed = 11
        for(var i = 0; i < 9; i++) {
            if(foe_at(i) == 4) numFours++
        }
        if(!buddies[THIEF]) return false
        if(view[4].ant.type != 5 && buddies[QUEEN] && myFood < 500 && myFood+buddies[THIEF] > (foodNeed-numFours*3)) return true
        return myFood < 500 && myFood >= (foodNeed-numFours*3)
    },
    near_nest_move: function(colours, buddies) {
        var victim_pos = -1
        var avoid_pos = -1
        var friend_pos = -1
        for(var i = 0; i < 9; i++) {
            if(foe_at(i) == QUEEN) victim_pos = i
            if(foe_at(i) > 0 && foe_at(i) < 4) avoid_pos = i
            if(friend_at(i) == THIEF) friend_pos = i
        }
        if(victim_pos >= 0) return rail_miners.follow_victim(view[4].ant, buddies, colours, victim_pos)
        if(view[4].ant.type == THIEF && buddies[QUEEN]) return NOP
        if(view[4].ant.type == QUEEN && rail_miners.worth_leeching(view[4].ant.food, buddies)) {
            if(avoid_pos >= 0 && view[4].color != WHITE) {
                return best_of([
                    try_all_angles.bind(null, [
                        (rot) => (friend_at(rot[1]) === THIEF ? {cell: rot[0]} : null),
                        (rot) => (friend_at(rot[0]) === THIEF ? {cell: rot[3]} : null)
                    ]),
                    try_all_angles.bind(null, [
                        (rot) => (friend_at(rot[1]) === THIEF ? {cell: rot[2]} : null),
                        (rot) => (friend_at(rot[0]) === THIEF ? {cell: rot[1]} : null)
                    ]),
                    NOP
                ])
            }
            var allowed = [[8,4,8],[4,6,8],[6,8,4],[5,5,6],[6,5,2],[2,6,5]]
            var curr = [view[4].color,view[friend_pos].color,view[8-friend_pos].color]
            var found = false
            allowed.forEach(function (al) {
                if(al[0] == curr[0] && al[1] == curr[1] && al[2] == curr[2]) {
                    found = true
                }
            })
            if(!found) {
                return best_of([
                    try_all_angles.bind(null, [
                        (rot) => (friend_at(rot[1]) === THIEF && [2,4,5].indexOf(view[rot[2]].color) >= 0 ? {cell: rot[2]} : null),
                        (rot) => (friend_at(rot[1]) === THIEF && [2,4,5].indexOf(view[rot[0]].color) >= 0 ? {cell: rot[0]} : null)
                    ]),
                    NOP
                ])
            }
            return NOP
        }
        return null
    },
    likely_nest: function(colours) {
        var bestScore = 0
        // check every rotation for each 3x3 rail possibility
        var q = 0
        TARGET_ARRANGEMENT_RAIL.forEach(function (arrangement) {
            var j = 0
            ROTATIONS.forEach(function (rot){
                var score = 0
                for(var i = 0; i < 9; i++) {
                    score += arrangement[i] == view[rot[i]].color?1:0
                    if(view[i].ant != null && view[i].ant.friend && view[i].ant.type == THIEF && view[i].color == WHITE) score++
                }
                if(score > bestScore) {
                    bestScore = score
                }
                j++
            })
            q++
        })
        if(view[4].ant.type == THIEF && rail_miners.near_nest(colours)) return true
        if(bestScore >= 7) {
            if(highway.likely_nest(colours)) return false
            return true
        }
        return false
    },

    likely_victim: function(victim_pos) {
        return true
    },

    follow_victim: function(me, buddies, colours, victim_pos) {
        if(me.type == QUEEN) {
            if(victim_pos % 2 == 0) {
                return best_of([
                    try_all_angles.bind(null, [
                        (rot) => (foe_at(rot[0]) === QUEEN && friend_at(rot[5]) == THIEF ? {cell: rot[2]} : null),
                        (rot) => (foe_at(rot[0]) === QUEEN /*&& friend_at(rot[7]) == THIEF*/ ? {cell: rot[6]} : null)
                    ]),
                    NOP
                ])
            }
            else {
                return best_of([
                    try_all_angles.bind(null, [
                        (rot) => (foe_at(rot[1]) === QUEEN && friend_at(rot[2]) == THIEF ? {cell: rot[5], type: THIEF} : null),
                        (rot) => (foe_at(rot[1]) === QUEEN ? {cell: rot[3], type: THIEF} : null),
                        (rot) => (foe_at(rot[1]) === QUEEN ? {cell: rot[5], type: THIEF} : null),
                        (rot) => (buddies[THIEF] < 4 && foe_at(rot[1]) === QUEEN ? {cell: rot[2], type: THIEF} : null),
                        (rot) => (buddies[THIEF] < 4 && foe_at(rot[1]) === QUEEN ? {cell: rot[0], type: THIEF} : null)
                    ]),
                    NOP
                ])
            }
        }
        return NOP
    },
    find_victim: function(me, buddies, colours) {
        var forwardCell = -1
        var current = view[CENTRE].color
        var target = TARGET_COLOURS_RAIL.next(current)
        var antitarget = TARGET_COLOURS_RAIL.prev(current)
        var queenPos = -1
        for(var i = 0; i < 9; i++) {
            if(i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget && current != WHITE){
                forwardCell = i
            }
            if(friend_at(i) == QUEEN) queenPos = i
        }
        if(forwardCell < 0 && current == 4) {
            target = 4
            antitarget = 2
            for(var i = 0; i < 9; i++) {
                if(i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget){
                    forwardCell = i
                }
            }
        }
        if(me.type == QUEEN) {
            var numEn = 0
            for(var i = 0; i < 9; i++) {
                if(i % 2 == 1 && friend_at(i) == THIEF && friend_at(8-i) == THIEF){
                    if(foe_at(deRotate(i,1)) > 0)
                        return {cell:forwardCell}
                    if(foe_at(deRotate(i,-1)) > 0)
                        return {cell:forwardCell}
                    return NOP
                }
                if(i % 2 == 0 && friend_at(i) == THIEF && friend_at(deRotate(i,2)) == THIEF){
                    return {cell:deRotate(i,3), type:THIEF}
                }
            }
            return wilderness.find_victim(me, buddies, colours)
        }
        else if(forwardCell >= 0) {
            if(friend_at(forwardCell) == QUEEN) {
                return best_of([
                    try_all_angles.bind(null, [
                        (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
                        (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null)
                    ]),
                    go_anywhere
                ])
            }
        }
        else if(queenPos>=0 && view[queenPos].color == WHITE && (foe_at(deRotate(queenPos,2)) && foe_at(deRotate(queenPos,-2)))) {
            return wilderness.find_victim(me, buddies, colours)
        }
        if(me.type == THIEF && forwardCell >= 0 && buddies[THIEF] == 1) {
            return wilderness.find_victim(me, buddies, colours)
        }
        return NOP
    }
}

var TARGET_ARRANGEMENT_WIND = [
        [5,4,0,7,6,0,6,4,0],
        [7,6,0,6,4,0,5,4,0],
        [6,4,0,5,4,0,7,6,0]
]
var TARGET_ARRANGEMENT_WINDCENTER = [
        [2,7,6,2,6,4,6,5,4],
        [2,6,4,6,5,4,2,7,6],
        [6,5,4,2,7,6,2,6,4]
]
var WIND_BAND = colour_band([5,6,7])
var windmill = {
    name:function() { return "windmill"; },
    near_nest: function(colours) { return false; },
    near_nest_move: function(colours, buddies) { return null; },
    likely_nest: function(colours) { // Main nest detection
        var bestScore = 0
        // check every rotation for each 3x3 rail possibility
        TARGET_ARRANGEMENT_WIND.forEach(function (arrangement) {
            ROTATIONS.forEach(function (rot){
                var score = 0
                for(var i = 0; i < 9; i++) {
                    score += arrangement[i] == view[rot[i]].color?1:0
                }
                if(score > bestScore) {
                    bestScore = score
                }
            })
        })
        if(bestScore >= 5 && view[4].ant.type != THIEF) {
            return true
        }

        var bestScore = 0
        // check every rotation for each 3x3 rail possibility
        TARGET_ARRANGEMENT_WINDCENTER.forEach(function (arrangement) {
            ROTATIONS.forEach(function (rot){
                var score = 0
                for(var i = 0; i < 9; i++) {
                    score += arrangement[i] == view[rot[i]].color?1:0
                }
                if(score > bestScore) {
                    bestScore = score
                }
            })
        })
        if(bestScore >= 8) {
            return true
        }
        var buddies = [0, 0, 0, 0, 0, 0]
        for(var i = 0; i < 9; ++ i) {
            ++ buddies[friend_at(i)]
        }
        return buddies[LANCE] || buddies[LANCE_TIP]
    },
    worth_leeching: function(myFood, buddies) {
        if(view[4].ant.type == THIEF && (buddies[LANCE] > 0 || buddies[LANCE_TIP] > 0)) return true
        return myFood > 5 || (myFood > 1 && buddies[LANCE])
    },
    likely_victim: function(victim_pos) {
        return false
    },

    follow_victim: function(me, buddies, colours, victim_pos) {
        // nest is chaotic and varies by direction of approach
        // We'll let the Find Victim logic handle this
        return NOP
    },

    find_victim: function(me, buddies, colours) {
        if(me.type == THIEF) {
            var queenPos = -1
            var lancePos = -1
            var tipPos = -1
            for(var i=0;i<9;i++) {
                if(friend_at(i) == QUEEN) queenPos = i
                if(friend_at(i) == LANCE) lancePos = i
                if(friend_at(i) == LANCE_TIP) tipPos = i
            }
            if(queenPos < 0 || (foe_at(deRotate(queenPos,1)) > 0 && foe_at(deRotate(queenPos,2)) > 0)) {
                if(queenPos < 0)
                    return go_anywhere
                return {cell:8-queenPos}
            }
            if(queenPos % 2 == 1 && tipPos % 2 == 0) {
                return go_anywhere
            }
            if(queenPos % 2 == 0 && lancePos % 2 == 1) {
                return go_anywhere
            }
            if(queenPos % 2 == 1 && foe_at(deRotate(queenPos,-2)) > 0) {
                return go_anywhere
            }
            return NOP
        }
        if(buddies[LANCE_TIP]) {
            var lancePos = -1
            for(var i=0;i<9;i++) {
                if(friend_at(i) == LANCE_TIP) {
                    lancePos = i
                }
            }
            if(buddies[LANCE]) {
                if(friend_at(8-lancePos) == LANCE) {
                    if(foe_at(deRotate(8-lancePos,1)) == 1 || foe_at(deRotate(8-lancePos,2)) == 1) {
                        var ret = NOP
                        if(lancePos % 2 == 1)
                            ret = {cell:deRotate(8-lancePos,-2)}
                        if(lancePos % 2 == 0)
                            ret = {cell:deRotate(8-lancePos,-3)}
                        if(!sanity_check(ret)) {
                            ret = best_of([
                                try_all_cells_near(lancePos, (i) => (ant_type_at(i) == 0 && view[i].color == 6 ? {cell: i} : null), true),
                                NOP
                            ])
                        }
                        return ret
                    }
                    if(foe_at(deRotate(lancePos,-2)) > 0) {
                        return {cell:deRotate(lancePos,2)}
                    }
                    return NOP
                }
                if(friend_at(deRotate(lancePos,3)) == LANCE) {
                    if((view[lancePos].color == 2 && view[4].color == 7) || foe_at(8-lancePos)) {
                        return {cell:deRotate(lancePos,1)}
                    }
                    return NOP
                }
                if(view[4].color == 6 && view[lancePos].color == 6 && friend_at(deRotate(lancePos,1)) == LANCE) {
                    if(foe_at(deRotate(lancePos,2)) > 0) {

                        return {cell:8-deRotate(lancePos,2)}
                    }
                    return NOP
                }
                if(view[lancePos].color == 2 && view[deRotate(lancePos,-3)].color == 5 && friend_at(deRotate(lancePos,-3)) == LANCE) {
                    return NOP
                }
                if(lancePos % 2 == 0) {
                    if(foe_at(deRotate(lancePos,-1)) > 0 && lancePos % 2 == 1) return {cell:deRotate(lancePos,2)}
                    if(view[deRotate(lancePos,-1)].color != 5) return {cell:deRotate(lancePos,-1),color:5}
                    if(view[deRotate(lancePos,-1)].color == 3 && view[4].color == 1) return {cell:4,color:3}
                    if(view[deRotate(lancePos,-1)].color == 5 && view[4].color == 3) return {cell:4,color:2}
                    if(view[deRotate(lancePos,-1)].color == 5 && view[4].color == 2) return {cell:4,color:1}
                    if(view[deRotate(lancePos,-1)].color == 5 && view[4].color == 7 && view[deRotate(lancePos,-1)].ant == null) return {cell:deRotate(lancePos,-1),type:THIEF}
                    if(view[deRotate(lancePos,-1)].color == 5 && view[4].color == 7) return {cell:4,color:3}
                }
                return {cell:deRotate(lancePos,-1)}
            }
            if(view[4].color == WHITE && view[lancePos].color == WHITE) {
                return {cell:deRotate(lancePos,-2),type:BOUNCER}
            }
            if(view[deRotate(lancePos,-1)].ant != null && view[deRotate(lancePos,-1)].ant.type == 5) {
                return {cell:deRotate(lancePos,2)}
            }
            if(view[4].color == 6 && view[deRotate(lancePos,1)].color == 7) {
                return {cell:deRotate(lancePos,1)}
            }
            if(foe_at(deRotate(lancePos,-2)) > 0 || foe_at(deRotate(lancePos,-3)) > 0) {
                if(foe_at(deRotate(lancePos,-2)) > 0 && foe_at(deRotate(lancePos,3)) > 0 && (foe_at(deRotate(lancePos,-1)) > 0 || foe_at(deRotate(lancePos,4)) > 0)) {
                    return {cell:deRotate(lancePos,1)}
                }
                if(foe_at(deRotate(lancePos,3)) > 0) {
                    return NOP
                }
                return {cell:deRotate(lancePos,1)}
            }
            if(foe_at(deRotate(lancePos,2)) > 0 && view[deRotate(lancePos,-1)].color != 2) {
                return {cell:deRotate(lancePos,-1),color:2}
            }
            if(foe_at(deRotate(lancePos,-1)) > 0) {
                return {cell:deRotate(lancePos,1)}
            }
            if(lancePos % 2 == 1 && friend_at(deRotate(lancePos,-1)) == THIEF) {
                return {cell:deRotate(lancePos,-2)}
            }
            return {cell:deRotate(lancePos,-1)}
        }
        else if(buddies[LANCE]) {
            var lancePos = -1
            for(var i=0;i<9;i++) {
                if(view[i].ant && view[i].ant.friend && view[i].ant.type == LANCE) {
                    lancePos = i
                }
            }
            if(view[4].color == 3 && lancePos % 2 == 1) return NOP
            var moveNext = lancePos % 2 == 1 ? {cell:deRotate(lancePos,2)} : {cell:deRotate(lancePos,1)}
            if(view[moveNext.cell].ant != null && !view[moveNext.cell].ant.friend) {
                moveNext = {cell:deRotate(lancePos,1),type:LANCE_TIP}
            }
            if(view[lancePos].ant.food > 0) {
                if(lancePos % 2 == 1)
                    return {cell:deRotate(lancePos,4),type:LANCE_TIP}
                else
                    return {cell:deRotate(lancePos,3),type:LANCE_TIP}
            }
            if(view[lancePos].color == 6 && view[moveNext.cell].color == 8 && view[deRotate(lancePos,2)].color == 5) {
                return {cell:moveNext.cell,type:LANCE_TIP}
            }

            return moveNext
        }
        else {
            var current = view[CENTRE].color
            var standOn = WIND_BAND.next(WIND_BAND.next(WIND_BAND.next(current)))
            var target = WIND_BAND.next(current)
            var antitarget = WIND_BAND.next(target)
            if(current != standOn) return wilderness.find_victim(me, buddies, colours)

            var ret = best_of([
                try_all_cells((i) => ((i % 2 == 1 && view[i].color == target && view[8-i].color == antitarget && ([2,5,6].indexOf(view[deRotate(i,-1)].color) >= 0) && (view[i].color != 5 || view[deRotate(i,1)].color == 4)) ? {cell: i, type: LANCE} : null), true),
                NOP
            ])
            if(ret.cell == 4) {
                return wilderness.find_victim(me, buddies, colours)
            }
            return ret
        }
        return NOP
    }
}

var TARGET_ARRANGEMENT_HIGHWAY = [
    [2,3,7,6,8,2,3,7,6],
    [2,3,7,7,6,4,4,2,3],
    [2,4,6,7,3,2,4,6,7],
    [3,2,4,4,6,7,7,3,2],
    [3,4,7,7,2,6,6,3,4],
    [3,4,7,2,6,3,4,7,2],
    [3,6,2,2,7,4,4,3,6],
    [4,7,2,2,5,6,3,4,7],
    [4,6,7,2,6,3,3,4,7],
    [4,6,7,7,3,2,2,4,6],
    [6,4,2,3,7,6,4,2,3],
    [7,3,2,2,4,6,6,7,3],
    [7,4,3,6,2,7,4,3,5]
]
var HIGHWAY_BAND = colour_band([2,7,4,3,6])
var HIGHWAY_BAND2 = colour_band([2,3,7,6,4])

var highway = {
    name:function() { return "highway"; },                                     // For debugging
    near_nest: function(colours) { return false; },                // For dodging enemy workers without getting lost
    near_nest_move: function(colours, buddies) { return null; }, // How to move when near_nest is true
    likely_nest: function(colours) { // Main nest detection
        var bestScore = 0
        // check every rotation for each 3x3 rail possibility
        TARGET_ARRANGEMENT_HIGHWAY.forEach(function (arrangement) {
            ROTATIONS.forEach(function (rot){
                var score = 0
                for(var i = 0; i < 9; i++) {
                    score += arrangement[i] == view[rot[i]].color?1:0
                }
                if(score > bestScore) {
                    bestScore = score
                }
            })
        })
        if(bestScore >= 7) {
            return true
        }
        if(this.isCenter(colours)) return true

        return false
    },         // Main nest detection
    isCenter: function(colours) {
        var bestScore = 0
        ROTATIONS.forEach(function (rot){
            var score = 0
            for(var i = 0; i < 9; i++) {
                if(i >= 3 && i <= 5 && [2,7,4,3,6].indexOf(view[rot[i]].color) >= 0 && (i == 4 || view[rot[i]].color != view[rot[8-i]].color)) {
                    if(i != 4) {
                        score++
                    }
                    else {
                        if(view[rot[3]].color != view[rot[5]].color && view[rot[1]].color == view[rot[7]].color && (view[rot[4]].color != view[rot[1]].color && view[rot[4]].color != view[rot[3]].color && view[rot[4]].color != view[rot[5]].color && view[rot[4]].color != view[rot[7]].color)) {
                            score++
                        }
                    }
                }
                else if(i >= 6) {
                    if(view[rot[i]].color == view[rot[i-6]].color && [2,7,4,3,6].indexOf(view[rot[i]].color) >= 0 && (i == 7 || view[rot[i]].color != view[rot[8-i]].color) && view[rot[i]].color != view[4].color) {
                        score += 2
                    }
                }
            }
            if(score > bestScore) {
                bestScore = score
            }
        })
        if(bestScore >= 7) {
            return true
        }
        return false
    },
    worth_leeching:function(myFood, buddies){ return myFood > 80 && myFood < 500; }, // Is this nest worth leeching?
    likely_victim: function(victim_pos) {
        return true
    },   // Identifying the target queen
    follow_victim: function(me, buddies, colours, victim_pos) {
        if(me.type == QUEEN && buddies[THIEF] < 3) {
            return best_of([
                try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true),
                try_all_cells((i) => ({cell: i, type: THIEF}), true)
            ])
        }
        if(me.type == THIEF && buddies[QUEEN])
            return NOP
        return go_anywhere
    },   // How to handle what happens when the enemy queen is found
    find_victim: function(me, buddies, colours) {
        if(me.type == THIEF && !buddies[QUEEN]) {
            for(var i=0;i<9;i++) {
                if(foe_at(i)) return NOP
            }
            var target = HIGHWAY_BAND.prev(view[4].color)
            var followRail = best_of([
                try_all_cells((i) => (i % 2 == 1 && view[i].color == target) ? {cell:i} : null),
                NOP
            ])
        }
        else {
            var target = HIGHWAY_BAND.next(view[4].color)
            var followRail = best_of([
                try_all_cells((i) => (i % 2 == 1 && view[i].color == target) ? {cell:i} : null),
                NOP
            ])
        }
        return followRail
    }                // How to follow the nest
}

var wilderness = {
    name:function() { return "wilderness"; },
    near_nest: function(colours) { return false; },
    near_nest_move: function(colours, buddies) { return null; },
    likely_nest: function(colours) {
        return true
    },
    worth_leeching: function(myFood, buddies) {
        return true
    },
    likely_victim: function(victim_pos) {
        return true
    },

    follow_victim: function(me, buddies, colours, victim_pos) {
        // We stumbled across a random queen; make the most of it
        // TODO
        if(rail_miners.near_nest(colours)) {
            return rail_miners.follow_victim(me, buddies, colours, victim_pos)
        }

        // avoids blocking off the rail miner queen from her workers
        // (we'd like to leech her again)
        if(me.type === QUEEN && !buddies[THIEF] && me.food > 0) {

            // Make a buddy to help us steal
            return best_of([
                try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true),
                try_all_cells((i) => ({cell: i, type: THIEF}), true)
            ])
        }
        else if(me.type === QUEEN){
            var enemyCount = 0
            var allyPos = -1
            for(var a=0; a<9; a++) {
                if(a != 4 && view[a].ant != null) {
                    if(view[a].ant.friend) {
                        if(near(a,victim_pos)){
                            allyPos = a
                        }
                    }
                    else if(view[a].ant.type != 5) {
                        enemyCount++
                    }
                }
            }
            if(enemyCount >= buddies[THIEF] && allyPos >= 0) {
                //if next to the queen and we're outnumbered, move back to the center of the rail.
                var target = TARGET_COLOURS_RAIL.prev(view[allyPos].color)
                var best = best_of([
                    try_all_cells((i) => (near(i, victim_pos) && i % 2 == 0 ? {cell: i, type: THIEF} : null), true),
                    try_all_cells((i) => (near(i, victim_pos) ? {cell: i, type: THIEF} : null), true)
                ])
                if(best != null) return best

                best_of([
                    try_all_cells((i) => ((view[i].color == target && i != 4 && areAdjacent(i,a)) ? {cell: i} : null))
                ])
                if(best != null) return best

                return best_of([
                    {cell:deRotate(allyPos,1)},
                    {cell:deRotate(allyPos,-1)}
                ])
            }
        }

        return NOP
    },
    find_victim: function(me, buddies, colours) {
        if(me.type === QUEEN) {
            var in_void = true
            for(var i = 0; i < 9; ++ i) {
                if(view[i].color !== WHITE && !SAFE_COLOURS.contains(view[i].color)) {
                    in_void = false
                    break
                }
            }
            if(!in_void) {
                // because of avoiding returning Miner on a Rail workers
                // we dodge sideways and this takes us back onto track
                var nearMove = checkAllNearEnvirons(colours, buddies)
                if(nearMove) return nearMove
            }
            return best_of([
                // Make a buddy once we have a reasonable stash of food so we can
                // search the board faster
                // (but avoid making buddies when there's a potential nest nearby
                // better to wait until we find their queen)
                (!buddies[THIEF] && me.food >= INITIAL_GATHER && in_void) &&
                    try_all_cells((i) => ({cell: i, type: THIEF}), true),

                // Follow buddy in search of victims
                buddies[THIEF] && try_all_angles.bind(null, [
                    (rot) => (friend_at(rot[1]) === THIEF ? {cell: rot[2]} : null),
                    (rot) => (friend_at(rot[0]) === THIEF ? {cell: rot[1]} : null)
                ]),
                buddies[THIEF] && try_all_angles.bind(null, [
                    (rot) => (friend_at(rot[1]) === THIEF ? {cell: rot[0]} : null),
                    (rot) => (friend_at(rot[0]) === THIEF ? {cell: rot[3]} : null)
                ]),
                buddies[THIEF] && NOP, // Don't lose our buddy!

                // Random walk until we can make a buddy or find the victim
                grab_nearby_food,
                foe_near() ? go_anywhere : fast_diagonal.bind(null, SAFE_COLOURS),
                go_anywhere
            ])
        } else if(me.type === THIEF) {
            return best_of([
                // Lost the queen! Random walk because we have nothing better to do.
                // (don't leave lines; they could disrupt the pattern)
                !buddies[QUEEN] && go_anywhere,
                buddies[BOUNCER] && go_anywhere,
                buddies[THIEF] > 1 && go_anywhere, //untested
                // Follow queen in search of victims
                try_all_angles.bind(null, [
                    (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
                    (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null)
                ]),
                NOP // Don't lose our buddy!
            ])
        }
    }
}

var victims = [highway, rail_miners, windmill]

function guess_environment(colours, buddies) {
    var food = view[4].ant.food
    if(view[4].ant.type !== QUEEN) {
        for(var i = 0; i < 9; i++) {
            if(i != 4 && view[i].ant && view[i].ant.friend && view[i].ant.type === QUEEN) {
                food = view[i].ant.food
            }
        }
    }
    for(var i = 0; i < victims.length; ++ i) {
        if(victims[i].likely_nest(colours) && victims[i].worth_leeching(food, buddies)) {
            return victims[i]
        }
    }

    return wilderness
}

function is_safe(i) {
    var nearThief = false
    var nearOfficer = false
    for(var j = 0; j < 9; ++ j) {
        if(friend_at(j) === THIEF) {
            nearThief = true
        }
        if(foe_at(j) && foe_at(j) !== QUEEN) {
            nearOfficer = true
        }
    }
    return nearThief && !nearOfficer
}

function move(me, buddies) {
    var colours = [0, 0, 0, 0, 0, 0, 0, 0, 0]
    for(var i = 0; i < 9; ++ i) {
        ++ colours[view[i].color]
    }
    var env = guess_environment(colours,buddies)
    var victim_pos = -1
    var queen_pos = -1
    for(var i = 0; i < 9; ++ i) {
        if(foe_at(i) === QUEEN && env.likely_victim(i) && view[i].ant.food > 0) {
            victim_pos = i
            if(view[i].ant.food > 0) {
                break
            }
        }
        if(friend_at(i) === QUEEN) {
            queen_pos = i
        }
    }

    var in_void = true
    for(var i = 0; i < 9; ++ i) {
        if(view[i].color !== WHITE || (i != 4 && me.type === BOUNCER && friend_at(i) === BOUNCER)) {
            in_void = false
            break
        }
    }
    if(me.type === BOUNCER) {
        if(env === wilderness && in_void) {
            // Our work is done; leave queen and wander at random
            if(buddies[QUEEN]) {
                return best_of([
                    try_all_cells((i) => (ant_type_near(i, true) ? null : {cell: i}), true),
                    go_anywhere
                ])
            }
            return NOP
        }
        else if(env === rail_miners) {
            // Our work is done; leave queen and wander at random
            if(buddies[QUEEN]) {
                var allAngles = try_all_angles.bind(null, [
                    (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
                    (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null),
                    NOP
                ])
                return best_of([
                    //if next to an enemy queen, move out of the way
                    try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:9-i} : null), true),
                    try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:7-i} : null), true),
                    allAngles
                ])
            }
            return NOP
        } else if(buddies[QUEEN]) {
            // Escort queen out of nest
            var allAngles = try_all_angles.bind(null, [
                (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[0]} : null),
                (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[3]} : null),
                NOP
            ])

            return best_of([
                //if next to an enemy queen, move out of the way
                try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:9-i} : null), true),
                try_all_cells((i) => (foe_at(i) == QUEEN ? {cell:7-i} : null), true),
                allAngles
            ])
        }
        else {
            return go_anywhere
        }
    } else if(buddies[BOUNCER]) {
        if(me.type === QUEEN) {
            // Be escorted out of nest
            return try_all_angles.bind(null, [
                (rot) => (friend_at(rot[1]) === BOUNCER ? {cell: rot[2]} : null),
                (rot) => (friend_at(rot[0]) === BOUNCER ? {cell: rot[1]} : null),
                go_anywhere,
                NOP
            ])
        } else {
            // Get out of the way
            return try_all_angles.bind(null, [
                (rot) => (friend_at(rot[1]) === QUEEN ? {cell: rot[7]} : null),
                (rot) => (friend_at(rot[0]) === QUEEN ? {cell: rot[8]} : null),
                (rot) => (friend_at(rot[1]) === BOUNCER ? {cell: rot[7]} : null),
                (rot) => (friend_at(rot[0]) === BOUNCER ? {cell: rot[8]} : null),
                go_anywhere
            ])
        }
    }
    if(victim_pos !== -1) {
        // abandon the queen if she's dry.
        // abandon rail miner's queen so she has at least 10 food (otherwise she produces workers 3:4 food she aquires)
        // value is higher than 10 because there's two to three rounds of theft (at 4 ants each) before the queen gets out of range
        // this can still leave the rail miner's queen lower than 10, but unlikely
        // other queens are abandoned if they have less than 5 food, due to the "max 4 ants stealing" and at 0 food, she's not a target.
        if(view[victim_pos].ant.food < 5 || (env == rail_miners && view[victim_pos].ant.food < 28)) {
            if(me.type == THIEF) {
                if(rail_miners.near_nest(colours)) {
                    // we'd rather reuse the workers
                    return NOP
                }
            }
            // Victim is out of food; bounce out of nest
            if(env == rail_miners) {
                if(me.type == QUEEN && me.food < 300 && !buddies[BOUNCER]) {
                    if(friend_at(deRotate(victim_pos,2)) == THIEF && foe_at(deRotate(victim_pos,3)) == 0) return {cell:deRotate(victim_pos,3),type:BOUNCER}
                    if(friend_at(deRotate(victim_pos,-2)) == THIEF && foe_at(deRotate(victim_pos,-3)) == 0) return {cell:deRotate(victim_pos,-3),type:BOUNCER}
                }
                // murder SlM
                return NOP
            }
            var m = try_all_cells((i) => ({cell: i, type: BOUNCER}), true)
            if(m) {
                return m
            }
        }
        if(me.type === QUEEN && buddies[THIEF] && !is_safe(CENTRE)) {
            // Try to avoid getting food stolen back from us
            var m = try_all_cells((i) => (is_safe(i) ? {cell: i} : null), true)
            if(m) {
                return m
            }
        }
        return env.follow_victim(me, buddies, colours, victim_pos)
    } else {
        return env.find_victim(me, buddies, colours)
    }
}

// LANCE is only used by windmill targetting, easier to break this out as its own method
function moveLance(me, buddies) {
    var queenPos = -1
    var tipPos = -1
    var enQueenPos = -1
    if(buddies[BOUNCER]) {
        for(var i=0;i<9;i++) {
            if(friend_at(i) == BOUNCER) {
                return {cell:8-i}
            }
        }
    }
    for(var i=0;i<9;i++) {
        if(friend_at(i) == QUEEN) {
            queenPos = i
        }
        if(friend_at(i) == LANCE_TIP) {
            tipPos = i
        }
        if(foe_at(i) == QUEEN) enQueenPos = i
    }
    if(!buddies[QUEEN]) {
        for(var i=0;i<9;i++) {
            if(i % 2 == 0 && friend_at(i) == QUEEN) {
                if(view[deRotate(i,3)].ant != null && view[deRotate(i,3)].ant.friend && view[deRotate(i,3)].ant.type == LANCE_TIP) return NOP
                return {cell:deRotate(i,1)}
            }
        }
        if(!buddies[LANCE_TIP] && !buddies[THIEF] && view[4].color == 2) {
            for(var i = 0; i < 9; ++ i) {
                if(view[i].color == 1) return {cell:i}
            }
        }
        if(enQueenPos >= 0 && enQueenPos % 2 == 0 && foe_at(deRotate(enQueenPos,1)) == 1) {
            return {cell:deRotate(enQueenPos,-3)}
        }
        if(enQueenPos >= 0 && enQueenPos % 2 == 1 && foe_at(deRotate(enQueenPos,2)) == 1) {
            return {cell:8-enQueenPos}
        }
        if(enQueenPos >= 0 && (me.food > 0 || foe_at(deRotate(enQueenPos,-1)) || foe_at(deRotate(enQueenPos,3)))) {
            if(enQueenPos % 2 == 0 && (foe_at(deRotate(enQueenPos,4)) || friend_at(deRotate(enQueenPos,4)) == THIEF)) {
                return {cell:deRotate(enQueenPos,-3)}
            }
        }
        return NOP
    }
    if(buddies[LANCE_TIP]) {
        if(deRotate(queenPos,-1) == tipPos && view[tipPos].color == 8) return {cell:8-tipPos}
        if(deRotate(queenPos,-1) == tipPos) return try_all_cells((i) => (areAdjacent(i,tipPos) && view[i].color == 5 ? {cell:i} : null))
        if(foe_at(8-tipPos) == QUEEN) return {cell:8-tipPos,color:6}
        if(foe_at(8-queenPos) > 0 || foe_at(deRotate(8-queenPos,1)) > 0) return NOP
        return try_all_cells((i) => (!areAdjacent(i,queenPos) && !areAdjacent(i,tipPos) ? {cell:i} : null))
    }
    if(view[4].color != 4 && view[4].color != 6) {
        if(foe_at(8-queenPos) == QUEEN) {
            var formation = try_all_angles.bind(null, [
                (rot) => (foe_at(rot[1]) === 1 && foe_at(rot[2]) === QUEEN ? {cell: rot[3]} : null),
                (rot) => (foe_at(rot[1]) === 1 && foe_at(rot[0]) === QUEEN ? {cell: rot[7]} : null),
                (rot) => (foe_at(rot[1]) === 1 && view[rot[1]].ant.food > 0 && foe_at(rot[6]) === QUEEN && friend_at(rot[2]) === QUEEN ? {cell: rot[5]} : null),
            ])()
            if(formation != null) {
                return formation
            }
            return NOP
        }
        if(foe_at(deRotate(queenPos,1)) > 0 && foe_at(deRotate(queenPos,-1)) > 0) {
            return {cell:deRotate(queenPos,-3)}
        }
        return best_of([
            try_all_cells((i) => (enQueenPos ==-1 && i % 2 == 1 && (view[i].color == 4 || view[i].color == 6) && view[deRotate(i,1)].color != 2 && view[deRotate(i,-1)].color != 2 && areAdjacent(i,queenPos) ? {cell: i} : null), true),
            ((view[4].color != 6 || view[4].color != 4) && queenPos % 2 == 0 && view[deRotate(queenPos,-3)].color == 5) ? {cell:4,color:6} : null,
            NOP
        ])
    }
    else {
        var queenOn = view[8-queenPos].color
        var target = WIND_BAND.next(queenOn)
        var prior = WIND_BAND.next(target)
        var followRail = best_of([
            try_all_cells((i) => (view[deRotate(i,-3)].color == prior && view[deRotate(i,-1)].color == target && areAdjacent(i,queenPos) && (view[i].color == 4 || view[i].color == 6) ? {cell: i} : null), true),
            queenPos % 2 == 1 ? (view[queenPos].color == 4 || view[4].color == 4 ? NOP : {cell:deRotate(queenPos,-2)}) : (view[queenPos].color == 4 || view[queenPos].color == 6 ? {cell:deRotate(queenPos,-1)} : NOP)
        ])

        if(view[deRotate(queenPos,-1)].ant != null) {
            if(!view[deRotate(queenPos,-1)].ant.friend && view[deRotate(queenPos,-2)].ant != null && !view[deRotate(queenPos,-2)].ant.friend) {
                return NOP
            }
            if(queenPos % 2 == 0 && !view[deRotate(queenPos,-1)].ant.friend && view[deRotate(queenPos,-2)].ant == null && (view[queenPos].color == 3 || view[queenPos].color == WHITE)) {
                return {cell:deRotate(queenPos,-3)}
            }
            if(queenPos % 2 == 0 && friend_at(deRotate(queenPos,-1)) == THIEF && view[deRotate(queenPos,-2)].ant == null && (view[queenPos].color == 3 || view[queenPos].color == WHITE)) {
                return {cell:deRotate(queenPos,-3)}
            }
            return NOP
        }
        if(me.food > 0 && queenPos % 2 == 0) {
            return {cell:deRotate(queenPos,-1)}
        }
        if(foe_at(deRotate(queenPos,-3)) > 0 && (view[queenPos].color == 1 || view[deRotate(queenPos,1)].color == 1 || view[deRotate(queenPos,-1)].color == 1)) {
            if(view[queenPos].color == 3) {
                return followRail
            }
            if(view[queenPos].color == 7) return NOP
            return {cell:queenPos,color:3}
        }
        if((foe_at(deRotate(queenPos,-2)) > 0 || foe_at(deRotate(queenPos,-3)) > 0) && queenPos % 2 == 0 && view[deRotate(queenPos,-1)].color == 5) {
            if(view[queenPos].color == 7) return NOP
            return {cell:queenPos,color:3}
        }
        if(view[deRotate(queenPos,-4)].ant != null && !view[deRotate(queenPos,-4)].ant.friend && (view[queenPos].color == 1 || view[deRotate(queenPos,1)].color == 1 || view[deRotate(queenPos,-1)].color == 1)) {
            if(view[queenPos].color == 7) return NOP
            return {cell:queenPos,color:3}
        }
        if((followRail == null || followRail.cell == 4) && foe_at(deRotate(queenPos,-2)) == 1) {
            if(view[queenPos].color == 7) return NOP
            return {cell:queenPos,color:3}
        }
        if(followRail != null && followRail.cell != 4 && view[followRail.cell].color == 6 && view[deRotate(followRail.cell,1)].color == 6) {
            followRail = {cell:deRotate(followRail.cell,1)}
        }
        return followRail
    }
    return NOP
}

// LANCE_TIP never needs to move
// Unfortunately, reusing an existing worker type for this purpose is not easily possible.
// Used against Sliding Miners as a stationary blocker to prevent the queen slipping past.
function moveTip(me, buddies) {
    var queenPos = -1
    var in_void = true
    for(var i=0;i<9;i++) {
        if(friend_at(i) == QUEEN) {
            queenPos = i
        }
        if(view[i].color != WHITE) {
            in_void = false
        }
    }
    var colours = [0, 0, 0, 0, 0, 0, 0, 0, 0]
    var enemies = 0
    for(var i = 0; i < 9; ++ i) {
        ++ colours[view[i].color]
        if(foe_at(i) > 0) enemies++
    }
    var onRails = rail_miners.near_nest(colours)
    if(buddies[QUEEN] && !buddies[LANCE]) {
        if(onRails) return NOP
        if(foe_at(8-queenPos) == 4) {
            return {cell:deRotate(queenPos,2)}
        }
        if(in_void) return {cell:deRotate(queenPos,4)}
        if(enemies == 2 && queenPos % 2 == 0 && view[deRotate(queenPos,1)].ant == null) {
            return {cell:queenPos,color:7}
        }
        if(enemies == 2 && queenPos % 2 == 1) {
            return {cell:deRotate(queenPos,-1),color:7}
        }
    }
    if(buddies[QUEEN] && buddies[LANCE]) {
        if(enemies == 0 && view[queenPos].color == 1) return NOP
        if(view[deRotate(queenPos,1)].color == 5 && friend_at(deRotate(queenPos,1)) == LANCE) return {cell:deRotate(queenPos,4)}
        return {cell:deRotate(queenPos,2)}
    }
    if(!buddies[QUEEN] && view[4].color == 2) {
        for(var i = 0; i < 9; ++ i) {
            if(view[i].color == 8) return {cell:i}
        }
    }
    if(queenPos >=0 && foe_at(deRotate(queenPos,2)) > 0 && view[deRotate(queenPos,2)].ant.food == 0 && foe_at(deRotate(queenPos,4)) > 0 && view[deRotate(queenPos,4)].ant.food > 0) {
        return {cell:queenPos,color:7}
    }
    return NOP
}

function deRotate(m, amt) {
    var rotationsCW = [1,2,5,8,7,6,3,0]
    var rotationsCCW = [3,6,7,8,5,2,1,0]
    if(m == 4 || m < 0 || m > 8 || amt == 0) return m
    if(amt > 0)
        return rotationsCW[(rotationsCW.indexOf(m)+amt)%8]
    amt = -amt
    return rotationsCCW[(rotationsCCW.indexOf(m)+amt)%8]
}

return play_safe(move_agent([
    THIEF, move,
    QUEEN, move,
    BOUNCER, move,
    LANCE, moveLance,
    LANCE_TIP, moveTip
]))

Tidak ada yang memposting jawaban yang mengeksploitasi aturan "Seorang pekerja yang tidak berkebangsaan yang berdekatan dengan seorang ratu musuh akan mencuri 1 potong makanan, jika ada.", Jadi saya memutuskan untuk memperbaikinya!

Vampir ini mencari target khusus untuk lintah, yang terus diperbarui (karena ini adalah Wiki Komunitas), dan kadang-kadang dinonaktifkan (melalui worth_leechingfungsi) ketika mereka tidak lagi cukup menguntungkan.

Perhatikan bahwa worth_leechingfungsi yang disebutkan, selain penggunaannya dalam menonaktifkan target (jika mengembalikan false), memungkinkan untuk mengontrol objek penargetan yang diberikan untuk memaksimalkan pengambilan keputusan, misalnya mencegah Vampir untuk mengejar kemungkinan jebakan (Ziggurat membuat kantong kecil yang tidak memiliki Ratu di tengah) sampai Vampir telah mengumpulkan jumlah makanan yang cukup yang terjebak tidak akan benar-benar membuang-buang putaran (yaitu hanya skor 12 makanan). Parameter yang dilewatkan membuat metode ini sangat fleksibel tanpa harus menanyakan viewobjek.

Deskripsi target mengikuti, sesuai dengan urutan yang muncul dalam kode:

Lubang Hitam (target dinonaktifkan)

Ketika ini menemukan Black Hole, itu cukup acak apakah kita akhirnya akan menemukan ratu mereka, atau pekerja mereka akan mengeringkan makanan kita sendiri, atau kita akan bangkit dan melanjutkan pencarian. Ternyata Black Hole cukup sulit untuk ditargetkan.

Ziggurat (target dinonaktifkan)

Begitu sampai di pusat Ziggurat, sang ratu memunculkan pencuri untuk mengekstrak makanan sebanyak mungkin secepat mungkin. Itu harus cepat karena koloni sekarang membela kita dengan menelurkan pasukan pekerja (menghabiskan makanan mereka sendiri). Setelah semua makanan hilang, kami membuat "tukang pukul" dan mengantar diri kami keluar, mencari korban berikutnya.

Untuk Black Hole, ini menggunakan pekerja yang duduk di permukaan untuk menunggu ratu target lewat, sementara ratu kita duduk lebih dalam sehingga pekerja yang lewat tidak akan merampoknya. Ketika target queen akhirnya lewat, kami mengikutinya sebanyak mungkin.

Untuk Penambang di rel, ia menggunakan pencarian brute-force untuk melihat apakah ratu duduk di tengah sistem rel utama penambang. Kemudian mengikutinya kembali ke sumbernya (membiarkan pekerja penambang melangkah). Sekali di ratu itu menumbuhkan pencuri seperti biasa dan menghabiskan ratu sampai 9 makanan. Di bawah 10 makanan, ratu penambang menghasilkan 3 pekerja untuk setiap 4 makanan yang diberikan kepadanya, membuat tetap bertahan dengan prospek yang rendah. Bangkit keluar dan kembali lagi nanti. (Terima kasih pergi ke Draco18s untuk menyediakan kode lingkungan ini!)

Ketika ini menemukan Ziggurat, itu cukup baik, dan menyebabkan ledakan menarik, seperti yang terlihat dalam prototipe awal ini (sebelum memantul ditambahkan):

ziggurat meledak

FireFly (target dinonaktifkan)

Blok yang menargetkan Firefly menggunakan kode Ziggurat sebagai garis dasar, tetapi mengabaikan warna yang digunakan. Ini memudahkan setiap penulis untuk membuat target Vampir baru dengan mudah sambil menjaga byte-count rendah. Kode Firefly dinonaktifkan ketika Firefly tidak mengambil makanan, tetapi diaktifkan kembali di Mk. 5.

Tidak terkait dengan FIrefly, tetapi Mk.4 juga menambahkan dalam beberapa metode objek tambahan untuk menangani side-stepping pekerja target (digunakan oleh kode anti-rail-miners) yang memungkinkan ratu Vampire untuk secara aman menemukan reparasi penambang kereta yang bekerja, sebagai gantinya menyebabkan dua semut macet secara permanen.

Miners on a Rail (MoaR) / Sliding Minders (SlM)

Ketika ini menemukan Penambang di Rail, itu melacak rel ke arah outbound (arah yang sama bergerak rel pekerja unladen) untuk menghindari makanannya dicuri. Ketika menemukan ujung rel, itu bertindak seperti Penambang pada pekerja Rail sendiri 4 (tukang reparasi). Kita tahu di mana rel itu, mungkin juga mengikuti arah itu dan kembali untuk memulai!

Tetapi hasil yang paling umum adalah bahwa kita tidak pernah menemukan apa pun. Ini masih OK dalam hal itu; sekitar tengah papan skor.

Kincir angin

Pengembangan kode Don Quixote (penargetan kincir angin) (dalam Mr. 5) penuh dengan tantangan dan melewati banyak iterasi, termasuk yang akan menjadi tipe Semut baru) sebelum strategi yang dapat diterima dikembangkan. Ketika sang ratu mengetahui bahwa dia berdiri di rel tengah lengan utama Windmill, dia akan melahirkan pekerja jenis baru (diberi kode nama LANCE) untuk berlari di sisi rel untuk menghindari pekerja musuh (seolah-olah pekerja musuh melihat vampir). ratu, mereka akan berusaha untuk menemui jalan buntu), sampai akhirnya bertemu dengan ratu Kincir Angin.

Dua pendekatan untuk ratu relatif jelas dan kita dapat mencoba untuk mengandalkan Lance yang datang pertama (ratu Vampir akan mengamati pekerjanya memegang makanan) dan menggunakan saat itu untuk menelurkan dalam THIEFdaripada melangkah dan membiarkan ratu Kincir Angin menelurkan seorang pekerja berdekatan dengan Ratu Vampir (menghasilkan aliran netto nol makanan). Pendekatan ketiga berakhir berlari ke tukang kebun, sehingga ratu melahirkan seorang LANCE_TIPpekerja untuk melakukan beberapa manuver politik untuk berakhir dengan aliran makanan bersih-positif.

Serangan-serangan ini datang dengan mengorbankan ketidakmampuan untuk menentukan berapa banyak makanan yang dimiliki oleh ratu Kincir Angin dan karenanya tidak dapat memprediksi kapan akan muncul BOUNCERdan pergi untuk korban yang lebih baik. Namun, ini bekerja untuk Vampir karena Windmill adalah sapi yang gemuk dan berair sehingga lebih berharga untuk bertahan sampai akhir: Windmill dapat dengan mudah mengumpulkan beberapa ratus makanan hanya dalam beberapa ribu gerakan, jauh lebih banyak daripada yang bisa diperoleh Vampir dengan mencoba menemukan target baru untuk lintah.

Pembaruan Garlicked (Mk6) mengubah cara ratu sedikit mendekati. Pendekatan rel identik, tetapi setelah mencapai ratu, ada beberapa perubahan kecil, yang menghasilkan hanya satu pekerja yang berdekatan dengan ratu Kincir Angin, menjaga ratu Vampir sejauh mungkin (untuk menghindari pekerja Kincir Angin, dan yang paling penting , bawang putih). Perubahan itu karena kenyataan bahwa ketika dua pekerja berdekatan dengan ratu Kincir Angin, dia akan mengemas tasnya dan pindah.

Energi Terbarukan (Mk 7) merevisi logika penargetan Kincir Angin, sebagian besar membatalkan perubahan Mk 6, karena ada perubahan pada Windmill yang menyebabkan kode gagal untuk lintah 100% dari waktu. Tingkat keberhasilan yang direvisi adalah sekitar 70%, sisanya 30% adalah karena keberuntungan bodoh yang tidak dapat dihindari. Vektor pendekatan sedikit diubah.

Jalan raya

Jalan raya mudah ditemukan. Ini adalah gumpalan kehancuran kehijauan yang terus berkembang. Jadi, menunggu untuk menemukan dan menghancurkan mimpi para Penambang dan Kincir Angin terlebih dahulu sebelum pergi setelah Highway berarti bahwa Vampir tidak menargetkan terlalu cepat.

Menemukan ratu itu sederhana: temukan pusat jalan raya dan tunggu. Dia akhirnya akan datang. Pengaturan pekerja akan menentukan apakah dia akan dihentikan atau tidak atau kita harus menunggu satu putaran lagi, tetapi penundaan itu tidak membuat perbedaan besar: pada akhirnya kita akan menangkapnya dan mencuri SEMUA makanannya.

Jika Vampir memiliki 1000 makanan atau lebih, ia mengabaikan Highway, karena pada saat itu, tidak masalah lagi.


1
@trichoplax tidak masalah; ketika saya mendapat waktu untuk melihatnya lagi, saya akan melihat apa yang dapat saya tingkatkan sekarang karena pekerja itu tidak perlu bergoyang-goyang. Adapun berapa lama sampai ini dibuat, saya pikir hal utama sedang menunggu lawan untuk mudah ditemukan!
Dave

9
ini sangat berarti.
Lemon Destructible

2
trichoplax secara eksplisit memungkinkan strategi kelemahan-eksploitasi tersebut
pppery

1
Saya menaikkan suara "ini benar-benar kejam" karena saya setuju, dan itu adalah salah satu hal kejam yang saya ingin lihat secara khusus dalam kontes ini ...
trichoplax

1
Ya. Sangat berarti. Jika itu penghiburan: itu hampir tidak pernah berhasil dalam permainan nyata (dengan pemain lain selain ini & korbannya), karena penghapus garis terus menggerakkan pola target.
Dave

11

Semut Langton

Semua jawaban saya akan berisi logika tingkat rendah yang serupa dalam bentuk Kerangka Fungsi Formik. "TINGKAT LOGIKA TINGGI MULAI DI SINI" menandai akhir kode Kerangka ini.

// FORMIC FUNCTIONS FRAMEWORK //
// Version 1.0                //

var WHITE = 1;
var QUEEN = 5;
var HERE = 4;
var MY_VO = view[HERE];
var ME = MY_VO.ant;
var NOP = move(HERE);

var ORTHOGONALS = [1, 3, 5, 7];
var DIAGONALS = [0, 2, 6, 8];
var DIRECTIONS = [0, 1, 2, 3, 5, 6, 7, 8];
var ALL_CELLS = [0, 1, 2, 3, 4, 5, 6, 7, 8];
var VIEW_ORIENTATIONS = [
  [0,1,2,
   3,4,5,
   6,7,8],

  [6,3,0,
   7,4,1,
   8,5,2],

  [8,7,6,
   5,4,3,
   2,1,0],

  [2,5,8,
   1,4,7,
   0,3,6]
];

function rotateCW(cell, amount) {
  if (cell === HERE) return cell;
  var order = [0, 1, 2, 5, 8, 7, 6, 3];
  return order[(order.indexOf(cell) + amount + 8) % 8];
}

function isDiagonal(cell) {
  return DIAGONALS.includes(cell);
}
function isOrthogonal(cell) {
  return ORTHOGONALS.includes(cell);
}

function move(cell) {
  return {cell: cell};
}
function moveMany(cells) {
  var p = [];
  for (var i = 0; i < cells.length; i++) p.push(move(cells[i]));
  return p;
}

function color(cell, col) {
  return {cell: cell, color: col};
}
function colorMany(cells, col) {
  var p = [];
  for (var i = 0; i < cells.length; i++) p.push(color(cells[i], col));
  return p;
}

function spawn(cell, type) {
  return {cell: cell, type: type};
}
function spawnMany(cells, type) {
  var p = [];
  for (var i = 0; i < cells.length; i++) p.push(spawn(cells[i], type));
  return p;
}

function isSane(action, ant) {
  // TODO: Minimize this
  if (ant === undefined || !ant.isObject) ant = ME;
  if (action === undefined || action.cell < 0 || action.cell >= 9) return false;
  if (action.color !== undefined) {
    if (action.color < 1 || action.color > 8) return false;
    return true;
  }
  else if (action.type !== undefined) {
    if (action.type < 1 || action.type > 4) return false;
    if (ant.type !== QUEEN || ant.food === 0) return false;
    if (isOccupied(action.cell, ant) || view[action.cell].food !== 0) return false;
    return true;
  }
  else {
    if (isOccupied(action.cell, ant) && action.cell !== HERE) return false;
    return true;
  }
}
function isOccupied(cell, ant) {
  if (ant === undefined || !ant.isObject) ant = ME;
  return view[cell].ant !== null || (view[cell].food > 0 && ant.type !== QUEEN && ant.food === 1);
}
function isColoringMeaningful(action) {
  return isSane(action) && action.color !== undefined && view[action.cell].color !== action.color;
}

function test(cell, test) {
  var vo = view[cell];
  return (test.color === undefined || test.color === vo.color) &&
         (test.food === undefined || test.food === vo.food) &&
         (test.ant === undefined || test.ant === vo.ant || (
           (test.ant !== null && vo.ant !== null) &&
           (test.ant.food === undefined || test.ant.food === vo.ant.food) &&
           (test.ant.type === undefined || test.ant.type === vo.ant.type) &&
           (test.ant.friend === undefined || test.ant.friend === vo.ant.friend)
         ));
}

function findOrientation(tests) {
  var best = {orientation: null, matches: []};
  for (var o = 0; o < VIEW_ORIENTATIONS.length; o++) {
    var matches = [];
    for (var i = 0; i < tests.length; i++) {
      if (test(VIEW_ORIENTATIONS[o][tests[i].cell], tests[i])) {
        matches.push(tests[i]);
      }
    }
    if (matches.length > best.matches.length) {
      best.orientation = o;
      best.matches = matches;
    }
  }

  return best;
}

function orientCells(orientation, cells) {
  if (orientation === null || orientation < 0 || orientation >= 4) orientation = 0;
  for (var i = 0; i < cells.length; i++) {
    cells[i] = VIEW_ORIENTATIONS[orientation][cells[i]];
  }
  return cells;
}

function findFirst(func, cells) {
  if (cells === undefined) cells = ALL_CELLS;
  for (var i = 0; i < cells.length; i++) {
    if (func(cells[i])) return cells[i];
  }
  return null;
}
function findAll(func, cells) {
  if (cells === undefined) cells = ALL_CELLS;
  var found = [];
  for (var i = 0; i < cells.length; i++) {
    if (func(cells[i])) found.push(cells[i]);
  }
  return found;
}

// HIGH-LEVEL LOGIC STARTS HERE //
var ROAD_COL = 3;
var SIM_COLS = [ 1, 8, 5, 6, 4, 2, 7, 3]; // SIM_COLS.length must be greater or equal to SIM_ROTS.length
var SIM_ROTS = [-1,-1,+1,-1,-1,+1,-1,-1];
// Here are some additional rulesets to play around with:
// [+1,-1,+1,-1,+1,-1,+1,-1] // Classic Langton's ant, extended to use all 8 colors cyclically
// [+1,+1,-1,+1,-1,+1,+1]    // Produces a very interesting highway
// [-1,-1,+1,-1,-1,+1,-1,-1] // The default one -- chosen because it produces a highway nearly instantly, and the highway itself is pretty efficient
// [-1,+1,-1,-1,-1,+1,+1,-1]
// [-1,-1,+1,-1,-1,+1,-1,-1]
// [-1,+1,-1,-1,-1,+1,+1,-1]
// [+1,-1,+1,+1,+1,-1]

var ANCHOR = 1;
var TAIL = 2;

var DEBUG_MODE = false;

function getSimColIndex(cell) {
  return Math.max(SIM_COLS.lastIndexOf(view[cell].color, SIM_ROTS.length - 1), 0);
}
function getNextSimCol(simColIndex) {
  return SIM_COLS[(simColIndex + 1) % SIM_ROTS.length];
}
function getSimRot(simColIndex) {
  return SIM_ROTS[simColIndex];
}

function run() {
  switch (ME.type) {
    case QUEEN: {
      var anch = findFirst(c => test(c, {ant: {type: ANCHOR, friend: true}}), DIRECTIONS);
      if (anch !== null) {
        if (isOrthogonal(anch)) {
          var tail = findFirst(c => test(c, {ant: {type: TAIL, friend: true}}), DIAGONALS);
          if (tail !== null) {
            return [move(rotateCW(anch, -2 * getSimRot(getSimColIndex(HERE))))].filter(isSane)[0] || NOP;
          } else {
            var nanch = rotateCW(anch, -getSimRot(getSimColIndex(anch)));
            return color(nanch, getNextSimCol(getSimColIndex(nanch)));
          }
        } else {
          return NOP;
        }
      } else {
        var tailO;
        if (ME.food === 2) {
          return spawnMany(ORTHOGONALS, TAIL).filter(isSane)[0] || NOP;
        } else if (ME.food === 1 && (tailO = findOrientation([{cell: 1, ant: {type: TAIL, friend: true}}])).orientation !== null) {
          return spawnMany(orientCells(tailO.orientation, [3, 5]), ANCHOR).filter(isSane)[0] || NOP;
        }

        var f;
        if ((f = findFirst(c => test(c, {food: 1})))) {
          return move(f);
        }

        if (test(HERE, {color: ROAD_COL})) {
          return moveMany(orientCells(findOrientation([{cell: 0, color: ROAD_COL}]).orientation, [8, 6, 2])).filter(a => isSane(a) && !test(a.cell, {color: ROAD_COL}))[0] || moveMany(DIAGONALS).filter(isSane)[0] || NOP;
        } else {
          return color(HERE, ROAD_COL);
        }
      }
      break;
    }
    case ANCHOR: {
      var queen = findFirst(c => test(c, {ant: {type: QUEEN, friend: true}}), DIAGONALS);
      var tail = findFirst(c => test(c, {ant: {type: TAIL, friend: true}}), ORTHOGONALS);
      var qp = [rotateCW(queen, -1), rotateCW(queen, +1)];
      var tp = [rotateCW(tail, -2), rotateCW(tail, +2)];
      for (var i = 0; i < qp.length; i++) {
        for (var j = 0; j < tp.length; j++) {
          if (qp[i] === tp[j]) return [move(qp[i])].filter(isSane)[0] || NOP;
        }
      }
      return NOP;
      break;
    }
    case TAIL: {
      var anch = findFirst(c => test(c, {ant: {type: ANCHOR, friend: true}}), DIAGONALS);
      if (anch !== null) {
        var rot = getSimRot(getSimColIndex(anch));
        var nanch = rotateCW(anch, rot);
        if (test(nanch, {ant: {type: QUEEN, friend: true}})) return [move(rotateCW(anch, -rot))].filter(isSane)[0] || NOP;
        return [move(nanch)].filter(isSane)[0] || NOP;
      } else {
        return NOP;
      }
      break;
    }
  }
}

var output = run();
if (isSane(output)) return output;
else {
  if (DEBUG_MODE) {
    return;
  } else {
    return NOP;
  }
}

Langton's ant ("RL") Langton's ant ("LLRLLRLL")


pengantar

Ini adalah implementasi dari semut Langton dalam batas-batas tantangan Formic Functions QOTH. Sebagai bonus tambahan, ini mendukung varian multi-warna semut Langton. Saya juga berhasil memanfaatkan palet penuh hingga 8 warna. Selanjutnya, semut ini menyelesaikan satu langkah simulasi dalam dua langkah waktu dalam game.

Sayangnya, varian semut Langton yang lebih menarik membutuhkan lebih dari 8 warna, jadi saya memilih yang efisien. Saya menulis sebuah program pelengkap untuk menemukan semut-semut yang mencakup tanah dengan jumlah terbesar dalam batas waktu tertentu, dan menemukan berbagai variasi aturan "RRL". Semuanya mencakup 4168 sel dalam 15000 langkah.

Sekarang, 4000 sel sangat buruk! Ini berarti bahwa, rata-rata, entri ini akan mengakhiri permainan dengan EMPAT makanan. Dan itu ada di papan kosong! Terus terang, Jig Brown lebih baik dari ini. Singkatnya, ini bukan pesaing serius ketika datang ke posisi di papan skor. Apa yang dapat dilakukannya, adalah mengacaukan entri lainnya. Dan itulah satu-satunya alasan mengapa saya repot-repot mengirimkan ini. Interaksi yang sangat kompleks sering muncul antara entri ini dan yang sangat bergantung pada warna. Dan mereka yang mengandalkan warna biasanya menderita.


Penjelasan

Warna ( SIM_COLS) memiliki rotasi yang sesuai ( SIM_ROTS) yang digunakan Ratu untuk mengubah arah yang dia tuju. +1berarti rotasi searah jarum jam ("R"), -1berarti rotasi berlawanan arah jarum jam ("L"). Semut klasik Langton akan menjadi SIM_COLS = [ 1, 8], SIM_ROTS = [+1,-1]. The SIM_COLSArray efektif membuang semua elemen yang tidak memiliki elemen yang cocok di SIM_ROTSberbagai. Semua warna yang tidak terkandung dalam SIM_COLSarray terpotong diperlakukan sebagai warna 0 dan tidak akan pernah diproduksi oleh semut itu sendiri.

Seperti semut lain yang layak, semut yang satu ini mulai dengan mengumpulkan cukup makanan sehingga dapat mulai melakukan sesuatu. Membutuhkan 2 makanan .

Setelah menyelesaikan perebutan awal, Ratu menelurkan dua semut, bernama Anchor and Tail . Sang Ratu memunculkan Ekor terlebih dahulu, berdekatan secara ortogonal (sel 1, 3, 5, 7) dengan sang Ratu. Kemudian, ia memunculkan Jangkar, berdekatan secara ortogonal dengan Ratu dan secara diagonal (sel 0, 2, 6, 8) ke Ekor.

Perilaku terdiri dari siklus yang selalu berulang dari dua langkah yang berbeda.

Langkah 1

Buntut: Pencarian untuk sel yang ditempati Ratu 2 langkah simulasi lalu dan pergi ke sana. Perhatikan bahwa ia menggunakan warna dari bawah Anchor dan keberadaan Queen (atau ketiadaan) untuk secara akurat menentukan sel target. Ini rawan bencana yang akan saya bahas di masa depan pembaruan.

Anchor: Tidak melakukan apa-apa.

Ratu: Menemukan Anchor dan memperlakukannya sebagai kebalikan dari arah semut. Arahnya kemudian diputar sesuai dengan warna di bawah Ratu. Ratu bergerak ke sel yang ditunjukkan oleh arah yang diputar.

Langkah 2

Ekor: Tidak melakukan apa-apa.

Anchor: Mencari sel yang berbatasan secara diagonal dengan Tail dan secara orthogonal berbatasan dengan Queen dan pergi ke sana. Paling banyak ada satu sel seperti itu.

Ratu: Sama halnya dengan Ekor di langkah 1, dia mencari sel yang dia tempati 2 langkah simulasi lalu dan mengubah warnanya dengan cara siklus. Perhatikan bahwa dia menggunakan warna dari bawah Anchor untuk menentukan secara akurat sel target. Bencana terburuk yang bisa terjadi adalah mewarnai sel yang salah, dan sementara itu akan mengubah lintasan semut, itu tidak benar-benar masalah karena lintasan akan diubah oleh kehadiran semut lain pula.

Menentukan apa yang harus dilakukan

Ratu menentukan langkah saat ini dengan mencari Ekor secara orthogonal. Jika dia menemukannya, dia ada di langkah 1. Kalau tidak, dia ada di langkah 2. Dalam kasus yang jarang terjadi, dia mungkin menemukan Anchor berdekatan diagonal untuk dirinya sendiri. Dalam hal itu, dia tidak akan melakukan apa pun.

Anchor tidak memiliki logika untuk menentukan langkahnya. Itu selalu berusaha untuk secara ortogonal berbatasan dengan Ratu dan berbatasan secara diagonal dengan Ekor. Jika tidak ada sel seperti itu, itu tidak bergerak.

Ekor menentukan langkah saat ini dengan mencari Anchor secara diagonal. Jika menemukannya, itu pada langkah 1. Jika tidak, itu pada langkah 2.

Bencana yang disebutkan sebelumnya selama pergerakan Ekor dapat dipicu oleh semut yang mengubah warna di bawah Jangkar. Jika warna yang diubah menunjukkan rotasi yang berbeda dari aslinya dan Ratu tidak melihat Tail, posisi yang dihasilkan setelah pergerakan Tail adalah garis lurus dari 3 semut. Saat ini saya sedang memperbaiki obat untuk situasi ini.


Meskipun ini jelas bukan pesaing serius untuk tempat pertama, itu pasti untuk membumbui hal-hal di arena dan menggeser meta sedikit ke arah semut tidak berwarna. Saya punya beberapa kiriman masakan (dan salah satunya terlihat sangat pedas), jadi harap beberapa entri lagi dari saya dalam beberapa minggu mendatang.


3
Selamat datang di PPCG!
Steadybox

10

Ziggurat v3.0

var clockwise = [1,2,5,0,4,8,3,6,7];
var opposite = [8,7,6,5,4,3,2,1,0];
var cyclic_cw = [0,1,2,5,8,7,6,3,0];
var worker_colors = [4,5,7,8];
var next_worker_color = [1,1,1,1,5,7,1,8,4];
var prev_worker_color = [1,1,1,1,8,4,1,5,7];
var diags = [0,2,6,8];
var orthos = [1,3,5,7];
var cleaning_color = 6;


// Borrowed from Medusa
function clean(move) {
    if (move["color"] == undefined) {
        if (view[move["cell"]].ant != null) {
            move = {cell: 4};
        }
        if (move["type"] == undefined) {
            if (view[move["cell"]].food == 1 && view[4].ant.type < 5 && view[4].ant.food > 0) {
                move = {cell: 4};
            }
        } else if (view[4].ant.type != 5 || view[4].ant.food == 0 || view[move["cell"]].food == 1) {
            move = {cell: 4};
        }
    }
    return move;
}

function worker_blank(cel) {
    return (worker_colors.indexOf(view[cel].color) < 0);
}

// Own status
var my_color = view[4].color;
var my_food = view[4].ant.food;
var my_type = view[4].ant.type;

// Random free cell
var free_cell = 4;
for (var cel = 0; cel < 9; cel++) {
    if (view[cel].ant == null) {
    free_cell = cel;
    }
}

// Check surroundings
var blanks = 0;
var outer_edge = 0;
var inner_edge = 0;
var next_edge = -1;
var prev_nonblank = -1;
var some_nonblank = -1;
var prev_cell = -1;
var next_cell = -1;
var food_cell = -1;
var nonblank_color = 0;
var cleaning_marker = -1;
var uniform = 1;
var low_type = -1;
var friend_workers = 0;
var guards = 0;
var enemies = 0;
var enemy_queens = 0;
var my_queen = -1;

if (!worker_blank(4)) {
    nonblank_color = view[4].color;
}
for (var ix = 0; ix < 8; ix++) {
    var cel = cyclic_cw[ix];
    var cel2 = cyclic_cw[ix+1];
    if (view[cel].food == 1) {
    food_cell = cel;
    }
    if (worker_blank(cel)) {
    blanks++;
    if (!worker_blank(cel2)) {
        if (worker_blank(4)) {
        outer_edge = 1;
        } else {
        inner_edge = 1;
        }
        next_edge = cel;
            prev_nonblank = cel2;
    }
    if (view[cel].color == cleaning_color) {
        cleaning_marker = cel;
    }
    } else {
    some_nonblank = cel;
    if (nonblank_color == 0) {
        nonblank_color = view[cel].color;
    } else if (view[cel].color != nonblank_color) {
        uniform = 0;
    }
    }
    if ((!worker_blank(4) && view[cel2].color == prev_worker_color[my_color] && view[cel2].ant == null) || (worker_blank(4) && !worker_blank(cel2))) {
    prev_cell = cel2;
    }
    if (!worker_blank(4) && view[cel2].color == next_worker_color[my_color] && view[cel2].ant == null) {
    next_cell = cel2;
    }
    if (view[cel].ant != null) {
    var the_ant = view[cel].ant;
    if (the_ant.friend) {
        if (low_type < 0 || the_ant.type < low_type) {
        low_type = the_ant.type;
        }
        if (the_ant.type == 4) {
        guards++;
        } else if (the_ant.type == 5) {
        my_queen = cel;
        } else {
        friend_workers++;
        }
    } else {
        enemies++;
        if (the_ant.type == 5) {
        enemy_queens++;
        }
    }
    }
}

// Queen before finding food (motile)
if (my_type == 5 && worker_blank(4)) {
    if (my_food > 1) {
    return {cell:4, color:worker_colors[1]};
    }
    if (food_cell >= 0 && my_color != 1) {
    return clean({cell:food_cell});
    }
    if (my_color == 2) {
    for (var ix = 0; ix < 4; ix++) {
        var cel = diags[ix];
        var oppo = opposite[cel];
        if (view[cel].color == 2) {
        if (view[oppo].color == 1 && worker_blank(oppo) && view[oppo].ant == null) {
            return clean({cell:oppo});
        }
                if (view[oppo].color != 2) {
            return {cell:oppo, color:1};
                }
        }
    }
        for (var ix = 0; ix < 4; ix++) {
        var cel = diags[ix];
        if (view[cel].color != 2) {
        return {cell:cel, color:2};
        }
    }
    }
    if (my_color == 1) {
    return {cell:4, color:2};
    }
    return clean({cell:free_cell});
}

// Queen after finding food (sessile)
if (my_type == 5) {
    for (var ix = 0; ix < 8; ix++) {
        cel = cyclic_cw[ix];
    if (worker_blank(cel)) {
            return {cell:cel, color:worker_colors[1]};
    }
    }

    if (my_color != worker_colors[0]) {
        if (my_food > 0) {
        return clean({cell:free_cell, type:1});
        } else {
            return {cell:4, color:worker_colors[0]};
        }
    }
    if (my_food > 0) {
    if (my_food > 3 && guards < 2) {
        return clean({cell:free_cell, type:4});
    }
    if (0 < low_type && low_type < 3) {
        return clean({cell:free_cell, type:(low_type + 1)});
    }
    }
    return {cell:4};
}

// Queen's guard

if (my_type == 4) {
    // Queen is a nbor
    if (my_queen >= 0) {
    if (enemy_queens > 0) {
        return {cell:4};
    }
    if (my_queen == 1) {
        return clean({cell:5});
    }
    return clean({cell:clockwise[my_queen]});
    }
    // Try to get to queen
    if (prev_cell >= 0) {
    return clean({cell:prev_cell});
    }
    // Wander
    return clean({cell:free_cell});
}

// Worker

// Create new ziggurat
if (blanks == 8 && cleaning_marker < 0 && my_color != cleaning_color) {
    if (worker_colors.indexOf(my_color) >= 0) {
    return {cell:free_cell, color:my_color};
    }
    return {cell:free_cell, color:worker_colors[0]};
}

var front = view[1].color;
if (!worker_blank(4) && !worker_blank(1) && my_color != front) {
    if (view[7].color == front || (view[6].color == front && view[8].color == front)) {
        return {cell:4, color:front};
    }
}

if (my_food == 0) {
    // Grab food
    if (food_cell >= 0 && (!worker_blank(4) || !worker_blank(food_cell))) {
        return clean({cell:food_cell});
    }

    // Clear marked uniform region
    if (my_color == cleaning_color && uniform) {
    if (blanks < 7) {
        return {cell:some_nonblank, color:1};
    } else if (blanks == 7) {
        return {cell:some_nonblank, color:cleaning_color};
    }
    }

    // Follow cleaning color
    if (blanks == 8) {
    if (cleaning_marker < 0 || my_color == cleaning_color) {
        return {cell:4, color:1};
    } else if (cleaning_marker >= 0) {
        return clean({cell:cleaning_marker});
    }
    }

    // Dive into uniform region
    if (blanks > 3 && worker_blank(4) && worker_blank(1) && !worker_blank(2) && view[2].color == view[5].color && view[2].color == view[8].color) {
    return clean({cell:2});
    }

    // Mark uniform region for clearing
    if (!worker_blank(4) && uniform && (blanks < 4 || (blanks < 7 && ((worker_blank(1) && worker_blank(7)) || (worker_blank(3) && worker_blank(5)))))) {
    return {cell:4, color:cleaning_color};
    }

    // Extend edge
    if (outer_edge) {
    var new_color = 0;
    var cl = clockwise[next_edge];
    var cl2 = clockwise[cl];
    if (!worker_blank(1) && view[7].color == view[1].color) {
        new_color = view[1].color;
    } else if (!worker_blank(3) && view[5].color == view[3].color) {
        new_color = view[3].color;
        } else if (!worker_blank(cl2) && view[cl].color == next_worker_color[view[cl2].color]) {
        if (cl > 1) {
        new_color = view[cl].color;
        } else {
        new_color = next_worker_color[view[cl].color];
        }
    } else if (!worker_blank(cl)) {
        new_color = next_worker_color[view[cl].color];
        } else if (prev_nonblank >= 0) {
            new_color = next_worker_color[prev_nonblank];
    }
    if (new_color == 0 && blanks < 2) {
        new_color = worker_colors[0];
    }
        if (new_color > 0) {
            return {cell:4, color:new_color};
        } else {
            return clean({cell:next_edge});
        }
    }

    // Escape from hole
    if (worker_blank(4) && blanks == 0) {
        return {cell:4, color:next_worker_color[nonblank_color]};
    }

    // Go outside or fill it
    if (inner_edge && next_edge >= 0) {
    if (friend_workers > 1) {
        return clean({cell:free_cell});
    }
        if (blanks == 4 && prev_nonblank >= 0) {
            return {cell:next_edge, color:next_worker_color[view[prev_nonblank].color]};
        }
    if (view[next_edge].ant == null) {
            return clean({cell:next_edge});
    }
    return {cell:4};
    }

    // Go toward border
    if (!worker_blank(4) && next_cell >= 0 && next_cell != 1) {
    return clean({cell:next_cell});
    }

    // Wander
    return clean({cell:free_cell});
}

if (my_food > 0) {
    // Take food to queen
    if ((prev_cell >= 0 && prev_cell != 1) || (prev_cell >= 0 && worker_blank(4))) {
    return clean({cell:prev_cell});
    }

    // Wander
    if (worker_blank(free_cell) && !worker_blank(4)) {
    return {cell:4};
    }
    return clean({cell:free_cell});
}

return clean({cell:free_cell});

Bagaimana itu bekerja

Semut ini merupakan kombinasi Black Hole dan Medusa . Sang ratu mulai dengan mencari makanan secara diagonal. Setelah memiliki dua unit makanan, menjadi sessile dan membuat dua pekerja. Para pekerja melingkari ratu dan membuat daerah bergaris yang tumbuh ("ziggurat") menggunakan empat warna. Ketika seorang pekerja menemukan makanan, ia mengikuti garis-garis dan membawanya ke ratu, yang duduk di tengah alun-alun. Jika pekerja itu adalah salah satu dari dua yang asli, ratu membuat pekerja baru; jika tidak, dia menyimpan makanan.

Komentar

Versi 2.0 menggunakan empat warna, yang diharapkan mencegah semut tersesat dengan mudah. Skema penciptaan pekerja juga telah diubah untuk pertama-tama menghasilkan banyak pekerja dan menumbuhkan daerah tersebut, kemudian menyimpan makanan untuk membuatnya ke papan peringkat. Ada juga lebih banyak cek terhadap langkah ilegal.

Versi 2.1 menampilkan peningkatan rutin konstruksi ziggurat. Pekerja kadang-kadang dapat memilih warna mereka dari dua opsi, yang membuat pola garis lebih acak, semoga menipu Vampir. Pekerja terkadang meletakkan dua warna sebelum bergerak, yang membuat ziggurat tumbuh lebih cepat. Ketika berada di dalam ziggurat, pekerja kadang-kadang membuat langkah acak untuk menghindari loop tak terbatas, dan mencoba untuk memperbaiki ketidakkonsistenan lokal tertentu. Sang ratu lebih pintar menghindari jalan di fase motilinya. Akhirnya, jika sessile queen diserang, dia melawan balik dengan menciptakan ledakan pekerja baru, yang juga akan membantunya pulih jika penyerang adalah penghapus jejak yang memotong dia dari semua pekerja yang ada.

Versi 3.0 memiliki lebih banyak penyesuaian untuk rutin konstruksi. Sebagai tindakan balasan (sebagian besar belum teruji) terhadap Black Hole dan Wildfire, pekerja yang menemukan daerah berwarna seragam akan mencoba untuk menghapusnya. Sang ratu tidak lagi melahirkan puluhan pekerja saat diserang; sebaliknya, dia memiliki sepasang semut pelindung yang mengorbit di sekitarnya. Dia juga akan menunggu lebih lama sebelum mulai menimbun makanan.

Saya tidak benar-benar tahu JS, jadi kodenya berantakan.

Ini gambar wilayah bergaris:

Ziggurat


Ini indah untuk ditonton. Sangat menarik bahwa kadang-kadang ada ketidaksempurnaan di garis-garis, seperti cacat pada kristal yang tumbuh, tetapi mereka masih diatur mengarah ke ratu sehingga tampaknya tidak menyebabkan masalah pada pekerja.
trichoplax

1
Saya suka menonton yang satu ini bekerja, terutama ketika polanya mendapatkan beberapa anomali yang tidak sempurna (karena para pekerja tidak dapat kembali ke tempat di mana mereka menemukan makanan)
Skidsdev

1
@trichoplax Saya baru saja memperbarui bot. Seharusnya tidak pernah membuat langkah ilegal sekarang, dan secara keseluruhan sedikit lebih pintar.
Zgarb

1
Dan, versi yang lebih baru sepertinya bisa selamat dari serangan oleh satu penghapus jejak dengan sedikit kesulitan. Tidak yakin apa yang terjadi jika lebih dari satu menyerbu,
pppery

1
@trichoplax Harus diperbaiki sekarang. Saya menambahkan peningkatan lain juga.
Zgarb

9

Kebakaran Hutan Mk.3

Semua jawaban saya membagikan fungsi pembantu tingkat rendah yang sama. Cari "Logika tingkat tinggi dimulai di sini" untuk melihat kode spesifik untuk jawaban ini.

// == Shared low-level helpers for all solutions ==

var QUEEN = 5;

var WHITE = 1;
var COL_MIN = WHITE;
var COL_LIM = 9;

var CENTRE = 4;

var NOP = {cell: CENTRE};

var DIR_FORWARDS = false;
var DIR_REVERSE = true;
var SIDE_RIGHT = true;
var SIDE_LEFT = false;

function sanity_check(movement) {
  var me = view[CENTRE].ant;
  if(!movement || (movement.cell|0) !== movement.cell || movement.cell < 0 || movement.cell > 8) {
    return false;
  }
  if(movement.type) {
    if(movement.color) {
      return false;
    }
    if((movement.type|0) !== movement.type || movement.type < 1 || movement.type > 4) {
      return false;
    }
    if(view[movement.cell].ant || view[movement.cell].food) {
      return false;
    }
    if(me.type !== QUEEN || me.food < 1) {
      return false;
    }
    return true;
  }
  if(movement.color) {
    if((movement.color|0) !== movement.color || movement.color < COL_MIN || movement.color >= COL_LIM) {
      return false;
    }
    if(view[movement.cell].color === movement.color) {
      return false;
    }
    return true;
  }
  if(movement.cell !== CENTRE && view[movement.cell].ant) {
    return false;
  }
  if(view[movement.cell].food + me.food > 1 && me.type !== QUEEN) {
    return false;
  }
  return true;
}

function as_array(o) {
  if(Array.isArray(o)) {
    return o;
  }
  return [o];
}

function best_of(movements) {
  var m;
  for(var i = 0; i < movements.length; ++ i) {
    if(typeof(movements[i]) === 'function') {
      m = movements[i]();
    } else {
      m = movements[i];
    }
    if(sanity_check(m)) {
      return m;
    }
  }
  return null;
}

function play_safe(movement) {
  // Avoid disqualification: no-op if moves are invalid
  return best_of(as_array(movement)) || NOP;
}

var RAND_SEED = (() => {
  var s = 0;
  for(var i = 0; i < 9; ++ i) {
    s += view[i].color * (i + 1);
    s += view[i].ant ? i * i : 0;
    s += view[i].food ? i * i * i : 0;
  }
  return s % 29;
})();

var ROTATIONS = [
  [0, 1, 2, 3, 4, 5, 6, 7, 8],
  [6, 3, 0, 7, 4, 1, 8, 5, 2],
  [8, 7, 6, 5, 4, 3, 2, 1, 0],
  [2, 5, 8, 1, 4, 7, 0, 3, 6],
];

function try_all(fns, limit, wrapperFn, checkFn) {
  var m;
  fns = as_array(fns);
  for(var i = 0; i < fns.length; ++ i) {
    if(typeof(fns[i]) !== 'function') {
      if(checkFn(m = fns[i])) {
        return m;
      }
      continue;
    }
    for(var j = 0; j < limit; ++ j) {
      if(checkFn(m = wrapperFn(fns[i], j))) {
        return m;
      }
    }
  }
  return null;
}

function identify_rotation(testFns) {
  // testFns MUST be functions, not constants
  return try_all(
    testFns,
    4,
    (fn, r) => fn(ROTATIONS[r]) ? ROTATIONS[r] : null,
    (r) => r
  );
}

function near(a, b) {
  return (
    Math.abs(a % 3 - b % 3) < 2 &&
    Math.abs(Math.floor(a / 3) - Math.floor(b / 3)) < 2
  );
}

function try_all_angles(solverFns) {
  return try_all(
    solverFns,
    4,
    (fn, r) => fn(ROTATIONS[r]),
    sanity_check
  );
}

function try_all_cells(solverFns, skipCentre) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i === CENTRE && skipCentre) ? null : fn(i)),
    sanity_check
  );
}

function try_all_cells_near(p, solverFns) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i !== p && near(p, i)) ? fn(i) : null),
    sanity_check
  );
}

function ant_type_at(i, friend) {
  return (view[i].ant && view[i].ant.friend === friend) ? view[i].ant.type : 0;
}

function friend_at(i) {
  return ant_type_at(i, true);
}

function foe_at(i) {
  return ant_type_at(i, false);
}

function ant_type_near(p, friend) {
  for(var i = 0; i < 9; ++ i) {
    if(i !== 4 && ant_type_at(i, friend) && near(i, p)) {
      return true;
    }
  }
  return false;
}

function move_agent(agents) {
  var me = view[CENTRE].ant;
  var buddies = [0, 0, 0, 0, 0, 0];
  for(var i = 0; i < 9; ++ i) {
    ++ buddies[friend_at(i)];
  }

  for(var i = 0; i < agents.length; i += 2) {
    if(agents[i] === me.type) {
      return agents[i+1](me, buddies);
    }
  }
  return null;
}

function grab_nearby_food() {
  return try_all_cells((i) => (view[i].food ? {cell: i} : null), true);
}

function go_anywhere() {
  return try_all_cells((i) => ({cell: i}), true);
}

function colours_excluding(cols) {
  var r = [];
  for(var i = COL_MIN; i < COL_LIM; ++ i) {
    if(cols.indexOf(i) === -1) {
      r.push(i);
    }
  }
  return r;
}

function generate_band(start, width) {
  var r = [];
  for(var i = 0; i < width; ++ i) {
    r.push(start + i);
  }
  return r;
}

function colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function(c) {
      return colours[(colours.indexOf(c) + 1) % colours.length];
    }
  };
}

function random_colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function() {
      return colours[RAND_SEED % colours.length];
    }
  };
}

function fast_diagonal(colourBand, avoidedColours) {
  if(!avoidedColours) {
    avoidedColours = colourBand;
  }
  var m = try_all_angles([
    // Avoid nearby checked areas
    (rot) => {
      if(
        !avoidedColours.contains(view[rot[0]].color) &&
        avoidedColours.contains(view[rot[5]].color) &&
        avoidedColours.contains(view[rot[7]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // Go in a straight diagonal line if possible
    (rot) => {
      if(
        !avoidedColours.contains(view[rot[0]].color) &&
        avoidedColours.contains(view[rot[8]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // When in doubt, pick randomly but avoid doubling-back
    (rot) => (avoidedColours.contains(view[rot[0]].color) ? null : {cell: rot[0]}),

    // Double-back when absolutely necessary
    (rot) => ({cell: rot[0]})
  ]);

  // Lay a colour track so that we can avoid doubling-back
  // (and mess up our foes as much as possible)
  if(!avoidedColours.contains(view[CENTRE].color)) {
    var prevCol = m ? view[8-m.cell].color : WHITE;
    return {cell: CENTRE, color: colourBand.next(prevCol)};
  }

  return m;
}

function follow_edge(obstacleFn, side, resultFn) {
  // Since we don't know which direction we came from, this can cause us to get
  // stuck on islands, but the random orientation helps to ensure we don't get
  // stuck forever.

  if(!resultFn) {
    resultFn = (i) => ({cell: i});
  }

  var order = ((side === SIDE_LEFT)
    ? [0, 3, 6, 7, 8, 5, 2, 1, 0]
    : [0, 1, 2, 5, 8, 7, 6, 3, 0]
  );
  return try_all(
    [obstacleFn],
    order.length - 1,
    (fn, i) => ((fn(order[i+1]) && !fn(order[i])) ? resultFn(order[i]) : null),
    sanity_check
  );
}

function start_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => ((
      !protectedCols.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 2 : 0]].color) &&
      !colourBand.contains(view[rot[1]].color)
    )
      ? {cell: rot[right ? 5 : 3], color: colourBand.next(WHITE)}
      : null)
  ]);
}

function lay_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => {
      var ahead = rot[right ? 2 : 0];
      var behind = rot[right ? 8 : 6];
      if(
        colourBand.contains(view[behind].color) &&
        !protectedCols.contains(view[ahead].color) &&
        !colourBand.contains(view[ahead].color) &&
        !colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        return {cell: ahead, color: colourBand.next(view[behind].color)};
      }
    }
  ]);
}

function follow_dotted_path(colourBand, side, direction) {
  var forwards = (direction === DIR_REVERSE) ? 7 : 1;
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    // Cell on our side? advance
    (rot) => {
      if(
        colourBand.contains(view[rot[right ? 5 : 3]].color) &&
        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[0]].color) &&
        !colourBand.contains(view[rot[2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    },

    // Cell ahead and behind? advance
    (rot) => {
      var passedCol = view[rot[right ? 8 : 6]].color;
      var nextCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(passedCol) &&
        nextCol === colourBand.next(passedCol) &&

        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[right ? 0 : 2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    }
  ]);
}

function escape_dotted_path(colourBand, side, newColourBand) {
  var right = (side === SIDE_RIGHT);
  if(!newColourBand) {
    newColourBand = colourBand;
  }

  return try_all_angles([
    // Escape from beside the line
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        !colourBand.contains(view[rot[right ? 8 : 6]].color) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[rot[7]].color) ||
        colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        // not oriented, or in a corner
        return null;
      }
      return best_of([
        {cell: rot[right ? 0 : 2], color: newColourBand.next(approachingCol)},
        {cell: rot[right ? 3 : 5]},
        {cell: rot[right ? 0 : 2]},
        {cell: rot[right ? 6 : 8]},
        {cell: rot[right ? 2 : 0]},
        {cell: rot[right ? 8 : 6]},
        {cell: rot[right ? 5 : 3]}
      ]);
    },

    // Escape from inside the line
    (rot) => {
      if(
        !colourBand.contains(view[rot[7]].color) ||
        !colourBand.contains(view[rot[1]].color) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      return best_of([
        {cell: rot[3]},
        {cell: rot[5]},
        {cell: rot[0]},
        {cell: rot[2]},
        {cell: rot[6]},
        {cell: rot[8]}
      ]);
    }
  ]);
}

function latch_to_dotted_path(colourBand, side) {
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(approachingCol) &&
        view[rot[right ? 8 : 6]].color === colourBand.next(approachingCol) &&
        !colourBand.contains(view[rot[right ? 5 : 3]].color)
      ) {
        // We're on the wrong side; go inside the line
        return {cell: rot[right ? 5 : 3]};
      }
    },

    // Inside the line? pick a side
    (rot) => {
      var passedCol = view[rot[7]].color;
      var approachingCol = view[rot[1]].color;
      if(
        !colourBand.contains(passedCol) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      if((approachingCol === colourBand.next(passedCol)) === right) {
        return best_of([{cell: rot[3]}, {cell: rot[6]}, {cell: rot[0]}]);
      } else {
        return best_of([{cell: rot[5]}, {cell: rot[2]}, {cell: rot[8]}]);
      }
    }
  ]);
}


// == High-level logic begins here ==


var groundCol = 5;
var poisonCol = 8;

var DIRECTOR = 1;
var FORAGER0 = 2;
var FORAGER1 = 3;
var FORAGER2 = 4;
var MAX_FORAGER_TYPES = 3; // Worker creation throttle
var MIN_FOOD = 3; // Don't embarrass ourselves when things go bad
var MAX_FOOD_SPAWN = 80; // If we're doing well, don't spoil it all

var GROUND_COLOURS = colour_band([groundCol, poisonCol]);
var POISON_COLOURS = colour_band([poisonCol]);
var SAFE_COLOURS = random_colour_band(colours_excluding([WHITE, groundCol, poisonCol]));
var INITIAL_OBSTACLES = random_colour_band(colours_excluding([WHITE]));

function ground_at(i) {
  return GROUND_COLOURS.contains(view[i].color);
}

function unlaiden_friend_at(i) {
  return friend_at(i) && (friend_at(i) === QUEEN || !view[i].ant.food);
}

function obstacle_at(i) {
  // foes are unpredictable, so don't consider them obstacles
  return view[i].food || unlaiden_friend_at(i) || GROUND_COLOURS.contains(view[i].color);
}

function wait_if_blocked(i) {
  return friend_at(i) ? {cell:CENTRE} : {cell: i};
}

function move_director(me, buddies) {
  if(!buddies[QUEEN]) {
    // Lost the queen!
    return go_anywhere();
  }

  var rot = identify_rotation((rot) => (
    friend_at(rot[0]) === QUEEN || friend_at(rot[1]) === QUEEN
  ));

  var ready = (friend_at(rot[1]) === QUEEN && view[rot[1]].color === groundCol);
  var shift = (view[rot[2]].color === groundCol && GROUND_COLOURS.contains(view[rot[5]].color));

  return best_of([
    // Ensure we never end up underground unless we mean to, and provide a
    // base poison layer to help workers find the right side if lost
    {cell: CENTRE, color: poisonCol},
//    {cell: rot[5], color: poisonCol},
    {cell: rot[3], color: poisonCol},

    // Move up to avoid own line after wrapping (us being underground is a signal)
    (ready && shift) && {cell: rot[2]},

    // Advance
    (ready && !shift) && {cell: rot[5]},

    // Make the poison layer more solid if we have extra time
    {cell: rot[7], color: poisonCol},
    {cell: rot[6], color: poisonCol},
    {cell: rot[8], color: poisonCol},

    // Don't lose the queen
    NOP
  ]);
}

function move_forager(me, buddies) {
  var underground = GROUND_COLOURS.contains(view[CENTRE].color);
  var buried = 0;
  for(var i = 0; i < 9; ++ i) {
    if(i !== 4 && GROUND_COLOURS.contains(view[i].color)) {
      ++ buried;
    }
  }
  var travelCol = underground ? POISON_COLOURS : SAFE_COLOURS;

  if(buddies[DIRECTOR]) {
    // We've somehow got in the way of the line; get out of the way
    return try_all_angles((rot) =>
      ((friend_at(rot[6]) === DIRECTOR || friend_at(rot[7]) === DIRECTOR) &&
      best_of([{cell: rot[0]}, {cell: rot[1]}, {cell: rot[2]}])));
  }

  if(me.food) {
    // We have food for the queen; run ahead to find her as fast as we can

    return best_of([
      // Identify confusing pinch points and close them (don't get stuck on islands)
      try_all_angles((rot) => (
        obstacle_at(rot[1]) && obstacle_at(rot[7]) &&
        !obstacle_at(rot[5]) && !GROUND_COLOURS.contains(view[CENTRE].color)
      ) && {cell: CENTRE, color: groundCol}),

      // We're enclosed; mark this as a dead-end
      (buried >= 7) && {cell: CENTRE, color: poisonCol},

      // Race to queen, but don't climb over each other and cause a blockage
      follow_edge(obstacle_at, SIDE_RIGHT, wait_if_blocked),

      // Lost? Travel quickly to find the surface again
      fast_diagonal.bind(null, travelCol),

      // Totally lost
      go_anywhere
    ]);
  }

  if(buddies[QUEEN]) {
    // Don't overtake the queen!
    return NOP;
  }

  // Paint the ground
  if(!underground) {
    return {cell: CENTRE, color: groundCol};
  }

  return best_of([
    // Unpaint small islands which would confuse us or our buddies
    (buried >= 3) && try_all_angles((rot) => (
      !view[rot[0]].ant &&
      GROUND_COLOURS.contains(view[rot[0]].color) &&
      !GROUND_COLOURS.contains(view[rot[1]].color) &&
      !GROUND_COLOURS.contains(view[rot[3]].color)
    ) && {cell: rot[0], color: SAFE_COLOURS.next(WHITE)}),

    (buried >= 3) && try_all_angles((rot) => (
      !view[rot[1]].ant &&
      GROUND_COLOURS.contains(view[rot[1]].color) &&
      !GROUND_COLOURS.contains(view[rot[0]].color) &&
      !GROUND_COLOURS.contains(view[rot[2]].color)
    ) && {cell: rot[1], color: SAFE_COLOURS.next(WHITE)}),

    // Follow line
    follow_edge(ground_at, SIDE_RIGHT, wait_if_blocked),

    // Disoriented; find the surface again
    fast_diagonal.bind(null, travelCol),

    // Totally lost; random walk
    {cell: 0}
  ]);
}

function move_queen(me, buddies) {
  if(buddies[DIRECTOR]) {
    var rot = identify_rotation((rot) => (
      (friend_at(rot[7]) === DIRECTOR && view[rot[7]].color !== groundCol) ||
      (friend_at(rot[5]) === DIRECTOR && view[rot[5]].color === groundCol) ||
      friend_at(rot[8]) === DIRECTOR
    ));

    var rand14 = rot === ROTATIONS[0];
    var existing = friend_at(rot[0]);
    var nextType = existing ? (existing + 1) : FORAGER0;
    var workerSpawn = (
      me.food > MIN_FOOD && me.food < MAX_FOOD_SPAWN &&
      view[rot[3]].color === groundCol && // Don't spawn if disrupted
      view[rot[0]].color === WHITE && // Don't spawn while stuck in a nest
      view[rot[1]].color === WHITE &&
      !friend_at(rot[1]) && !friend_at(rot[3]) && !friend_at(rot[6]) &&
      (existing || rand14) // reduce likelihood of spawning new chains
    );

    return best_of([
      // Paint ground
      {cell: CENTRE, color: groundCol},

      // Follow director up slopes
      (friend_at(rot[5]) === DIRECTOR) && {cell: rot[2]},
      (friend_at(rot[5]) === DIRECTOR) && {cell: rot[1]},

      // Recognise likely erasure issues and correct
      view[rot[2]].color === groundCol && GROUND_COLOURS.contains(view[rot[8]].color) &&
        {cell: CENTRE, color: groundCol},

      // Clear cells which could confuse workers
      GROUND_COLOURS.contains(view[rot[2]].color) && {cell: rot[2], color: SAFE_COLOURS.next(WHITE)},

      // Spawn new workers when ready (throttle probabilistically)
      (workerSpawn && nextType < FORAGER0 + MAX_FORAGER_TYPES) && {cell: rot[1], type: nextType},

      // Follow director along flat planes
      (friend_at(rot[8]) === DIRECTOR) && {cell: rot[5]},

      // Don't lose director
      NOP
    ]);
  }

  return best_of([
    // Begin wildfire
    (me.food >= MIN_FOOD + MAX_FORAGER_TYPES + 1) && try_all_angles((rot) =>
      (view[rot[5]].color !== groundCol && sanity_check({cell: rot[5]}) &&
        {cell: rot[8], type: DIRECTOR})),

    // Hungry or too crowded to begin; frantically find food
    grab_nearby_food,
    fast_diagonal.bind(null, SAFE_COLOURS, INITIAL_OBSTACLES),
    go_anywhere,
    {cell: 1, color: SAFE_COLOURS.next(WHITE)}
  ]);
}

return play_safe(move_agent([
  DIRECTOR, move_director,
  FORAGER0, move_forager,
  FORAGER1, move_forager,
  FORAGER2, move_forager,
  QUEEN, move_queen,
]));

Semut api menyapu papan, melahap semua yang ada di jalan mereka. Ini terinspirasi oleh pencampuran konsep Black Hole (oleh Draco18s) dengan Semut Forensik saya .

Sang ratu akan mulai dengan cepat mengumpulkan simpanan makanan. Begitu dia sudah cukup, dia akan mulai bergerak dalam garis lurus dan menelurkan beberapa pembantu. Pembantu ini akan mengikuti di belakangnya dalam barisan yang teratur, berlari untuk mengejar jika mereka menemukan makanan.

Mk.2 menggunakan semut spesialis untuk membantu ratu mempertahankan arahnya, dan semut ini juga memberikan jejak warna hitam untuk membantu pekerja yang hilang menemukan sisi band yang benar. Dikombinasikan dengan navigasi pekerja yang lebih baik, sekarang kinerjanya jauh lebih baik ketika dicampur dengan semut yang bersaing. Bahkan (akhirnya) pulih setelah mengenai sarang titik-titik merah.

Mk.2c menggunakan probabilitas untuk mengontrol populasi pekerja dan tampaknya mengelolanya dengan cukup baik. Para pekerja masih tersesat di dalam api lebih sering daripada yang saya inginkan, tetapi meskipun demikian ia berhasil mengambil alih jumlah papan yang mengesankan ketika sendirian.

Mk.3 menambahkan perlindungan terhadap garis merah diagonal yang dihasilkan oleh entri "ratu tunggal", yang juga membantu dalam beberapa situasi serupa. Para pekerja jauh lebih kecil kemungkinannya untuk terganggu oleh jalan memutar acak sekarang, dan tampaknya berkinerja lebih baik.

Inilah hasil dari salah satu prototipe:

API

(ya, itu 865 semut pekerja ... dan eh, 5 makanan)


Berikut screenshot dengan kompetisi:

Membersihkan api


Dan inilah cara saya membayangkan semut dalam pikiran saya (ditambah musik yang sesuai):

Parade Paprika


Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
Martin Ender

tidak dapat mendengar musik Anda ...
NH.

1
@NH. katak / kekacauan / kegilaan umum berasal dari film Paprika. Jika Anda ingin tahu musiknya, tonton itu!
Dave

9

Kecepatan cahaya

Semua jawaban saya akan berisi logika tingkat rendah yang serupa dalam bentuk Kerangka Fungsi Formik. "TINGKAT LOGIKA TINGGI MULAI DI SINI" menandai akhir kode Kerangka ini.

 //  FORMIC FRAMEWORK  //
// Version 6.1.10     //
const WHITE = 1;
const QUEEN = 5;
const CENTER = 4;
const HERE = view[CENTER];
const ME = HERE.ant;
const ORTHOGONALS = [1, 3, 5, 7];
const DIAGONALS = [0, 2, 6, 8];
const DIAGONALS_ORTHOGONALS = [0, 2, 6, 8, 1, 3, 5, 7];
const DIRECTIONS = [0, 1, 2, 3, 5, 6, 7, 8];
const CLOCKWISE_DIRECTIONS = [0, 1, 2, 5, 8, 7, 6, 3];
const CELLS = [0, 1, 2, 3, 4, 5, 6, 7, 8];
const ROTATIONS = [
  [0, 1, 2,
   3, 4, 5,
   6, 7, 8],

  [6, 3, 0,
   7, 4, 1,
   8, 5, 2],

  [8, 7, 6,
   5, 4, 3,
   2, 1, 0],

  [2, 5, 8,
   1, 4, 7,
   0, 3, 6]
];
const NEIGHBORS = [
  [1, 4, 3],
  [2, 5, 4, 3, 0],
  [5, 4, 1],
  [0, 1, 4, 7, 6],
  [0, 1, 2, 5, 8, 7, 6, 3],
  [8, 7, 4, 1, 2],
  [3, 4, 7],
  [6, 3, 4, 5, 8],
  [7, 4, 5]
];
const HORIZONTAL_FLIP = [2, 1, 0, 5, 4, 3, 8, 7, 6];
const VERTICAL_FLIP = [6, 7, 8, 3, 4, 5, 0, 1, 2];

const DEBUG_MODE = false;
function dump() {
  if (DEBUG_MODE) {
    throw "dump() not implemented";
  }
}
function log(...args) {
  if (DEBUG_MODE) {
    console.log(...args);
  }
}
function error(...args) {
  log("Transformed view state:", view);
  log(...args);
  throw "A critical error has occurred!";
}

function createArray(func, length) {
  const arr = [];
  for (let i = 0; i < length; i++) {
    arr.push(func(i, arr));
  }
  return arr;
}

class Test {
  run(cell) {
    error("No run method defined for this instance of Test:", this);
  }
  find(cells = CELLS) {
    return cells.find((c) => this.run(c));
  }
  findIndex(cells = CELLS) {
    return cells.findIndex((c) => this.run(c));
  }
  filter(cells = CELLS) {
    return cells.filter((c) => this.run(c));
  }
  every(cells = CELLS) {
    return cells.every((c) => this.run(c));
  }
  some(cells = CELLS) {
    return cells.some((c) => this.run(c));
  }
  count(cells = CELLS) {
    return this.filter(cells).length;
  }
  invert() {
    return new InverseTest(this);
  }
  and(test) {
    return new EveryTest(this, test);
  }
  or(test) {
    return new SomeTest(this, test);
  }
}

class InverseTest extends Test {
  constructor(test) {
    super();
    this.test = test;
  }
  run(cell) {
    return !this.test.run(cell);
  }
  invert() {
    return this.test;
  }
}

class CombinedTest extends Test {
  constructor(...tests) {
    super();
    this.tests = tests;
  }
  append(test) {
    this.tests.push(test);
    return this;
  }
}
class EveryTest extends CombinedTest {
  run(cell) {
    return this.tests.every((test) => test.run(cell));
  }
  and(test) {
    return this.append(test);
  }
}
class SomeTest extends CombinedTest {
  run(cell) {
    return this.tests.some((test) => test.run(cell));
  }
  or(test) {
    return this.append(test);
  }
}

class ColorTest extends Test {
  constructor(color) {
    super();
    this.color = color;
  }
  run(cell) {
    return view[cell].color === this.color;
  }
}
class ColorBandTest extends SomeTest {
  constructor(colorBand) {
    super(...colorBand.map((color) => new ColorTest(color)));
  }
}

class FoodTest extends Test {
  constructor(hasFood = true) {
    super();
    this.food = hasFood ? 1 : 0;
  }
  run(cell) {
    return view[cell].food === this.food;
  }
}

class AntTest extends Test {
  constructor(friend, type, food) {
    super();
    this.friend = friend;
    this.type = type;
    this.food = food;
  }
  run(cell) {
    const ant = view[cell].ant;
    return ant !== null && (this.type === undefined || ant.type === this.type) && (this.friend === undefined || ant.friend === this.friend) && (this.food === undefined || (this.food ? ant.food > 0 : ant.food === 0));
  }
}

class NeighborTest extends Test {
  constructor(test) {
    super();
    this.test = test;
  }
  run(cell) {
    return this.test.some(NEIGHBORS[cell]);
  }
}

class MatchTest extends Test {
  constructor(matches) {
    super();
    this.matches = matches;
  }
  run(cell) {
    return this.matches[cell];
  }
}

class CustomTest extends Test {
  constructor(func, ...args) {
    super();
    this.func = func;
    this.args = args;
  }
  run(cell) {
    return this.func(cell, ...this.args);
  }
}

class Action {
  constructor(cell, test) {
    this.cell = cell;
    this.test = test;
  }

  valid() {
    return this.cell >= 0 && this.cell < 9 && (!this.test || this.test.run(this.cell));
  }
  attempt() {
    return this.valid() ? this : null;
  }
  static tryAll(...actions) {
    return actions.find((action) => action instanceof this && action.valid()) || null;
  }
}
class Move extends Action {
  constructor(cell, test) {
    super(cell, test);
  }

  valid() {
    return super.valid() && view[this.cell].ant === null && (view[this.cell].food === 0 || ME.food === 0 || ME.type === QUEEN);
  }
  static many(cells, test) {
    return cells.map((cell) => new this(cell, test));
  }
}
class Paint extends Action {
  constructor(cell, color, test) {
    super(cell, test);
    this.color = color;
  }

  valid() {
    return super.valid() && view[this.cell].color !== this.color && this.color >= 1 && this.color <= 8;
  }
  static many(cells, colors, test) {
    return cells.map((cell, i) => new this(cell, colors[i % colors.length], test));
  }
}
class Spawn extends Action {
  constructor(cell, type, test) {
    super(cell, test);
    this.type = type;
  }

  valid() {
    return super.valid() && view[this.cell].ant === null && view[this.cell].food === 0 && ME.food > 0 && ME.type === QUEEN && this.type >= 1 && this.type <= 4;
  }
  static many(cells, type, test) {
    return cells.map((cell, i) => new this(cell, type, test));
  }
}
class NOP extends Action {
  constructor() {
    super(CENTER);
  }

  valid() {
    return true;
  }
}

class Context {
  apply(func, ...args) {
    const hiddenView = view;
    if (this.viewTranslator) {
      view = this.viewTranslator(hiddenView);
    }

    let output = func(...args);
    if (output instanceof Action && this.outputTranslator) {
      this.outputTranslator(output);
    }

    view = hiddenView;
    return output;
  }
}

class TranslationContext extends Context {
  constructor(translationArray) {
    super();
    this.translationArray = translationArray;
  }

  viewTranslator(oldView) {
    const newView = [];
    for (let i = 0; i < 9; i++) {
      newView.push(oldView[this.translationArray[i]]);
    }
    return newView;
  }

  outputTranslator(out) {
    out.cell = this.translationArray[out.cell];
  }
}
class RotationContext extends TranslationContext {
  constructor(orientation) {
    super(ROTATIONS[orientation]);
    this.orientation = orientation;
  }
}
class OffsetContext extends TranslationContext {
  constructor(centerCell) {
    throw "OffsetContext not implemented";
  }
}
class HorizontalReflectionContext extends TranslationContext {
  constructor() {
    super(HORIZONTAL_FLIP);
  }
}
class VerticalReflectionContext extends TranslationContext {
  constructor() {
    super(VERTICAL_FLIP);
  }
}

class ColorMapContext extends Context {
  constructor(map, unmap) {
    super();
    this.map = map;
    this.unmap = unmap;
  }

  viewTranslator(oldView) {
    return oldView.map((cell) => ({color: this.map[cell.color - 1], food: cell.food, ant: cell.ant}));
  }

  outputTranslator(out) {
    out.color = this.unmap[out.color - 1];
  }
}

class XY {
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
  }

  static fromTuples(...xyTuples) {
    return xyTuples.map((xy) => new this(xy[0], xy[1]));
  }
}

class WrapProperties {
  constructor(horizontal, vertical, size, wrapOffsets) {
    this.horizontal = !!horizontal;
    this.vertical = !!vertical;
    this.size = size;
    this.wrapOffsets = wrapOffsets || {};
  }
}

class ScoredTest {
  constructor(test, score = 1) {
    this.test = test;
    this.score = score;
  }

  run(cell) {
    return this.test.run(cell) ? this.score : 0;
  }
}

class Environment {
  constructor(tests, wrapping) {
    this.tests = tests.map((test) => test instanceof Test ? new ScoredTest(test) : test);
    this.wrapping = wrapping;
  }

  at(x, y) {
    const w = this.wrapping;
    while ((w.horizontal && (x < 0 || x >= w.size.x)) || (w.vertical && (y < 0 || y >= w.size.y))) {
      if (w.horizontal) {
        if (x < 0) {
          x += w.size.x;
          y += w.wrapOffsets.left || 0;
        } else if (x >= w.size.x) {
          x -= w.size.x;
          y += w.wrapOffsets.right || 0;
        }
      }

      if (w.vertical) {
        if (y < 0) {
          y += w.size.y;
          x += w.wrapOffsets.up || 0;
        } else if (y >= w.size.y) {
          y -= w.size.y;
          x += w.wrapOffsets.down || 0;
        }
      }
    }

    if ((!w.horizontal || (x >= 0 && x < w.size.x)) && (!w.vertical || (y >= 0 || y < w.size.y))) {
      return this.tests[x + y * w.size.x];
    } else {
      return null;
    }
  }
  around(x, y) {
    const arr = [];
    for (let oy = -1; oy <= 1; oy++) {
      for (let ox = -1; ox <= 1; ox++) {
        arr.push(this.at(x + ox, y + oy));
      }
    }
    return arr;
  }

  detect(...positions) {
    return createArray((i) => new RotationContext(i), 4).reduce((best, context) => {
      const next = context.apply(() => {
        return positions.reduce((best, pos, i) => {
          let score = 0;
          const matches = this.around(pos.x, pos.y).map((test, i) => {
            if (test && (test.test instanceof Test)) {
              const result = test.run(i);
              if (result) {
                score += result;
                return true;
              } else {
                return false;
              }
            } else {
              return null;
            }
          });
          if (score > best.score) {
            return {position: pos, positionIndex: i, orientation: context.orientation, environment: this, matches: matches, score: score, confidence: score - best.score};
          } else {
            best.confidence = Math.min(best.score - score, best.confidence);
            return best;
          }
        }, {position: positions[0], positionIndex: 0, orientation: 0, environment: this, matches: [], score: 0, confidence: 0});
      });
      if (next.score > best.score) {
        next.confidence = next.score - best.score;
        return next;
      } else {
        best.confidence = Math.min(best.score - next.score, best.confidence);
        return best;
      }
    }, {position: positions[0], positionIndex: 0, orientation: 0, environment: this, matches: [], score: 0, confidence: 0});
  }

  static chooseBest(...detectionResults) {
    const r = detectionResults.reduce((best, result, i) => {
      if (result.score > best.score) {
        result.index = i;
        result.confidence = result.score - best.score;
        return result;
      } else {
        best.confidence = Math.min(best.score - result.score, best.confidence);
        return best;
      }
    });
    r.index = r.index || 0;
    return r;
  }
}

class ColoredEnvironment extends Environment {
  constructor(colors, wrapping) {
    super(colors.map((color) => new ColorTest(color)), wrapping);
  }

  getPainter(detectionResult) {
    return new (class Painter {
      constructor(loc) {
        this.pos = loc.position;
        this.matches = loc.matches;
        this.colors = loc.environment.around(this.pos.x, this.pos.y).map((test) => test && test.test instanceof ColorTest ? test.test.color : null);
        this.test = new MatchTest(loc.matches).invert();
        this.orient = loc.orientation;
      }

      paint(...cells) {
        return cells.map((cell) => new Paint(cell, this.colors[cell], this.test));
      }
      cleanup(eraseColor, eraseTargets, ...cells) {
        const eraseQual = this.test.and(new ColorBandTest(eraseTargets));
        return cells.map((cell) => new Paint(cell, eraseColor, eraseQual));
      }
    })(detectionResult);
  }
}

// HIGH-LEVEL LOGIC STARTS HERE //
const PARTNER = 1;

// TODO: Do a 180 when 3 workers are in front of us
function logicOrthogonal(frontC, sideC, backC, backCells, moveCells) {
  const a = [frontC, sideC, backC, backCells, moveCells];
  const f = new FoodTest;
  return Action.tryAll(
    ...Move.many([frontC, sideC], f),
    f.some(backCells) ? new Move(backC) : null,
    ...Move.many(moveCells),
    new NOP
  );
}
function logicDiagonal(adjacentC) {
  return Action.tryAll(...Move.many(adjacentC), new NOP);
}
function logic(partnerTest, partnerOrthC, partnerDiagC, frontC, sideC, backC, backCells, moveCells, adjacentC) {
  function detectEnv(c) {
    return new Environment(createArray((i) => i === c ? partnerTest : undefined, 9), new WrapProperties(false, false, new XY(3, 3), null)).detect(new XY(1, 1));
  }
  const orth = detectEnv(partnerOrthC);
  const diag = detectEnv(partnerDiagC);
  return orth.score === 1 ? new RotationContext(orth.orientation).apply(() => logicOrthogonal(frontC, sideC, backC, backCells, moveCells)) : 
    diag.score === 1 ? new RotationContext(diag.orientation).apply(() => logicDiagonal(adjacentC)) : 
    error("How did we get here?");
}

if (ME.type === QUEEN) {
  const partner = new AntTest(true, PARTNER);
  if (partner.some(DIRECTIONS)) {
    return logic(partner, 5, 8, 2, 1, 7, [0, 3, 6, 7], [2, 7, 1, 8], [5, 7]);
  } else {
    const COLOR = 5;
    const bgTest = new ColorTest(WHITE);
    if (bgTest.run(CENTER)) {
      return new Paint(CENTER, COLOR).attempt() || error("Something went terribly wrong while painting own cell");
    }

    const food = new FoodTest().find(DIRECTIONS);
    if (food !== undefined) {
      return new Move(food);
    }

    const det = new ColoredEnvironment([
      WHITE, WHITE, WHITE, 
      WHITE, undefined, undefined,
      WHITE, undefined, COLOR
    ], new WrapProperties(false, false, new XY(3, 3))).detect(new XY(1, 1));
    return (ME.food > 0 ? Action.tryAll(...Spawn.many(ORTHOGONALS, PARTNER)) : null) ||
      (det.score === 6 ? new RotationContext(det.orientation).apply(() => Action.tryAll(...Move.many([0, 2, 6, 1, 3, 5, 7, 8]))) : null) ||
      Action.tryAll(...Move.many(DIAGONALS_ORTHOGONALS), new NOP);
  }
} else {
  return logic(new AntTest(true, QUEEN), 1, 0, 2, 5, 3, [8, 7, 6, 3], [2, 3, 5, 0], [1, 3]);
}

Penjelasan

Ini adalah semut sederhana yang saya kaget tidak ada yang pernah memikirkannya ...

Semut ini menghasilkan Mitra setelah mengumpulkan 1 makanan dengan pendekatan klasik. Setelah ini, sang Ratu dan Sang Mitra berjalan dalam garis lurus diagonal dengan kecepatan setinggi mungkin (satu sel per putaran) menggunakan posisi masing-masing. Mereka tidak mengecat sel apa pun. Mereka mensurvei rata-rata 6 sel per giliran , memberi mereka total teoritis 180 makanan per game pada peta kosong, yang mereka capai secara konsisten.

Versi 2.0+ sudah keluar! Fitur komentar dalam kode yang menjelaskan secara spesifik entri ini.


Changelog

Versi 1.0

  • Peluncuran pertama

Versi 2.0

  • manuver yang benar-benar dirubah
    • sekarang menggunakan kedekatan diagonal untuk opsi gerakan lainnya
    • mengubah perilaku untuk mengambil makanan
      • tingkat pengumpulan rata-rata tetap tidak berubah, tetapi sistem ini lebih kuat
      • mencegah kebuntuan ketika ada lebih dari satu potong makanan, secara signifikan meningkatkan konsistensi
      • mengurangi laju perubahan arah
      • bisa mengambil makanan di belakangnya sendiri
    • perubahan perilaku untuk menghindari rintangan
      • mengurangi kemungkinan kebuntuan, semakin meningkatkan konsistensi
      • tidak putar balik kecuali benar-benar diperlukan, meningkatkan total area rata-rata yang dicakup
  • membersihkan kode
  • menambahkan komentar

Versi 2.1

  • memecahkan kebuntuan eksotis

Versi 2.2

  • memecahkan kebuntuan eksotis lainnya

Versi 2.3

  • menghindari musuh sekarang jauh lebih efektif

Versi 2.3.1

  • menghindari musuh masih sedikit lebih efektif

Versi 2.4

  • Kerangka Kerja Formic diperbarui ke versi 5.0.4 (dari 1.0)
  • kode refactored (perilaku hampir identik dengan versi 2.3.1)

Versi 2.5

  • Kerangka Kerja Formic diperbarui ke versi 6.1.10 (dari 5.0.4)
  • kode refactored agar sesuai dengan standar pengkodean baru (perilaku hampir identik dengan versi 2.4)
  • komentar kode yang dihapus :(

Versi 2.5.0.1

  • perbaikan kecil

Versi 2.5.0.2

  • memperbaiki diskualifikasi bug dengan bantuan dzaima dalam obrolan

Versi 2.5.0.3

  • debugging dinonaktifkan

8

Penghapus jejak

var i, j
var orthogonals = [1, 3, 7, 5]  // These are the non-diagonal cells
if(view[4].ant.type == 5) {
//Queen moves straight to get food
// Color own cell if white
if (view[4].color != 6) { 
    return {cell:4, color:6}
}
var specified = null;
// Otherwise move to a white cell opposite a colored cell
for (i=0; i<4; i++) {
    j = (i+2) % 4
    if (view[orthogonals[i]].color !== 6 &&
        view[orthogonals[j]].color == 6 && !view[orthogonals[i]].ant) {
        specified = {cell:orthogonals[i]}
    } else if (view[4].ant.food < 8 && view[4].ant.food && view[orthogonals[i]].color !== 6 && !view[orthogonals[i]].ant && !view[orthogonals[i]].food && view[orthogonals[i]].color !== 1) {
        //create workers once I encounter a trail
        return {cell:orthogonals[i], type:(view[orthogonals[i]].color%4)+1};
    } else if (view[orthogonals[i]].food) {
        return {cell:orthogonals[i]}
    }
}
if(specified) { return specified; }
// Otherwise move to one of the vertical or horizontal cells if not occupied
for (i=1; i<9; i+=2) {
    if (!view[i].ant) {
        return {cell:i}
    }
}

// Otherwise move to one of the diagonal cells if not occupied
for (i=0; i<9; i+=2) {
    if (!view[i].ant) {
        return {cell:i};
    }
}

// Otherwise don't move at all
return {cell:4};
}
//workers erase their trails
//Follow the trail to erase
var move, color, enemyAnt = false;
var nearbyColoredCells = 0;
if(view[4].color != 1){
   color =  {cell:4, color:1}
}
for(i=0;i<9;i++) {
    if(i != 4 && view[i].color != 1 && !view[i].ant && (!view[4].ant.food || !view[i].food) && (!move || (view[i].color % 4 + 1) == view[4].type || (view[move.cell].color == 6 && view[i].color != 6))) {
        move = {cell:i}
    }
    if(view[i].ant && view[i].ant.friend && view[i].ant.type == 5){
       return {cell:4}
    }
    if(i != 4 && view[i].color != 1 && view[i].color != 6){
        nearbyColoredCells += 1;
    }
    if(view[i].ant && !view[i].ant.friend) {
         enemyAnt = i;
    }
}
if(nearbyColoredCells <= 1 || enemyAnt > 1) {
    // Either I'm following a standard trail or there are enemy workers; possibly decolor own cell and move
    if(color && (!move || !enemyAnt)) { return color; }
    if(move) { return move; }
} else if (nearbyColoredCells > 1){
   for (i = 0; i < 9; i++){
       if(view[i].color != 1){ return {cell:i, color:1} }
   }
}
// uh-oh, our trail ended or we got lost -- random walk
// find a safe place to move
for (i=0;i<9;i+=1) {
    if (!view[i].ant && (!view[4].ant.food || !view[i].food)) {
       return {cell:i}
    }
}
return {cell:4}

Semut ini, walaupun cukup pandai dalam menemukan makanan (tetapi tidak sebagus Semut Romawi atau semut Forensik) mencoba membingungkan semut lain. Ia melakukannya dengan menciptakan pekerja yang tujuan utamanya adalah untuk menghapus jejak setiap kali bertemu dengan jalur warna yang bersilangan. Pekerja yang mencapai ujung jalan mereka sia-sia berjalan acak sampai mereka menemukan jejak lain. Untuk menjaga makanan dan mungkin melakukan yang lebih baik, semut ini akan beralih strategi ke salah satu dari tidak menghasilkan lebih banyak pekerja setelah mencapai delapan makanan, yang seharusnya ada di akhir permainan hanya karena cenderung memerlukan kombo spesifik untuk mengawetkan makanan dan bukan hanya gunakan itu semua.


1
Sabotase! 😮 Apa yang dilakukan orang Romawi terhadap kamu?
Dave

Entri ini bersenang-senang mengotak-atik lubang hitam ...
Frenzy Li

2
@Dave Mereka mengirimkan entri yang bersaing untuk tantangan ini ...
pppery

2
Yah, pemain ini terkadang membuang-buang semua makanannya, karena dikacaukan oleh semut Romawi
pppery

2
Pembaruan kedua: tidak lagi menimbun makanan di awal, alih-alih mulai mengumpulkan makanan alih-alih menyabot nanti
pppery

8

Hyperwave

Semua jawaban saya akan berisi logika tingkat rendah yang serupa dalam bentuk Kerangka Fungsi Formik. "TINGKAT LOGIKA TINGGI MULAI DI SINI" menandai akhir kode Kerangka ini.

 //  FORMIC FRAMEWORK  //
// Version 6.1.10     //
const WHITE = 1;
const QUEEN = 5;
const CENTER = 4;
const HERE = view[CENTER];
const ME = HERE.ant;
const ORTHOGONALS = [1, 3, 5, 7];
const DIAGONALS = [0, 2, 6, 8];
const DIAGONALS_ORTHOGONALS = [0, 2, 6, 8, 1, 3, 5, 7];
const DIRECTIONS = [0, 1, 2, 3, 5, 6, 7, 8];
const CLOCKWISE_DIRECTIONS = [0, 1, 2, 5, 8, 7, 6, 3];
const CELLS = [0, 1, 2, 3, 4, 5, 6, 7, 8];
const ROTATIONS = [
  [0, 1, 2,
   3, 4, 5,
   6, 7, 8],

  [6, 3, 0,
   7, 4, 1,
   8, 5, 2],

  [8, 7, 6,
   5, 4, 3,
   2, 1, 0],

  [2, 5, 8,
   1, 4, 7,
   0, 3, 6]
];
const NEIGHBORS = [
  [1, 4, 3],
  [2, 5, 4, 3, 0],
  [5, 4, 1],
  [0, 1, 4, 7, 6],
  [0, 1, 2, 5, 8, 7, 6, 3],
  [8, 7, 4, 1, 2],
  [3, 4, 7],
  [6, 3, 4, 5, 8],
  [7, 4, 5]
];
const HORIZONTAL_FLIP = [2, 1, 0, 5, 4, 3, 8, 7, 6];
const VERTICAL_FLIP = [6, 7, 8, 3, 4, 5, 0, 1, 2];

const DEBUG_MODE = true;
function dump() {
  if (DEBUG_MODE) {
    throw "dump() not implemented";
  }
}
function log(...args) {
  if (DEBUG_MODE) {
    console.log(...args);
  }
}
function error(...args) {
  log("Transformed view state:", view);
  log(...args);
  throw "A critical error has occurred!";
}

function createArray(func, length) {
  const arr = [];
  for (let i = 0; i < length; i++) {
    arr.push(func(i, arr));
  }
  return arr;
}

class Test {
  run(cell) {
    error("No run method defined for this instance of Test:", this);
  }
  find(cells = CELLS) {
    return cells.find((c) => this.run(c));
  }
  findIndex(cells = CELLS) {
    return cells.findIndex((c) => this.run(c));
  }
  filter(cells = CELLS) {
    return cells.filter((c) => this.run(c));
  }
  every(cells = CELLS) {
    return cells.every((c) => this.run(c));
  }
  some(cells = CELLS) {
    return cells.some((c) => this.run(c));
  }
  count(cells = CELLS) {
    return this.filter(cells).length;
  }
  invert() {
    return new InverseTest(this);
  }
  and(test) {
    return new EveryTest(this, test);
  }
  or(test) {
    return new SomeTest(this, test);
  }
}

class InverseTest extends Test {
  constructor(test) {
    super();
    this.test = test;
  }
  run(cell) {
    return !this.test.run(cell);
  }
  invert() {
    return this.test;
  }
}

class CombinedTest extends Test {
  constructor(...tests) {
    super();
    this.tests = tests;
  }
  append(test) {
    this.tests.push(test);
    return this;
  }
}
class EveryTest extends CombinedTest {
  run(cell) {
    return this.tests.every((test) => test.run(cell));
  }
  and(test) {
    return this.append(test);
  }
}
class SomeTest extends CombinedTest {
  run(cell) {
    return this.tests.some((test) => test.run(cell));
  }
  or(test) {
    return this.append(test);
  }
}

class ColorTest extends Test {
  constructor(color) {
    super();
    this.color = color;
  }
  run(cell) {
    return view[cell].color === this.color;
  }
}
class ColorBandTest extends SomeTest {
  constructor(colorBand) {
    super(...colorBand.map((color) => new ColorTest(color)));
  }
}

class FoodTest extends Test {
  constructor(hasFood = true) {
    super();
    this.food = hasFood ? 1 : 0;
  }
  run(cell) {
    return view[cell].food === this.food;
  }
}

class AntTest extends Test {
  constructor(friend, type, food) {
    super();
    this.friend = friend;
    this.type = type;
    this.food = food;
  }
  run(cell) {
    const ant = view[cell].ant;
    return ant !== null && (this.type === undefined || ant.type === this.type) && (this.friend === undefined || ant.friend === this.friend) && (this.food === undefined || (this.food ? ant.food > 0 : ant.food === 0));
  }
}

class NeighborTest extends Test {
  constructor(test) {
    super();
    this.test = test;
  }
  run(cell) {
    return this.test.some(NEIGHBORS[cell]);
  }
}

class MatchTest extends Test {
  constructor(matches) {
    super();
    this.matches = matches;
  }
  run(cell) {
    return this.matches[cell];
  }
}

class CustomTest extends Test {
  constructor(func, ...args) {
    super();
    this.func = func;
    this.args = args;
  }
  run(cell) {
    return this.func(cell, ...this.args);
  }
}

class Action {
  constructor(cell, test) {
    this.cell = cell;
    this.test = test;
  }

  valid() {
    return this.cell >= 0 && this.cell < 9 && (!this.test || this.test.run(this.cell));
  }
  attempt() {
    return this.valid() ? this : null;
  }
  static tryAll(...actions) {
    return actions.find((action) => action instanceof this && action.valid()) || null;
  }
}
class Move extends Action {
  constructor(cell, test) {
    super(cell, test);
  }

  valid() {
    return super.valid() && view[this.cell].ant === null && (view[this.cell].food === 0 || ME.food === 0 || ME.type === QUEEN);
  }
  static many(cells, test) {
    return cells.map((cell) => new this(cell, test));
  }
}
class Paint extends Action {
  constructor(cell, color, test) {
    super(cell, test);
    this.color = color;
  }

  valid() {
    return super.valid() && view[this.cell].color !== this.color && this.color >= 1 && this.color <= 8;
  }
  static many(cells, colors, test) {
    return cells.map((cell, i) => new this(cell, colors[i % colors.length], test));
  }
}
class Spawn extends Action {
  constructor(cell, type, test) {
    super(cell, test);
    this.type = type;
  }

  valid() {
    return super.valid() && view[this.cell].ant === null && view[this.cell].food === 0 && ME.food > 0 && ME.type === QUEEN && this.type >= 1 && this.type <= 4;
  }
  static many(cells, type, test) {
    return cells.map((cell, i) => new this(cell, type, test));
  }
}
class NOP extends Action {
  constructor() {
    super(CENTER);
  }

  valid() {
    return true;
  }
}

class Context {
  apply(func, ...args) {
    const hiddenView = view;
    if (this.viewTranslator) {
      view = this.viewTranslator(hiddenView);
    }

    let output = func(...args);
    if (output instanceof Action && this.outputTranslator) {
      this.outputTranslator(output);
    }

    view = hiddenView;
    return output;
  }
}

class TranslationContext extends Context {
  constructor(translationArray) {
    super();
    this.translationArray = translationArray;
  }

  viewTranslator(oldView) {
    const newView = [];
    for (let i = 0; i < 9; i++) {
      newView.push(oldView[this.translationArray[i]]);
    }
    return newView;
  }

  outputTranslator(out) {
    out.cell = this.translationArray[out.cell];
  }
}
class RotationContext extends TranslationContext {
  constructor(orientation) {
    super(ROTATIONS[orientation]);
    this.orientation = orientation;
  }
}
class OffsetContext extends TranslationContext {
  constructor(centerCell) {
    throw "OffsetContext not implemented";
  }
}
class HorizontalReflectionContext extends TranslationContext {
  constructor() {
    super(HORIZONTAL_FLIP);
  }
}
class VerticalReflectionContext extends TranslationContext {
  constructor() {
    super(VERTICAL_FLIP);
  }
}

class ColorMapContext extends Context {
  constructor(map, unmap) {
    super();
    this.map = map;
    this.unmap = unmap;
  }

  viewTranslator(oldView) {
    return oldView.map((cell) => ({color: this.map[cell.color - 1], food: cell.food, ant: cell.ant}));
  }

  outputTranslator(out) {
    out.color = this.unmap[out.color - 1];
  }
}

class XY {
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
  }

  static fromTuples(...xyTuples) {
    return xyTuples.map((xy) => new this(xy[0], xy[1]));
  }
}

class WrapProperties {
  constructor(horizontal, vertical, size, wrapOffsets) {
    this.horizontal = !!horizontal;
    this.vertical = !!vertical;
    this.size = size;
    this.wrapOffsets = wrapOffsets || {};
  }
}

class ScoredTest {
  constructor(test, score = 1) {
    this.test = test;
    this.score = score;
  }

  run(cell) {
    return this.test.run(cell) ? this.score : 0;
  }
}

class Environment {
  constructor(tests, wrapping) {
    this.tests = tests.map((test) => test instanceof Test ? new ScoredTest(test) : test);
    this.wrapping = wrapping;
  }

  at(x, y) {
    const w = this.wrapping;
    while ((w.horizontal && (x < 0 || x >= w.size.x)) || (w.vertical && (y < 0 || y >= w.size.y))) {
      if (w.horizontal) {
        if (x < 0) {
          x += w.size.x;
          y += w.wrapOffsets.left || 0;
        } else if (x >= w.size.x) {
          x -= w.size.x;
          y += w.wrapOffsets.right || 0;
        }
      }

      if (w.vertical) {
        if (y < 0) {
          y += w.size.y;
          x += w.wrapOffsets.up || 0;
        } else if (y >= w.size.y) {
          y -= w.size.y;
          x += w.wrapOffsets.down || 0;
        }
      }
    }

    if ((!w.horizontal || (x >= 0 && x < w.size.x)) && (!w.vertical || (y >= 0 || y < w.size.y))) {
      return this.tests[x + y * w.size.x];
    } else {
      return null;
    }
  }
  around(x, y) {
    const arr = [];
    for (let oy = -1; oy <= 1; oy++) {
      for (let ox = -1; ox <= 1; ox++) {
        arr.push(this.at(x + ox, y + oy));
      }
    }
    return arr;
  }

  detect(...positions) {
    return createArray((i) => new RotationContext(i), 4).reduce((best, context) => {
      const next = context.apply(() => {
        return positions.reduce((best, pos, i) => {
          let score = 0;
          const matches = this.around(pos.x, pos.y).map((test, i) => {
            if (test && (test.test instanceof Test)) {
              const result = test.run(i);
              if (result) {
                score += result;
                return true;
              } else {
                return false;
              }
            } else {
              return null;
            }
          });
          if (score > best.score) {
            return {position: pos, positionIndex: i, orientation: context.orientation, environment: this, matches: matches, score: score, confidence: score - best.score};
          } else {
            best.confidence = Math.min(best.score - score, best.confidence);
            return best;
          }
        }, {position: positions[0], positionIndex: 0, orientation: 0, environment: this, matches: [], score: 0, confidence: 0});
      });
      if (next.score > best.score) {
        next.confidence = next.score - best.score;
        return next;
      } else {
        best.confidence = Math.min(best.score - next.score, best.confidence);
        return best;
      }
    }, {position: positions[0], positionIndex: 0, orientation: 0, environment: this, matches: [], score: 0, confidence: 0});
  }

  static chooseBest(...detectionResults) {
    const r = detectionResults.reduce((best, result, i) => {
      if (result.score > best.score) {
        result.index = i;
        result.confidence = result.score - best.score;
        return result;
      } else {
        best.confidence = Math.min(best.score - result.score, best.confidence);
        return best;
      }
    });
    r.index = r.index || 0;
    return r;
  }
}

class ColoredEnvironment extends Environment {
  constructor(colors, wrapping) {
    super(colors.map((color) => new ColorTest(color)), wrapping);
  }

  getPainter(detectionResult) {
    return new (class Painter {
      constructor(loc) {
        this.pos = loc.position;
        this.matches = loc.matches;
        this.colors = loc.environment.around(this.pos.x, this.pos.y).map((test) => test && test.test instanceof ColorTest ? test.test.color : null);
        this.test = new MatchTest(loc.matches).invert();
        this.orient = loc.orientation;
      }

      paint(...cells) {
        return cells.map((cell) => new Paint(cell, this.colors[cell], this.test));
      }
      cleanup(eraseColor, eraseTargets, ...cells) {
        const eraseQual = this.test.and(new ColorBandTest(eraseTargets));
        return cells.map((cell) => new Paint(cell, eraseColor, eraseQual));
      }
    })(detectionResult);
  }
}

// HIGH-LEVEL LOGIC STARTS HERE //
// TODO:
// - more food checkpoints (no disadvantages because it's illogical for WFW to "premanently lose" workers)
// - randomly shifting 1 up (5% chance? watch out for hoarding stealing your randomness!)
// - randomly skip painting a tiny bit of local cells (prevent deadlock against ants outside of view)
// - escape routine when situation is dire (many workers near the queen/partner)

const COLOR_BAND = [4, 7, 3, 2, 8];

const PARTNER = 2;
const WORKER = 1;

const START_FOOD = 6;
const MIN_CONFIDENCE = 2;

const PATTERN = new ColoredEnvironment(COLOR_BAND, new WrapProperties(true, true, new XY(COLOR_BAND.length, 1), {up: 2, down: -2})).detect(...createArray((i) => new XY(i, 0), COLOR_BAND.length));

function checkpoint(val, tolerance) {
  return ME.food >= val - tolerance && ME.food <= val;
}
function shouldSpawn() {
  return PATTERN.orientation === 0 && PATTERN.positionIndex % 3 === 0 &&
    ME.food < 400 &&
    (ME.food < 75 || PATTERN.positionIndex === 0) && 
    !checkpoint(300, 4) &&
    !checkpoint(200, 5) &&
    !checkpoint(160, 3) &&
    !checkpoint(130, 2) &&
    !checkpoint(100, 2) &&
    !checkpoint(75, 2) &&
    !checkpoint(50, 2) &&
    !checkpoint(35, 1) &&
    !checkpoint(20, 1) &&
    !checkpoint(10, 0);
}

function lightspeed() {
  // TODO: Do a 180 when 3 workers are in front of us
  function logicOrthogonal(frontC, sideC, backC, backCells, moveCells) {
    const a = [frontC, sideC, backC, backCells, moveCells];
    const f = new FoodTest;
    return Action.tryAll(
      ...Move.many([frontC, sideC], f),
      f.some(backCells) ? new Move(backC) : null,
      ...Move.many(moveCells),
      new NOP
    );
  }
  function logicDiagonal(adjacentC) {
    return Action.tryAll(...Move.many(adjacentC), new NOP);
  }
  function logic(partnerTest, partnerOrthC, partnerDiagC, frontC, sideC, backC, backCells, moveCells, adjacentC) {
    function detectEnv(c) {
      return new Environment(createArray((i) => i === c ? partnerTest : undefined, 9), new WrapProperties(false, false, new XY(3, 3), null)).detect(new XY(1, 1));
    }
    const orth = detectEnv(partnerOrthC);
    const diag = detectEnv(partnerDiagC);
    return orth.score === 1 ? new RotationContext(orth.orientation).apply(() => logicOrthogonal(frontC, sideC, backC, backCells, moveCells)) : 
      diag.score === 1 ? new RotationContext(diag.orientation).apply(() => logicDiagonal(adjacentC)) : 
      error("How did we get here?");
  }

  if (ME.type === QUEEN) {
    const partner = new AntTest(true, PARTNER);
    if (partner.some(DIRECTIONS)) {
      return logic(partner, 5, 8, 2, 1, 7, [0, 3, 6, 7], [2, 7, 1, 8], [5, 7]);
    } else {
      const COLOR = 5;
      const bgTest = new ColorTest(WHITE);
      if (bgTest.run(CENTER)) {
        return new Paint(CENTER, COLOR).attempt() || error("Something went terribly wrong while painting own cell");
      }

      const food = new FoodTest().find(DIRECTIONS);
      if (food !== undefined) {
        return new Move(food);
      }

      const det = new ColoredEnvironment([
        WHITE, WHITE, WHITE, 
        WHITE, undefined, undefined,
        WHITE, undefined, COLOR
      ], new WrapProperties(false, false, new XY(3, 3))).detect(new XY(1, 1));
      return (ME.food > 0 ? Action.tryAll(...Spawn.many(ORTHOGONALS, PARTNER)) : null) ||
        (det.score === 6 ? new RotationContext(det.orientation).apply(() => Action.tryAll(...Move.many([0, 2, 6, 1, 3, 5, 7, 8]))) : null) ||
        Action.tryAll(...Move.many(DIAGONALS_ORTHOGONALS), new NOP);
    }
  } else {
    return logic(new AntTest(true, QUEEN), 1, 0, 2, 5, 3, [8, 7, 6, 3], [2, 3, 5, 0], [1, 3]);
  }
}

function queen() {
  const partnerTest = new AntTest(true, PARTNER);
  if (PATTERN.confidence < MIN_CONFIDENCE && (ME.food < START_FOOD || partnerTest.some(DIAGONALS))) return lightspeed();
  return new RotationContext(PATTERN.orientation).apply(() => {
    const partnerCell = new AntTest(true, PARTNER).find(DIRECTIONS);
    const p = PATTERN.environment.getPainter(PATTERN);
    const e = new AntTest(false);
    const enemy = e.some(DIRECTIONS);
    return Action.tryAll(
      ...!PATTERN.matches[8] ? [
        ...!enemy ? [...p.paint(7, 4, 5, 1, 2), ...shouldSpawn() && PATTERN.score === 8 ? Spawn.many([0, 2], WORKER) : []] : [],
        ...partnerCell === 1 ? Move.many(e.run(5) ? [0, 2] : PATTERN.orientation === 1 && PATTERN.positionIndex % 3 === 1 ? [2, 0, 5] : [5, 2, 0]) : 
          partnerCell === 0 ? Move.many(e.run(1) ? [3] : enemy ? [1, 3] : []) :
          partnerCell === 2 ? Move.many(e.run(1) ? [5] : []) :
          []
      ] : [
        ...!enemy ? p.paint(8, 7, 6, 5, 4, 3, 2, 1, 0) : [],
        ...Move.many(partnerCell === 1 ? [2, 0] : partnerCell === 0 ? [1, 3] : partnerCell === 2 && e.run(1) ? [5] : [])
      ],
      new NOP
    )
  });
}
function partner() {
  const queenTest = new AntTest(true, QUEEN)
  const queenCell = queenTest.find(DIRECTIONS);
  if (queenCell === undefined) {
    return new NOP; // TODO: What do we do if we've lost our queen?
  }
  if (PATTERN.confidence < MIN_CONFIDENCE && (view[queenCell].ant.food < START_FOOD || DIAGONALS.includes(queenCell))) return lightspeed();
  return PATTERN.confidence >= MIN_CONFIDENCE ? new RotationContext(PATTERN.orientation).apply(() => {
    const queenCell = queenTest.find(DIRECTIONS);
    const e = new AntTest(false);
    const enemy = e.some(DIRECTIONS);
    return Action.tryAll(
      ...!enemy ? PATTERN.environment.getPainter(PATTERN).paint(...CELLS) : [],
      ...Move.many([
        [1],
        [0, 2],
        [1],
        [0, 1],
        [], // Queen can't be on cell 4 - I'm here, after all!
        [2, 1],
        [3],
        [],
        [5]
      ][queenCell]),
      new NOP
    )
  }) : new NOP;
}
function worker() {
  const m = new MatchTest(PATTERN.matches);
  const n = m.invert();
  const u = new AntTest(true, WORKER, false);
  const l = new AntTest(true, WORKER, true);
  const q = new AntTest(true, QUEEN);
  const pt = new AntTest(true, PARTNER);
  const p = PATTERN.environment.getPainter(PATTERN);
  return new RotationContext(PATTERN.orientation).apply(() => { // TODO: Unique (random?) behavior when confidence low
    if (ME.food === 0) {
      const f = new FoodTest();
      const count = n.count([6, 7, 8]);
      return Action.tryAll(
        ...PATTERN.confidence >= 2 ? p.paint(4, 0, 1, 2) : [],
        //...p.cleanup(WHITE, COLOR_BAND, ...CELLS),
        ...((food) => food !== undefined ? [...p.paint(...NEIGHBORS[food], food), new Move(food)] : [])(f.find(DIRECTIONS)),
        ...q.or(pt).some(DIRECTIONS) || u.some([6, 7, 8, 5, 2]) ? Move.many([0, 1, 3], m) : [],
        ...count > 1 ? p.paint(...[6, 7, 8]) : [],
        ...count === 1 ? [...p.paint(...[3, 5]), ...Move.many([7, 8, 6, 3], m)] : [],
        /*n.run(6) ? new Move(3, m) : null,
        ...n.run(7) ? Move.many([6, 3], m) : [],
        n.run(8) ? new Move(7, m) : null,*/
        n.run(5) ? new Move(5) : null,
        ...Move.many([2, 1, 0, 3], m),
        new NOP
        /*
        ...(PATTERN.confidence >= 2 ? [...(PATTERN.score < 8 || new AntTest(false).some(DIRECTIONS) ? p.paint(4, 3, 0, 1, 2, 5) : []), ...p.cleanup(WHITE, COLOR_BAND, ...CELLS)] : []),
        ...((food) => food !== undefined ? [...p.paint(...NEIGHBORS[food]), new Move(food)] : [])(f.find(DIRECTIONS)), ...(
          w ? Move.many([1, 0, 2]) :
          m.some([4, 3]) ? p.paint(4, 3) :
          m.some([0, 1, 2]) ? Move.many([1, 5]) :
          m.run(6) ? [new Move(3)] :
          m.run(7) ? Move.many([6, 3]) :
          m.run(8) ? [new Move(7)] :
          m.run(5) ? Move.many([5, 7]) :
          Move.many([2, 1, 5])
        )
        new NOP*/
      );
    } else {
      return Action.tryAll(
        //...Move.many(new AntTest(true, WORKER).some([2, 5, 8, 7]) || PATTERN.score < 9 ? [8, 7, 6, 3] : [5, 8, 2], m.invert()),
        ...((test) => createArray((i) => new Move(CLOCKWISE_DIRECTIONS[(6 - i) % 8], test), 5))(new CustomTest((cell, moveTest, blockTest) => {
          const i = CLOCKWISE_DIRECTIONS.findIndex((c) => c === cell);
          return moveTest.run(CLOCKWISE_DIRECTIONS[i]) && blockTest.run(CLOCKWISE_DIRECTIONS[((i - 1) + 8) % 8]);
        }, m, n.or(new AntTest().and(l.invert())))),
        ...Move.many([2, 5, 1, 8], m),
        //...Move.many([...(PATTERN.score === 9 && !new AntTest(true, WORKER).some(DIRECTIONS) ? [2] : []), 5, 8, 7, 6, 3], m),
        new NOP
      );
    }
  });
}

switch (ME.type) {
  case QUEEN: {
    return queen();
  }
  case PARTNER: {
    return partner();
  }
  case WORKER: {
    return worker();
  }
}

Saya sementara waktu menghapus semua deskripsi entri ini karena tekanan waktu. Saya akan menambahkan deskripsi menyeluruh tentang pembaruan utama ini di kemudian hari.


Changelog

Versi 1.0

  • Peluncuran pertama

Versi 2.0

  • diganti Highway dengan Hyperwave

Versi 2.0.1

  • mekanisme penimbunan hotfixed

Yang ini dimulai dengan indah untuk ditonton, kemudian berubah menakutkan ...
trichoplax

1
Saatnya untuk memberlakukan dekrit vampir terhadap jalan.
Draco18s

Dan saya hanya akan memberi makan Vampir lebih banyak seiring berjalannya waktu @ Draco18s - ada banyak perbaikan yang ingin saya lakukan.
Alion

3
Entri pertama yang saya lihat yang seluruhnya dapat merangkum papan dalam waktu permainan standar. Sangat mengesankan.
Dave

7

Semut Romawi Mk.2

Semua jawaban saya membagikan fungsi pembantu tingkat rendah yang sama. Cari "Logika tingkat tinggi dimulai di sini" untuk melihat kode spesifik untuk jawaban ini.

// == Shared low-level helpers for all solutions ==

var QUEEN = 5;

var WHITE = 1;
var COL_MIN = WHITE;
var COL_LIM = 9;

var CENTRE = 4;

var NOP = {cell: CENTRE};

var DIR_FORWARDS = false;
var DIR_REVERSE = true;
var SIDE_RIGHT = true;
var SIDE_LEFT = false;

function sanity_check(movement) {
  var me = view[CENTRE].ant;
  if(!movement || movement.cell < 0 || movement.cell > 8) {
    return false;
  }
  if(movement.type) {
    if(movement.color) {
      return false;
    }
    if(movement.type < 1 || movement.type > 4) {
      return false;
    }
    if(view[movement.cell].ant || view[movement.cell].food) {
      return false;
    }
    if(me.type !== QUEEN || me.food < 1) {
      return false;
    }
    return true;
  }
  if(movement.color) {
    if(movement.color < COL_MIN || movement.color >= COL_LIM) {
      return false;
    }
    if(view[movement.cell].color === movement.color) {
      return false;
    }
    return true;
  }
  if(view[movement.cell].ant) {
    return false;
  }
  if(view[movement.cell].food + me.food > 1 && me.type !== QUEEN) {
    return false;
  }
  return true;
}

function as_array(o) {
  if(Array.isArray(o)) {
    return o;
  }
  return [o];
}

function best_of(movements) {
  var m;
  for(var i = 0; i < movements.length; ++ i) {
    if(typeof(movements[i]) === 'function') {
      m = movements[i]();
    } else {
      m = movements[i];
    }
    if(sanity_check(m)) {
      return m;
    }
  }
  return null;
}

function play_safe(movement) {
  // Avoid disqualification: no-op if moves are invalid
  return best_of(as_array(movement)) || NOP;
}

var RAND_SEED = (() => {
  var s = 0;
  for(var i = 0; i < 9; ++ i) {
    s += view[i].color * (i + 1);
    s += view[i].ant ? i * i : 0;
    s += view[i].food ? i * i * i : 0;
  }
  return s % 29;
})();

var ROTATIONS = [
  [0, 1, 2, 3, 4, 5, 6, 7, 8],
  [6, 3, 0, 7, 4, 1, 8, 5, 2],
  [8, 7, 6, 5, 4, 3, 2, 1, 0],
  [2, 5, 8, 1, 4, 7, 0, 3, 6],
];

function try_all(fns, limit, wrapperFn, checkFn) {
  var m;
  fns = as_array(fns);
  for(var i = 0; i < fns.length; ++ i) {
    if(typeof(fns[i]) !== 'function') {
      if(checkFn(m = fns[i])) {
        return m;
      }
      continue;
    }
    for(var j = 0; j < limit; ++ j) {
      if(checkFn(m = wrapperFn(fns[i], j))) {
        return m;
      }
    }
  }
  return null;
}

function identify_rotation(testFns) {
  // testFns MUST be functions, not constants
  return try_all(
    testFns,
    4,
    (fn, r) => fn(ROTATIONS[r]) ? ROTATIONS[r] : null,
    (r) => r
  );
}

function near(a, b) {
  return (
    Math.abs(a % 3 - b % 3) < 2 &&
    Math.abs(Math.floor(a / 3) - Math.floor(b / 3)) < 2
  );
}

function try_all_angles(solverFns) {
  return try_all(
    solverFns,
    4,
    (fn, r) => fn(ROTATIONS[r]),
    sanity_check
  );
}

function try_all_cells(solverFns, skipCentre) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i === CENTRE && skipCentre) ? null : fn(i)),
    sanity_check
  );
}

function try_all_cells_near(p, solverFns) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i !== p && near(p, i)) ? fn(i) : null),
    sanity_check
  );
}

function ant_type_at(i, friend) {
  return (view[i].ant && view[i].ant.friend === friend) ? view[i].ant.type : 0;
}

function friend_at(i) {
  return ant_type_at(i, true);
}

function foe_at(i) {
  return ant_type_at(i, false);
}

function foe_near(p) {
  for(var i = 0; i < 9; ++ i) {
    if(foe_at(i) && near(i, p)) {
      return true;
    }
  }
  return false;
}

function move_agent(agents) {
  var me = view[CENTRE].ant;
  var buddies = [0, 0, 0, 0, 0, 0];
  for(var i = 0; i < 9; ++ i) {
    ++ buddies[friend_at(i)];
  }

  for(var i = 0; i < agents.length; i += 2) {
    if(agents[i] === me.type) {
      return agents[i+1](me, buddies);
    }
  }
  return null;
}

function grab_nearby_food() {
  return try_all_cells((i) => (view[i].food ? {cell: i} : null), true);
}

function go_anywhere() {
  return try_all_cells((i) => ({cell: i}), true);
}

function colours_excluding(cols) {
  var r = [];
  for(var i = COL_MIN; i < COL_LIM; ++ i) {
    if(cols.indexOf(i) === -1) {
      r.push(i);
    }
  }
  return r;
}

function generate_band(start, width) {
  var r = [];
  for(var i = 0; i < width; ++ i) {
    r.push(start + i);
  }
  return r;
}

function colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function(c) {
      return colours[(colours.indexOf(c) + 1) % colours.length];
    }
  };
}

function random_colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function() {
      return colours[RAND_SEED % colours.length];
    }
  };
}

function fast_diagonal(colourBand) {
  var m = try_all_angles([
    // Avoid nearby checked areas
    (rot) => {
      if(
        !colourBand.contains(view[rot[0]].color) &&
        colourBand.contains(view[rot[5]].color) &&
        colourBand.contains(view[rot[7]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // Go in a straight diagonal line if possible
    (rot) => {
      if(
        !colourBand.contains(view[rot[0]].color) &&
        colourBand.contains(view[rot[8]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // When in doubt, pick randomly but avoid doubling-back
    (rot) => (colourBand.contains(view[rot[0]].color) ? null : {cell: rot[0]}),

    // Double-back when absolutely necessary
    (rot) => ({cell: rot[0]})
  ]);

  // Lay a colour track so that we can avoid doubling-back
  // (and mess up our foes as much as possible)
  if(!colourBand.contains(view[CENTRE].color)) {
    var prevCol = m ? view[8-m.cell].color : WHITE;
    return {cell: CENTRE, color: colourBand.next(prevCol)};
  }

  return m;
}

function follow_edge(obstacleFn, side) {
  // Since we don't know which direction we came from, this can cause us to get
  // stuck on islands, but the random orientation helps to ensure we don't get
  // stuck forever.

  var order = ((side === SIDE_LEFT)
    ? [0, 3, 6, 7, 8, 5, 2, 1, 0]
    : [0, 1, 2, 5, 8, 7, 6, 3, 0]
  );
  return try_all(
    [obstacleFn],
    order.length - 1,
    (fn, i) => (fn(order[i+1]) && !fn(order[i])) ? {cell: order[i]} : null,
    sanity_check
  );
}

function start_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => ((
      !protectedCols.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 2 : 0]].color) &&
      !colourBand.contains(view[rot[1]].color)
    )
      ? {cell: rot[right ? 5 : 3], color: colourBand.next(WHITE)}
      : null)
  ]);
}

function lay_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => {
      var ahead = rot[right ? 2 : 0];
      var behind = rot[right ? 8 : 6];
      if(
        colourBand.contains(view[behind].color) &&
        !protectedCols.contains(view[ahead].color) &&
        !colourBand.contains(view[ahead].color) &&
        !colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        return {cell: ahead, color: colourBand.next(view[behind].color)};
      }
    }
  ]);
}

function follow_dotted_path(colourBand, side, direction) {
  var forwards = (direction === DIR_REVERSE) ? 7 : 1;
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    // Cell on our side? advance
    (rot) => {
      if(
        colourBand.contains(view[rot[right ? 5 : 3]].color) &&
        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[0]].color) &&
        !colourBand.contains(view[rot[2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    },

    // Cell ahead and behind? advance
    (rot) => {
      var passedCol = view[rot[right ? 8 : 6]].color;
      var nextCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(passedCol) &&
        nextCol === colourBand.next(passedCol) &&

        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[right ? 0 : 2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    }
  ]);
}

function escape_dotted_path(colourBand, side, newColourBand) {
  var right = (side === SIDE_RIGHT);
  if(!newColourBand) {
    newColourBand = colourBand;
  }

  return try_all_angles([
    // Escape from beside the line
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        !colourBand.contains(view[rot[right ? 8 : 6]].color) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[rot[7]].color) ||
        colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        // not oriented, or in a corner
        return null;
      }
      return best_of([
        {cell: rot[right ? 0 : 2], color: newColourBand.next(approachingCol)},
        {cell: rot[right ? 3 : 5]},
        {cell: rot[right ? 0 : 2]},
        {cell: rot[right ? 6 : 8]},
        {cell: rot[right ? 2 : 0]},
        {cell: rot[right ? 8 : 6]},
        {cell: rot[right ? 5 : 3]}
      ]);
    },

    // Escape from inside the line
    (rot) => {
      if(
        !colourBand.contains(view[rot[7]].color) ||
        !colourBand.contains(view[rot[1]].color) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      return best_of([
        {cell: rot[3]},
        {cell: rot[5]},
        {cell: rot[0]},
        {cell: rot[2]},
        {cell: rot[6]},
        {cell: rot[8]}
      ]);
    }
  ]);
}

function latch_to_dotted_path(colourBand, side) {
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(approachingCol) &&
        view[rot[right ? 8 : 6]].color === colourBand.next(approachingCol) &&
        !colourBand.contains(view[rot[right ? 5 : 3]].color)
      ) {
        // We're on the wrong side; go inside the line
        return {cell: rot[right ? 5 : 3]};
      }
    },

    // Inside the line? pick a side
    (rot) => {
      var passedCol = view[rot[7]].color;
      var approachingCol = view[rot[1]].color;
      if(
        !colourBand.contains(passedCol) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      if((approachingCol === colourBand.next(passedCol)) === right) {
        return best_of([{cell: rot[3]}, {cell: rot[6]}, {cell: rot[0]}]);
      } else {
        return best_of([{cell: rot[5]}, {cell: rot[2]}, {cell: rot[8]}]);
      }
    }
  ]);
}


// == High-level logic begins here ==


var QUEEN_COL = colour_band(generate_band(3, 3));
var WORKER_COL = colour_band(generate_band(6, 3));
var AVOID_COL = colour_band([2]);
var INVERT_COL = colour_band([WHITE, 2]);

var MIN_FOOD = 5;
var MAX_WORKER_FOOD = 10;

function decide() {
  var me = view[CENTRE].ant;
  var queen = me.type === QUEEN;

  if(queen && me.food > MIN_FOOD && me.food < MAX_WORKER_FOOD) {
    var m = try_all_cells((i) => {
      if(view[i].food && !foe_near(i)) {
        // Try to spawn a worker next to the food;
        // the worker will pick up the food on the next turn
        return try_all_cells_near(i, (j) => ({cell: j, type: 1}));
      }
    }, true);
    if(sanity_check(m)) {
      return m;
    }
  }

  if(!queen && me.food) {
    return best_of([
      // Look for queen
      follow_dotted_path.bind(null, QUEEN_COL, SIDE_RIGHT, DIR_FORWARDS),
      latch_to_dotted_path.bind(null, QUEEN_COL, SIDE_RIGHT),

      // Failed to find queen's trail; try following worker trails backwards
      follow_dotted_path.bind(null, WORKER_COL, SIDE_RIGHT, DIR_REVERSE),
      latch_to_dotted_path.bind(null, WORKER_COL, SIDE_RIGHT),

      // Failed to find any trail; cover ground as quickly as possible
      fast_diagonal.bind(null, AVOID_COL)
    ]);
  }

  var myCol = queen ? QUEEN_COL : WORKER_COL;
  return best_of([
    grab_nearby_food,

    // Disperse workers away from queen
    !queen && escape_dotted_path.bind(null, QUEEN_COL, SIDE_RIGHT, WORKER_COL),

    // Follow our own path
    follow_dotted_path.bind(null, myCol, SIDE_RIGHT, DIR_FORWARDS),

    // If our path looks suspicious, it could have wrapped; try to escape it
    escape_dotted_path.bind(null, myCol, SIDE_RIGHT),

    // Explore
    // Laying a path causes us to move at 2/3 c, so workers can catch up.
    lay_dotted_path.bind(null, myCol, SIDE_RIGHT, QUEEN_COL),
    start_dotted_path.bind(null, myCol, SIDE_RIGHT, QUEEN_COL),

    // Fall-back to white dots if we're inside a colour block
    lay_dotted_path.bind(null, myCol, SIDE_RIGHT, INVERT_COL),
    start_dotted_path.bind(null, myCol, SIDE_RIGHT, INVERT_COL),

    // Stuck for some reason; try to escape
    fast_diagonal.bind(null, AVOID_COL)
  ]);
}

return play_safe([
  decide,

  // No valid moves; try to find *anywhere* we can go.
  go_anywhere,

  // Try changing a nearby cell's colour for the heck of it.
  {cell: 1, color: view[1].color % 8 + 1}
]);

Semut Romawi suka membangun jalan, lurus dan cepat. Karena mereka membangun jalan bertitik, mereka dapat bergerak pada kecepatan 2/3! Jalan mereka juga menggunakan warna bersepeda sehingga mereka tahu apakah mereka mengikuti jalan maju atau mundur, dan semut pekerja menggunakan serangkaian warna yang berbeda dari ratu (pekerja mulai melahirkan setelah ratu memiliki jumlah yang tidak memalukan dari makanan tersedia). Jika digabungkan, ini berarti bahwa begitu seekor semut pekerja menemukan makanan, ia dapat melacak jejaknya sendiri ke belakang lalu menelusuri jejak sang ratu ke depan untuk mengembalikan makanannya (sambil menelusuri sebuah jalan yang dilalui semut dengan kecepatan cahaya).

Jika Anda bertanya-tanya bagaimana semut dapat mengikuti garis putus-putus meskipun hanya dapat melihat kisi 3x3 kapan saja dan tidak mengetahui arahnya sendiri: mereka menarik garis ke kanan, bukan pada jalur perjalanan yang sebenarnya. Jadi jika (misalnya) semut melihat kuadrat yang terisi ke Utara, ia tahu jalannya harus di atasnya, yang berarti arah perjalanannya harus Barat (untuk menjaga jalan di kanan). Jika semut melihat kuadrat terisi ke Timur-Timur tetapi tidak ke Barat-Utara atau Tenggara, semut itu pasti telah melewati ujung garis, jadi cat sel ke Barat-Utara dan perjalanan ke Barat. Ada lebih banyak komplikasi di sekitar pengalihan arah begitu makanan telah ditemukan, tetapi itulah intinya.

Satu hal lain yang layak disebutkan adalah bahwa ketika semut pekerja melihat makanan, mereka akan melompat pada kesempatan untuk mengambilnya tanpa memikirkan bagaimana mereka akan kembali ke rumah (bagaimanapun, semut musuh bisa siap untuk mengambilnya!). Jadi, jika semut pekerja tersesat saat membawa makanan, ia akan mengubah perilaku menjadi pola berjalan-acak, meninggalkan jejak kuning di belakangnya untuk menghindari menelusuri kembali langkah-langkahnya. Ini membantu semut menemukan kembali jejak mereka dan memperkenalkan keacakan cukup untuk menghindari beberapa loop tak terbatas yang dapat terjadi.

Itu tidak berkinerja luar biasa, dan semut memiliki kebiasaan menelusuri kembali jejak mereka sendiri (mereka cukup bodoh untuk tidak mengetahui apakah jalan yang mereka ikuti adalah yang lama, atau yang mereka sedang dalam proses drawing!), yang kadang-kadang menyebabkan loop tak terbatas.

Akhirnya, ada metode pemeriksaan kewarasan yang diterapkan pada output apa pun yang memastikan ini tidak dapat didiskualifikasi (mudah-mudahan!)


Versi yang diperbarui mengganti berjalan acak untuk perjalanan diagonal cepat, memperbaiki banyak bug, dan menambahkan beberapa upaya berjalan menggunakan jalan putih jika ada semut yang terjebak di dalam area berwarna besar. Sebagian besar bug ditemukan saat mengonversinya untuk menggunakan pemisahan kemampuan berpikir tingkat rendah / tingkat rendah.


(Inilah yang saya siapkan saat ini masih dalam versi beta )
Dave

Untuk urutan besarnya: Saya hanya menjalankan ini dan mendapat 30 makanan + 6 semut pekerja. Itu tentang konsisten dengan angka yang saya lihat saat mengembangkannya.
Dave

Itu trik yang rapi meninggalkan celah di jalan untuk meningkatkan kecepatan 50%. Juga berarti jalan-jalan sulit dikenali pada awalnya - butuh beberapa saat untuk menemukan semut Anda ... Tapi itu dengan mudah mengungguli para pemain contoh, terutama sekali para pekerja mulai kembali. Beberapa pekerja akhirnya membangun hamparan pola kotak-kotak kuning dan putih - tidak yakin apakah itu bagian dari rencana?
trichoplax

1
@trichoplax oops; Saya lupa memasukkannya ke dalam penjelasan (lupa mereka melakukannya!). Saya sudah memperbarui penjelasannya. Singkatnya: ini adalah bagian dari rencana, tetapi itu bukan rencana yang bagus.
Dave

Penjelasan hebat!
trichoplax

7

Monorel

Masih dalam proses, tetapi saya ingin mendorong sesuatu sekarang hanya karena saya paranoid bahwa kompetisi akan berakhir sebelum saya menyelesaikan sebaliknya.

var c0=5 //red
var c1=4 //cyan
var c2=6 //green
var c3=3 //magenta
var c4=7 //blue
var c5=8 //black
var c6=2 //yellow
var cN=1 //white

var ws=4 //support
var wb=3 //bodyguard
var wg=1 //gather
var wq=5 //queen

var v=[[0, 1, 2, 3, 4, 5, 6, 7, 8],
       [8, 7, 6, 5, 4, 3, 2, 1, 0],
       [6, 3, 0, 7, 4, 1, 8, 5, 2],
       [2, 5, 8, 1, 4, 7, 0, 3, 6]]

var x1=[0,2,6,8]
var x2=[8,6,2,0]

var r1=[1,7,3,5]
var r2=[5,3,7,1]

switch(view[4].ant.type)
{
 case 5: return queen()
 case 4: return support()
 case 3: return bodyguard()
 case 1: return gather()
 default: return {cell:4}
}

function queen()
{
 if(fAlly(ws)<0)
 {
  if(view[4].color==c1&&view[4].ant.food>0)
  {
   var o=findOrient(c1,c2,c3)
   if(sOpen(v[o][7])) return {cell:v[o][7],type:ws}
   return doThing()
  }
  else if(view[4].color==c0&&view[4].ant.food>=6)
  {
   var o=findOrient(c1,c2,c3)
   if(view[v[o][1]].color!=c1) return {cell:v[o][1],color:c1}
   if(view[v[o][0]].color!=c2) return {cell:v[o][0],color:c2}
   if(view[v[o][2]].color!=c3) return {cell:v[o][2],color:c3}
   if(sOpen(v[o][7])) return {cell:v[o][7],type:ws}
  }
  return wander(c0)
 }
 else
 {
  if(view[4].color==c0&&view[4].ant.food<7)
  {
   if(view[4].ant.food >=5)
   {
    var o=findOrient(c1,c2,c3)
    if(sOpen(v[o][1])) return {cell:v[o][1],type:wb}
    if(sOpen(v[o][0])) return {cell:v[o][0],type:wb}
    if(sOpen(v[o][2])) return {cell:v[o][2],type:wb}
   }
   if(view[4].ant.food>0)
   {
    var op=fOpen()
    if(op>=0) return {cell:op,type:wg}
   }
  }
  return doThing()
 }
}

function doThing()
{
 if(view[4].color!=c1) return {cell:4,color:c1}
 var o=findOrient(c1,c2,c3)
 if(view[4].ant.food>0&&fAlly(wg)>=0&&o==0)
 {
  var op=fOpen()
  if(op>=0) return {cell:op,type:wg}
 }
 if(view[v[o][1]].color!=c1&&pootis(v[o][1])) return {cell:v[o][1],color:c1}
 if(view[v[o][0]].color!=c2&&pootis(v[o][0])) return {cell:v[o][0],color:c2}
 if(view[v[o][2]].color!=c3&&pootis(v[o][2])) return {cell:v[o][2],color:c3}
 if(mOpen(v[o][1])) return {cell:v[o][1]}
 if(view[v[o][3]].color!=c2&&pootis(v[o][3])) return {cell:v[o][3],color:c2}
 if(view[v[o][5]].color!=c3&&pootis(v[o][5])) return {cell:v[o][5],color:c3}
 if(view[v[o][6]].color!=c2&&pootis(v[o][6])) return {cell:v[o][6],color:c2}
 if(view[v[o][8]].color!=c3&&pootis(v[o][8])) return {cell:v[o][8],color:c3}
 if(view[v[o][7]].color!=c1&&pootis(v[o][7])) return {cell:v[o][7],color:c1}
 return {cell:4}
}

function support()
{
 var o = findOrient(c1,c2,c3)
 var p = fAlly(wq)

 switch(p)
 {
  case v[o][0]:
   //if(mOpen(v[o][3])) return {cell:v[o][3]}
   //if(mOpen(v[o][1])) return {cell:v[o][1]}
   break
  case v[o][1]:
   //if(view[v[o][0]].food&&mOpen([v[o][0]])) return {cell:v[o][0]}
   //if(view[v[o][2]].food&&mOpen([v[o][2]])) return {cell:v[o][2]}
   break
  case v[o][2]:
   //if(mOpen(v[o][5])) return {cell:v[o][5]}
   //if(mOpen(v[o][1])) return {cell:v[o][1]}
   break
  case v[o][3]:
   //if(mOpen(v[o][6])) return {cell:v[o][6]}
   break
  case v[o][5]:
   //if(mOpen(v[o][8])) return {cell:v[o][8]}
   break
  case v[o][6]: break
  case v[o][7]: break
  case v[o][8]: break
  default:
   if(sOpen(v[o][1])) return {cell:v[o][1]}
   break
 }
 return {cell:4}
}

function bodyguard()
{
 var o=0
 switch(view[4].color)
 {
  case c1: o=findOrient(c1,c2,c3);break
  case c2: o=findOrient(c2,c6,c1);break
  case c3: o=findOrient(c3,c1,c6);break
  default: return {cell:4}
 }
 var p = fAlly(wq)
 if(view[4].ant.food>0)
 {
  switch(p)
  {
   case v[o][0]:
    if(sOpen(v[o][1])) return {cell:v[o][1]}
    if(sOpen(v[o][3])) return {cell:v[o][3]}
    break
   case v[o][1]:
    if(sOpen([v[o][0]])) return {cell:v[o][0]}
    if(sOpen([v[o][2]])) return {cell:v[o][2]}
    break
   case v[o][2]:
    if(sOpen(v[o][1])) return {cell:v[o][1]}
    if(sOpen(v[o][5])) return {cell:v[o][5]}
    break
   case v[o][3]:
    if(sOpen([v[o][0]])) return {cell:v[o][0]}
    if(sOpen([v[o][1]])) return {cell:v[o][1]}
    break
   case v[o][5]:
    if(sOpen([v[o][2]])) return {cell:v[o][2]}
    if(sOpen([v[o][1]])) return {cell:v[o][1]}
    break
   case v[o][6]:
    if(sOpen([v[o][0]])) return {cell:v[o][0]}
    if(sOpen([v[o][1]])) return {cell:v[o][1]}
    if(sOpen([v[o][3]])) return {cell:v[o][3]}
    break
   case v[o][7]:
    if(sOpen([v[o][1]])) return {cell:v[o][1]}
    if(sOpen([v[o][0]])) return {cell:v[o][0]}
    if(sOpen([v[o][2]])) return {cell:v[o][2]}
    break
   case v[o][8]:
    if(sOpen([v[o][2]])) return {cell:v[o][2]}
    if(sOpen([v[o][1]])) return {cell:v[o][1]}
    if(sOpen([v[o][5]])) return {cell:v[o][5]}
    break
   default:
  }
 }
 else
 {
  switch(p)
  {
   case v[o][0]:
    if(mOpen(v[o][1])) return {cell:v[o][1]}
    if(mOpen(v[o][3])) return {cell:v[o][3]}
    break
   case v[o][1]:
    if(view[v[o][2]].food&&mOpen([v[o][2]])) return {cell:v[o][2]}
    if(mOpen([v[o][0]])) return {cell:v[o][0]}
    if(mOpen([v[o][2]])) return {cell:v[o][2]}
    break
   case v[o][2]:
    if(mOpen(v[o][1])) return {cell:v[o][1]}
    if(mOpen(v[o][5])) return {cell:v[o][5]}
    break
   case v[o][3]:
    if(view[v[o][1]].food&&mOpen([v[o][1]])) return {cell:v[o][1]}
    if(mOpen([v[o][0]])) return {cell:v[o][0]}
    if(mOpen([v[o][1]])) return {cell:v[o][1]}
    break
   case v[o][5]:
    if(view[v[o][1]].food&&mOpen([v[o][1]])) return {cell:v[o][1]}
    if(mOpen([v[o][2]])) return {cell:v[o][2]}
    if(mOpen([v[o][1]])) return {cell:v[o][1]}
    break
   case v[o][6]:
    if(view[v[o][1]].food&&mOpen([v[o][1]])) return {cell:v[o][1]}
    if(mOpen([v[o][0]])) return {cell:v[o][0]}
    if(mOpen([v[o][1]])) return {cell:v[o][1]}
    if(mOpen([v[o][3]])) return {cell:v[o][3]}
    break
   case v[o][7]:
    if(view[v[o][0]].food&&mOpen([v[o][0]])) return {cell:v[o][0]}
    if(view[v[o][2]].food&&mOpen([v[o][2]])) return {cell:v[o][2]}
    if(mOpen([v[o][1]])) return {cell:v[o][1]}
    if(mOpen([v[o][0]])) return {cell:v[o][0]}
    if(mOpen([v[o][2]])) return {cell:v[o][2]}
    break
   case v[o][8]:
    if(view[v[o][1]].food&&mOpen([v[o][1]])) return {cell:v[o][1]}
    if(mOpen([v[o][2]])) return {cell:v[o][2]}
    if(mOpen([v[o][1]])) return {cell:v[o][1]}
    if(mOpen([v[o][5]])) return {cell:v[o][5]}
    break
   default:
  }
 }
 if(view[4].color==c1)
 {
  if(view[v[o][1]].color!=c1&&pootis(v[o][1])) return {cell:v[o][1],color:c1}
  if(view[v[o][0]].color!=c2&&pootis(v[o][0])) return {cell:v[o][0],color:c2}
  if(view[v[o][2]].color!=c3&&pootis(v[o][2])) return {cell:v[o][2],color:c3}
  if(view[v[o][3]].color!=c2) return {cell:v[o][3],color:c2}
  if(view[v[o][5]].color!=c3) return {cell:v[o][5],color:c3}
  if(view[v[o][6]].color!=c2) return {cell:v[o][6],color:c2}
  if(view[v[o][8]].color!=c3) return {cell:v[o][8],color:c3}
  if(view[v[o][7]].color!=c1) return {cell:v[o][7],color:c1}
 }
 return {cell:4}
}

function gather()
{
 if(view[4].ant.food>0)
 {
  if(view[4].color==c1&&(testSQ(c1,c2)||testSQ(c1,c3))) return gDoThing(c1,c3,c2)
  if(view[4].color==c2&&testSQ(c2,c1)) return gDoThing(c2,c1,c6)
  if(view[4].color==c3&&testSQ(c3,c1)) return gDoThing(c3,c6,c1)
  return wander(c5)
 }
 var n=fAlly(wq)
 if(n<0) return wander(c4)
 switch(n)
 {
  case 0:
   if(mOpen(8)) return {cell:8}
   if(mOpen(7)) return {cell:7}
   if(mOpen(5)) return {cell:5}
   break
  case 1:
   if(mOpen(6)) return {cell:6}
   if(mOpen(8)) return {cell:8}
   if(mOpen(7)) return {cell:7}
   break
  case 2:
   if(mOpen(6)) return {cell:6}
   if(mOpen(3)) return {cell:3}
   if(mOpen(7)) return {cell:7}
   break
  case 3:
   if(mOpen(8)) return {cell:8}
   if(mOpen(2)) return {cell:2}
   if(mOpen(5)) return {cell:5}
   break
  case 5:
   if(mOpen(0)) return {cell:0}
   if(mOpen(6)) return {cell:6}
   if(mOpen(3)) return {cell:3}
   break
  case 6:
   if(mOpen(2)) return {cell:2}
   if(mOpen(5)) return {cell:5}
   if(mOpen(1)) return {cell:1}
   break
  case 7:
   if(mOpen(2)) return {cell:2}
   if(mOpen(0)) return {cell:0}
   if(mOpen(1)) return {cell:1}
   break
  case 8:
   if(mOpen(0)) return {cell:0}
   if(mOpen(1)) return {cell:1}
   if(mOpen(3)) return {cell:3}
   break
  default:
   break
 }
 return {cell:4}
}

function testSQ(ac1,ac2)
{
 for(var i=0;i<4;i++)
 {
  if(view[r1[i]].color==ac1)
  {
   if(view[v[i][0]].color==ac2&&view[v[i][3]].color==ac2) return 1
   if(view[v[i][2]].color==ac2&&view[v[i][5]].color==ac2) return 1
  }
 }
 return 0
}

function gDoThing(ac1,ac2,ac3)
{
 var o=findOrient(ac1,ac2,ac3)
 if(view[v[o][1]].color!=ac1&&pootis(v[o][1])) return {cell:v[o][1],color:ac1}
 if(view[v[o][0]].color!=ac2&&pootis(v[o][0])) return {cell:v[o][0],color:ac2}
 if(view[v[o][2]].color!=ac3&&pootis(v[o][2])) return {cell:v[o][2],color:ac3}
 if(sOpen(v[o][1])) return {cell:v[o][1]}
 if(ac1==c2)
 {
  if(sOpen(v[o][0])) return {cell:v[o][0]}
  if(sOpen(v[o][3])) return {cell:v[o][3]}
  if(sOpen(v[o][6])) return {cell:v[o][6]}
 }
 else if(ac1==c3)
 {
  if(sOpen(v[o][2])) return {cell:v[o][2]}
  if(sOpen(v[o][5])) return {cell:v[o][5]}
  if(sOpen(v[o][8])) return {cell:v[o][8]}
 }
 else
 {
  if(sOpen(v[o][0])) return {cell:v[o][0]}
  if(sOpen(v[o][2])) return {cell:v[o][2]}
  if(sOpen(v[o][3])) return {cell:v[o][3]}
  if(sOpen(v[o][5])) return {cell:v[o][5]}
 }
 return {cell:4}
}

function pootis(p)
{
 var a=view[p].ant
 if(a!=null&&a.friend&&a.type==wg&&(view[p].color==c4||view[p].color==c5)) return 0
 return 1
}

function fAlly(t)
{
 var a=view[4].ant
 for(var i=0;i<9;i++)
 {
  if(i==4) i++
  a=view[i].ant
  if(a!=null&&a.friend&&a.type==t) return i
 }
 return -1
}

function fOpen()
{
 for(var i=0;i<9;i++)
 {
  if(i==4) i++
  if(sOpen(i)) return i
 }
 return -1
}

function sOpen(p)
{
 return (view[p].ant==null&&!view[p].food)
}

function mOpen(p)
{
 return (view[p].ant==null)
}

function wander(ac)
{
 if(view[4].ant.type==5||view[4].ant.food==0)
 {
  var vf = vFood()
  if(vf>=0) return {cell:vf}
  if(view[4].color!=ac) return {cell:4,color:ac}
  for(var i=0;i<4;i++)
  {
   if(view[x1[i]].color==ac&&view[x2[i]].color!=ac&&view[x2[i]].color!=c1&&mOpen(x2[i])) return {cell:x2[i]}
  }
  for(var i=0;i<4;i++)
  {
   if(mOpen(x1[i])&&view[x1[i]].color!=c1) return {cell:x1[i]}
  }
 }
 else
 {
  if(view[4].color!=ac) return {cell:4,color:ac}
  for(var i=0;i<4;i++)
  {
   if(view[x1[i]].color==ac&&view[x2[i]].color!=ac&&sOpen(x2[i])) return {cell:x2[i]}
  }
  for(var i=0;i<4;i++)
  {
   if(sOpen(x1[i])) return {cell:x1[i]}
  }
 }
 return {cell:4}
}

function findOrient(ac1,ac2,ac3)
{
 var w=[0,0,0,0]
 w[0]=DI(view[0].color,view[1].color,view[2].color,0,ac1,ac2,ac3)
 w[1]=DI(view[8].color,view[7].color,view[6].color,1,ac1,ac2,ac3)
 w[2]=DI(view[6].color,view[3].color,view[0].color,2,ac1,ac2,ac3)
 w[3]=DI(view[2].color,view[5].color,view[8].color,3,ac1,ac2,ac3)
 var t=[0,0,0,0]
 for(var i=0;i<4;i++)
 {
  switch(w[i])
  {
   case 4: t[0]++;break
   case 5: t[1]++;break
   case 6: t[2]++;break
   case 7: t[3]++;break
   case 8: t[0]+=2;break
   case 9: t[1]+=2;break
   case 10: t[2]+=2;break
   case 11: t[3]+=2;break
   case 12: t[0]+=3;break
   case 13: t[1]+=3;break
   case 14: t[2]+=3;break
   case 15: t[3]+=3;break
   default: break
  }
 }
 var m=Math.max(...t)
 for(var i=0;i<4;i++)
 {
  if(t[i]==m) return i
 }
 return 0
}

function DI(v1,v2,v3,d,ac1,ac2,ac3)
{
 var t=[0,0,0,0]
 switch(v1)
 {
  case ac2: t[0]++;t[3]++;break
  case ac3: t[1]++;t[2]++;break
  default: break
 }
 switch(v2)
 {
  case ac1: t[0]++;t[1]++;break
  case ac2: t[3]++;break
  case ac3: t[2]++;break
  default: break
 }
 switch(v3)
 {
  case ac2: t[1]++;t[3]++;break
  case ac3: t[0]++;t[2]++;break
  default: break
 }
 var m=Math.max(...t)
 if(m==0) return 0
 var n=0
 for(var i=0;i<4;i++)
 {
  if(t[i]==m){n=i;break}
 }
 if((d==2&&n==2)||(d==3&&n==3)) n=1
 else if((d==2&&n==3)||(d==3&&n==2)) n=0
 else n^=d
 return m*4+n
}

function vFood()
{
 for(var i=0;i<9;i++)
 {
  if(view[i].food) return i
 }
 return -1
}

function nColor(c)
{
 var t=0
 for(var i=0;i<9;i++)
 {
  if(view[i].color==c) t++
 }
 return t
}

Untuk meringkas, sang ratu bergerak dalam garis lurus, menciptakan pola di belakangnya. Seorang pekerja pendukung diciptakan untuk bergerak tepat di belakangnya dan menjaga dia dalam antrean. Ada juga pengumpul, yang berburu makanan serigala a la lone atau ratu tunggal, yang akan terus berjalan sampai mereka mendapatkan sesuatu untuk dibawa kembali ke rel, yang kemudian akan membawa mereka kembali ke ratu.

Baiklah, ini versi 2. Sekarang pengumpul benar-benar melakukan sesuatu.

Versi 2.1 sekarang. Tanda Kuning / Cyan telah diputar untuk mencegah Black Hole memakan seluruh jejak setiap kali mereka muncul bersama dalam permainan yang sama.

Versi 3. Bodyguard telah ditambahkan untuk memberikan perlindungan ekstra dari depan (dan mengambil makanan di rel), sementara pengumpul sekarang menggambar tepi hitam untuk menangkal penghapus jejak dan mungkin membantu melindungi dari Black Hole. Ratu juga menghindari ubin cyan dalam mode pengumpul untuk mencegah penyebaran prematur.

Baiklah, waktu untuk versi 3.1. Warna telah diubah lagi untuk menjaga jejak penghapus di teluk tanpa memicu kemarahan Wildfire. Pengumpul juga telah diedit untuk terus dan mengidentifikasi rel lebih baik (kurang bailing / membuat rel baru). Ada beberapa hal kecil lainnya di sana yang juga tidak bisa saya singgung.

Versi 3.2: Memperbaiki beberapa masalah dengan pengawal. Ia tidak lagi mencoba mengambil makanan jika sudah ada, tidak mudah macet, dan akan menarik rel jika tidak dapat menemukan ratu.


Selama ada entri / suntingan baru saya akan menjalankan turnamen baru untuk melihat bagaimana tampilan leaderboard. Tidak ada tanggal penutupan. Turnamen saat ini mungkin memiliki beberapa hari tersisa sebelum bertemu di tempat 1, 2 dan 3 yang unik, maka saya akan memulai turnamen baru termasuk pemain baru ini. Saya khawatir itu akan memakan waktu lebih dari seminggu sebelum itu menunjukkan posisi apa yang Anda capai, tetapi saya akan memberi tahu Anda saat itu terjadi.
trichoplax

@QuoteBeta Saya mengerti paranoia Anda ... Saya baru saja melihat tantangan ini jadi saya sedang dalam proses membuat entri sebelum momentum tantangan berhenti.
Moogie

Baiklah kalau begitu. Saya tidak yakin apakah ini akan segera berakhir atau tidak, bagaimana dengan pembaruan yang sedikit melambat.
Kutipan dari

Kebanyakan orang mungkin sudah selesai, tetapi kami menunggu di papan peringkat. Dan itu perlu waktu.
Draco18s

Sepertinya semut Anda mungkin menginginkan beberapa kode pembersihan jejak saya (lihat Black Hole, bagaimana semut yang hilang di bagian dalam membersihkan jejak lama). i.stack.imgur.com/DTmA6.png
Draco18s

6

Ratu Claustrophobic

Pendekatan khusus ratu yang melakukan jalan acak sembari berusaha menghindari area berwarna. Bukan pesaing teratas, tetapi cukup berhasil dan anti-perusak. Parameter tweaker sedang berlangsung.

var i, j
var orthogonals = [1, 3, 7, 5]  // These are the non-diagonal cells
var move;
var scores = [];   // An array of how desirable each potential move is
var score, neighbor, claustrophobia, newColor;
var crowdedNeighbors = null;   // How many diagonal neighbors are colored CROWDED?
var runningFrom = null;    // When in running phase, which direction did we come from?
var runningTo = null;      // When in running phase, which direction should we head?

// Assign color magic numbers to variables
var EMPTY = 1;
var VISITED = 4;
var CROWDED = 7;
var RUNNING = 8;

function neighbors(cell) {
    switch (cell) {
        case 0: return [1, 3];
        case 1: return [0, 2];
        case 2: return [1, 5];
        case 3: return [0, 6];
        case 4: return orthogonals;
        case 5: return [2, 8];
        case 6: return [3, 7];
        case 7: return [6, 8];
        case 8: return [7, 5];
        default: return null;
    }
}

function isHungry(ant) {
    if (ant.type === 5 || ant.food === 0) {
        return true;
    } else {
        return false;
    }
}

// Color own cell based on the number of neighbors that are colored
claustrophobia = 0;
for (i=0; i<9; i++) {
    if (view[i].color !== EMPTY || i === 4) {
        claustrophobia++;
    }
    if (i % 2 === 0 && i !== 4) {
        if (view[i].color === CROWDED) {
            crowdedNeighbors++;
        } else if (view[i].color === RUNNING) {
            crowdedNeighbors++;
            runningFrom = i;
        }
    }
}

if (claustrophobia > 4) {
    if (crowdedNeighbors > 1 || runningFrom !== null) {
        // We're entering or currently in a straight-line running state
        // in which we keep going until we find sufficient whitespace
        newColor = RUNNING;
    } else {
        newColor = CROWDED;
    }
} else {
    newColor = VISITED;
}
if (view[4].color !== newColor) {
    return {cell:4, color:newColor}
}

// If we've already colored the current cell properly, and we're in running mode,
// then move diametrically away from the runningFrom cell
switch (runningFrom) {
    case 0:
        runningTo = 8;
        break;
    case 2:
        runningTo = 6;
        break;
    case 6:
        runningTo = 2;
        break;
    case 8:
        runningTo = 0;
        break;
    default:
        break;
}

// Calculate a score for each potential move
// Lower numbers are better; null means illegal move
// Unexplored areas are better; food is the best (as long as ant can eat); don't move onto other ants
for (i=0; i<9; i++) {
    // Base score of tile is 2 times color of tile
    score = 2 * (view[i].color);
    // Add colors of neighboring tiles
    for (neighbor of neighbors(i)) {
        score += view[neighbor].color;
    }
    // Give very good score to runningTo tile, unless it's also RUNNING color
    if (i === runningTo && view[i].color !== RUNNING) {
        score -= 4;   // Magic number, possibly to be tweaked
    }
    if (i!==4 && view[i].ant) {
        // Tile contains another ant; give very bad score
        score = null;
    } else if (view[i].food) {
        // If a tile contains food, it's either highly desirable if the ant can eat, or illegal if it can't
        if (isHungry(view[4].ant)) {
            // Ant can eat; give food tile very good score
            score = -1;
        } else {
            // Ant cannot eat; give food tile very bad score
            score = null;
        }
    }
    scores.push(score);
}

// Select best move based on the scores array
move = 4;   // By default, stay put (this probably won't be the best move)
for (i=0; i<9; i++) {
    if (scores[i] !== null && scores[i] < scores[move]) {
        move = i;
    }
}

return {cell:move};

Ratu meletakkan jejak cyan saat dia bergerak. Dia memprioritaskan pindah ke sel kosong dan sel dengan tetangga kosong. Cara matematika keluar saat ini, ini menghasilkan sebagian besar gerakan diagonal dan pola kotak-kotak. Jika sel saat ini memiliki empat atau lebih tetangga berwarna, itu berwarna biru, bukan cyan; sel biru dihindari dengan prioritas lebih tinggi. Akhirnya, jika ratu berdekatan dengan dua sel biru, ia mulai pada garis diagonal hitam hingga mencapai area terbuka lagi.

masukkan deskripsi gambar di sini


Tidak tahan rusak; trail-eraser dapat menghapus jejaknya, yang akan membuat semut berputar
putar

1
Eh, dalam kotak?
pppery

1
@pperry aku berkata " cukup ... tamper-proof." ;) Satu-satunya yang dapat dilakukan Trail-Eraser adalah membuatnya lebih mungkin untuk menutupi tanah yang sudah dibahas; tetapi bisa dengan mudah terus bergerak ke wilayah yang belum dijelajahi.
DLosc

6

Hoppemaur

Pertama-tama saya ingin mengucapkan terima kasih kepada Trichoplax karena membuat tantangan yang luar biasa ini, yang membuat saya mendaftar ke situs ini dan mulai pemrograman dalam javascript. Saya ingin juga berterima kasih kepada orang-orang yang masih bersembunyi di ruang obrolan pertanyaan ini, bahkan tepat 3 bulan setelah ditanyakan.

Bot saya bukan pesaing serius untuk tempat pertama, tetapi seorang ratu tunggal / diri menghindari pendekatan berjalan acak, sedikit seperti Ratu Claustrophobic melompat .

Detail bot Hoppemaur dengan pola yang dilukis

Kode

//Hoppemaur
//å hoppe: to jump
//maur: ant

var WHITE = 1;
var OWN = 2;
var FOOD = 6;
var ESCAPE = 3;
var paint = 0;
var score = {0:2,1:-3,2:2,3:-3,4:-1,5:-3,6:2,7:-3,8:2}; 
// The ant should only walk diagonal (except when feeding)
var highest = 0;
var scoreindex;
var diagonals = [0, 2, 6, 8];
var diagonalsandself = [0, 2, 4, 6, 8]
var inversdiagonals = [8, 6, 2, 0];
var orthogonals = [1, 3, 5, 7];
var inverseorthogonals = [7, 5, 3, 1];
var rotate1orthogonals = [3, 1, 7, 5];
var rotate2orthogonals = [5, 7, 1, 3];

//checks if one of the diagonals or the own tile are painted
function checkforemptypattern() {
  for (var i=0; i<diagonalsandself.length; i++) {
    if (view[diagonalsandself[i]].color == OWN){
     return false;}
  }
  return true;
}

//counts the diagonals painted in the requestest colour
function checktrapped(pattern) {
  var diags=0;
  for (var i=0; i<diagonals.length; i++) {
    if (view[diagonals[i]].color == pattern){
     diags=diags+1;
    }
  }
return diags;
}

//Biggest threat to this ant is food on orthogonals,
//it messes up the pattern if not dealt with it
if (view[4].color !== FOOD){
    for (var i=0; i<orthogonals.length; i++) {
        if (view[orthogonals[i]].food) {
            return {cell:4, color:FOOD};
            }
        }
    }

if (view[4].color == FOOD){
    for (var i=0; i<orthogonals.length; i++) {
        if (view[orthogonals[i]].food) {
        if (!view[orthogonals[i]].ant){
            return {cell:orthogonals[i]};
            }
        }
        }
    }

//If food shows up on diagonals while out of pattern,
//before grabbing food, the pattern must be painted
for (var i=0; i<diagonals.length; i++){
    if (view[diagonals[i]].food){
        if (checkforemptypattern()){
            return {cell:4, color: OWN}
            }
        }
    }


//Otherwise, food can easily be grabbed if not ant in way
for (var i=0; i<9; i++) {
    if (view[i].food) {
        if(!view[i].ant){
        return {cell:i}
        }
    }
}


//After food has been grabbed orthogonal, back to food pile
if (view[4].color == WHITE){
    for (i=0; i<orthogonals.length; i++) {
        if (view[orthogonals[i]].color == FOOD && checktrapped(FOOD) == 0 && view[inverseorthogonals[i]].color !== FOOD && view[rotate1orthogonals[i]].color !== FOOD && view[rotate2orthogonals[i]].color !== FOOD){
                 if (!view[orthogonals[i]].ant){
                     return {cell:orthogonals[i]};
                     }
                 }
        }
    }

//First part of scoring
// Scoring to determine next move
// Scoring everything higher than own pattern and escape
for (var i=0; i<9; i++) {
  if (view[i].color !== OWN) {
      score[i] = score[i]+3;
  }
  if (view[i].color !== ESCAPE){
      score[i] = score[i]+5;
  }
}

// Scoring while in painted area (f.e. wildfire)

var l = 0;
for (var i=2; i<9; i++) {
                          var k = 0;
    for (var j=0; j<9; j++) {
                             if (view[j].color == i) {
            k=k+1;
            if (k > 6){
            paint=i;
            }
        if (view[j].color !==WHITE) {
          l=l+1;

        }
    }
}
}

if (paint !== OWN && l >7) {
    for (var i=0; i<diagonals.length; i++){
        if (view[diagonals[i]].color == OWN) {
            score[inversdiagonals[i]]=score[inversdiagonals[i]]+7;
            }
        if (view[diagonals[i]].color == WHITE) {
          score[diagonals[i]]=score[diagonals[i]]+7
        }
        }
    }


if (paint == OWN && l >7) {
    for (var i=0; i<diagonals.length; i++){
        if (view[diagonals[i]].color == ESCAPE) {
            score[inversdiagonals[i]]=score[inversdiagonals[i]]+7;
            }
        if (view[diagonals[i]].color == WHITE) {
          score[diagonals[i]]=score[diagonals[i]]+7
        }
        }
    }

// the following might lead to some traps?
// score diagonals adjactant to white higher
  if (view[1].color === WHITE) {
    score[0] = score[0]+1;
    score[2] = score[2]+1;
   }
  if (view[3].color === WHITE) {
    score[0] = score[0]+1;
    score[6] = score[6]+1;
   }
  if (view[5].color === WHITE) {
    score[2] = score[2]+1;
    score[8] = score[8]+1;
   }
  if (view[7].color === WHITE) {
    score[6] = score[6]+1;
    score[8] = score[8]+1;
   }


//Don't move next to others, they steal your food!
  if (view[0].ant || view[1].ant || view[2].ant){
      score[6] = score [6]+10;
      score[8] = score [8]+10;
  }

  if (view[0].ant || view[3].ant || view[6].ant){
      score[2] = score [2]+10;
      score[8] = score [8]+10;
  }

  if (view[6].ant || view[7].ant || view[8].ant){
      score[0] = score [0]+10;
      score[2] = score [2]+10;

  }
   if (view[2].ant || view[5].ant || view[8].ant){
      score[0] = score [0]+10;
      score[6] = score [6]+10;

  }
//don't step on others!
for (var i=0; i<9; i++) {
  if (i!==4 && view[i].ant) {
        score[i] = -5;
 }
}

//end of scoring, calculate best
for (var i=0; i<9; i++) {
  if (score[i] > highest) {
    highest = score[i];
    scoreindex = i;
    }
  }

//Basic enemy avoidance
for (var i=0; i<9; i++) {
  if (i!==4 && view[i].ant) {
        return {cell:scoreindex}
        }
  }

//basic movement

//when surrounded by other paint
if (paint == ESCAPE && l>7){
    if(view[4].color == OWN){
      return{cell:scoreindex}
    }
}

if (paint !== OWN && paint !== 0 && l>7){
  if(view[4].color !== OWN){
    return{cell:4, color:OWN}
  }
}


if (paint == OWN && l>7){
  if(view[4].color !== ESCAPE){
    return{cell:4, color:ESCAPE}
  }
}

//a) when off pattern
if (view[4].color !== OWN) {
    if (view[4].color == ESCAPE){
         if (checktrapped(ESCAPE)==4){
            return{cell:scoreindex}
            }
        }
        if (view[4].color == ESCAPE){
         if (checktrapped(ESCAPE)==3){
            return{cell:scoreindex}
            }
        }
    if (checkforemptypattern()) {
    return{cell:4, color:OWN};
    }

    //Am I trapped? Different possible traps follow here
    if (view[4].color !== ESCAPE){
         if (checktrapped(OWN)==4){
            return{cell:4, color:ESCAPE}
            }
    }
    if (view[4].color !== ESCAPE){
         if (checktrapped(OWN)==3 && checktrapped(ESCAPE)==1){
            return{cell:4, color:ESCAPE}
            }
    }
    if (view[4].color !== ESCAPE){
         if (checktrapped(OWN)==2 && checktrapped(ESCAPE)==1){
            return{cell:4, color:ESCAPE}
            }
    }
    if (view[4].color !== ESCAPE){
         if (checktrapped(OWN)==2 && checktrapped(ESCAPE)==2){
            return{cell:4, color:ESCAPE}
            }
    }
    if (view[4].color !== ESCAPE){
         if (checktrapped(OWN)==1 && checktrapped(ESCAPE)==2){
            return{cell:4, color:ESCAPE}
            }
    }
    if (view[4].color !== ESCAPE){
         if (checktrapped(OWN)==1 && checktrapped(ESCAPE)==1){
            return{cell:4, color:ESCAPE}
            }
    }
    if (view[4].color !== ESCAPE){ //when the orthogonals are painted, some other guy was here before and movement traps are likely
         if (view[1].color == OWN || view[7].color == OWN || view[3].color == OWN || view[5].color == OWN){
            return{cell:4, color:ESCAPE}
            }
    }
    if (view[4].color !== ESCAPE){ //when the orthogonals are painted, some other guy was here before and movement traps are likely
         if (view[1].color == ESCAPE || view[7].color == ESCAPE || view[3].color == ESCAPE || view[5].color == ESCAPE){
            return{cell:4, color:ESCAPE}
            }
    }
}

//b) when on pattern check surroundings for escape route
if (checktrapped(ESCAPE)==3){
    return{cell:4, color:ESCAPE}
}
if (checktrapped(ESCAPE)==4){
    return{cell:4, color:ESCAPE}
}

//otherwise just move on
return{cell:scoreindex}

Penjelasan

Gagasan utama bot ini adalah mempersingkat waktu pengecatan sambil tetap dapat mengidentifikasi area yang sudah dikunjungi. Untuk mencapai ini, ratu biasanya hanya akan melukis setelah setiap gerakan kedua. Namun informasi arah gerakan hilang dengan melakukannya, menghasilkan pola lompatan yang tidak terarah. Untuk menghindari jalan acak yang benar, sang ratu menghargai ubin yang tidak dicat lebih tinggi dari yang sudah dicat dan dalam kasus mundur, menggunakan warna merah muda untuk memberi tanda pada dirinya bahwa dia ada di sana sebelumnya. Mekanisme penilaian yang menggabungkan hal yang disebutkan di atas memberikan tulang punggung pergerakan umum.

Satu risiko besar adalah tumpukan makanan di luar pola pergerakan diagonalnya. Jika dia tersandung pada itu, dia akan menggunakan warna hijau untuk menandai titik asalnya bahwa dia akan kembali setelah mengambil makanan untuk tidak keluar dari pola. Bagian terakhir dari bot berhubungan dengan situasi yang berbeda dimana warna pink melarikan diri akan digunakan.

Sebagai tambahan, penghindaran musuh dasar dan gerakan diagonal di area yang sudah dicat juga termasuk (tetapi belum benar-benar diuji).

Last but not least, bot menggambar beberapa pola abstrak yang indah, ketika dibiarkan sendiri:

Pola ikhtisar bot Hoppemaur di akhir pertandingan


3
Selamat datang di situs ini! Tantangan KoTH seringkali paling mudah untuk didobrak, di sini, meskipun jauh lebih jarang. Saya pikir ada tiga atau empat yang lebih tua dengan kontroler masih tersedia yang bisa menarik untuk dicoba, bahkan jika mereka secara resmi berakhir. CodeBots 4 (bot yang menyuntikkan kode mereka ke satu sama lain) dan Tahanan Dillema 3 (cawan petri). Juga Pertempuran Persekutuan dan Pandemi Seluruh Dunia. Saya tahu PD menggunakan Python dan saya pikir CB ada di Jawa, tidak ingat yang lain.
Draco18s

Terima kasih, @ Draco18s! Saya mungkin akan memeriksa tantangan-tantangan yang lebih tua itu, tetapi tidak sebelum saya berhasil membangun bot semut koloni :-)
Pelle Lundkvist

6

Formasi

Kiriman ini di-host di repositori github .

var marcher_count;var gatherer_count;var excess_gatherers;var tcell;var lh_cell;var rh_cell;var ant_off;var alt_cell;var cell_off;function debug(message)
{}
const MARCHER_A=1;const MARCHER_B=2;const GATHERER=3;const QUEEN=5;const S_END=[6,5,7,4,0,2,1,3];const S_FRONT=[7,5,6,0,4,1,3,2];const S_SIDE=[7,3,5,1,6,2,0,4];const S_GATHERER=[7,6,5,4,0,3,2,1];const SCAN_MOVES=[0,1,2,3,5,6,7,8];const CORNERS=[0,2,6,8];const EDGES=[1,3,5,7];const CCW=[[0,3,6,7,8,5,2,1],[1,0,3,6,7,8,5,2],[2,1,0,3,6,7,8,5],[3,6,7,8,5,2,1,0],[4,4,4,4,4,4,4,4],[5,2,1,0,3,6,7,8],[6,7,8,5,2,1,0,3],[7,8,5,2,1,0,3,6],[8,5,2,1,0,3,6,7]];const NEARS=[[6,5,3,5,4,2,3,2,1],[5,6,5,4,5,4,2,3,2],[3,5,6,2,4,5,1,2,3],[5,4,2,6,5,3,5,4,2],[4,5,4,5,6,5,4,5,4],[2,4,5,3,5,6,2,4,5],[3,2,1,5,4,2,6,5,3],[2,3,2,4,5,4,5,6,5],[1,2,3,2,4,5,3,5,6]];const SAN_ORD=[[1,3,6,2,5,7,8],[0,2,5,3,6,8,7],[5,1,0,8,7,3,6],[6,0,1,7,8,2,5],[],[2,8,7,1,0,6,3],[3,7,8,0,1,5,2],[8,6,3,5,2,0,1],[7,5,2,6,3,1,0]];const D_MARCH=1;const D_FOOD=2;const D_STALLED=3;const D_GATHERER=4;const U_REALIGN=5;const U_SENTINEL=6;const U_READY=7;const U_PANIC=8;const PUPS=[[0,1,2,3,4,5,6,7,8],[1,1,0,0,0,1,1,0,1],[2,0,2,0,4,2,2,0,2],[3,0,0,3,4,3,3,0,3],[4,0,4,4,4,4,0,0,4],[5,1,2,3,4,5,5,0,5],[6,1,2,3,0,5,5,0,6],[7,0,0,0,0,0,0,7,7],[8,1,2,3,4,5,6,7,8]];const PDOWNS=[[0,1,2,3,4,5,6,7,8],[1,1,0,3,4,5,5,0,1],[2,0,2,3,4,5,5,0,2],[3,3,3,3,3,3,3,3,3],[4,4,4,3,4,0,0,0,4],[5,5,5,3,0,5,5,0,5],[6,5,5,3,0,5,5,0,6],[7,0,0,3,0,0,0,7,7],[8,1,2,3,4,5,6,7,8]];const PSIDES=[[0,1,2,3,4,5,6,7,8],[1,1,0,3,4,1,1,0,1],[2,0,2,0,4,5,5,0,2],[3,3,0,3,3,3,3,3,3],[4,4,4,3,4,0,0,0,4],[5,1,5,3,0,5,5,0,5],[6,1,5,3,0,5,5,0,6],[7,0,0,3,0,0,0,7,7],[8,1,2,3,4,5,6,7,8]];const INIT_SEED=3734978372;const FINAL_SEED=2338395782;const SRECOLOR_PROB=0.7;const SONSTRIDE_PROB=0.5;const QFSPAWNP_MAX=0.05;const QFSPAWNP_MIN=0.00;const QFSPAWNP_DECAY=0.005;const QBSPAWNP_MAX=0.65;const QBSPAWNP_MIN=0.55;const QBSPAWNP_DECAY=0.01;const QFORMP_MAX=0.5;const QFORMP_MIN=0.3;const QFORMP_DECAY=0.01;const DISCOLORT=35;const ERASET=20;const SOBSTRUCT_FUZZ=6;const SSTRIDE_FUZZ=6;const OBSTRUCT_QWT=3;const SPREFWT=2;var state=null;function rand_init()
{state=INIT_SEED;for(var cell=0;cell<9;cell++)
{var v=view[cell];state^=v.color;state^=v.food<<3;if(v.ant!==null)
{state^=v.ant.friend<<4;state^=v.ant.type<<5;state^=v.ant.food<<8;}
ant_rand();}
state^=FINAL_SEED;if(state===0)state=1;}
function ant_rand()
{if(state===null)rand_init();state^=state<<13;state^=state>>>17;state^=state<<5;return state>>>0;}
function rand_choice(prob)
{return ant_rand()/4294967296<prob;}
function rand_sub(array,num)
{var return_array=array.slice();for(var i=0;i<num;i++)
{var rand_index=i+ant_rand()%(array.length-i);var x_val=return_array[rand_index];return_array[rand_index]=return_array[i];return_array[i]=x_val;}
return return_array.slice(0,num);}
function rand_perm(array)
{var return_array=array.slice();for(var i=0;i<array.length-1;i++)
{var rand_index=i+ant_rand()%(array.length-i)
var x_val=return_array[rand_index];return_array[rand_index]=return_array[i];return_array[i]=x_val;}
return return_array;}
function index_sort(arr)
{var index_array=[];for(var i=0;i<arr.length;i++)index_array.push(i);index_array.sort((a,b)=>(arr[a]===arr[b])?(a-b):(arr[a]-arr[b]));return index_array;}
function this_ant()
{return view[4].ant;}
function c_at(cell)
{return view[cell].color;}
function is_ally(cell)
{return view[cell].ant!==null&&view[cell].ant.friend===true;}
function is_enemy(cell)
{return view[cell].ant!==null&&view[cell].ant.friend===false;}
function is_harvestable(cell)
{return is_enemy(cell)&&view[cell].ant.type===QUEEN&&view[cell].ant.food>0;}
function lchk(c)
{if(is_ally(CCW[c][6])&&view[CCW[c][6]].ant.type===GATHERER)
if(is_ally(CCW[c][5])&&view[CCW[c][5]].ant.type!==GATHERER)return D_GATHERER;if(is_ally(CCW[c][7])&&view[CCW[c][7]].ant.type===GATHERER&&is_ally(CCW[c][1]))return D_GATHERER;if(is_ally(CCW[c][5])&&view[CCW[c][5]].ant.type===GATHERER)
if(is_ally(CCW[c][3])&&c_at(4)===D_MARCH)return D_STALLED;if(view[CCW[c][6]].food===1&&is_ally(CCW[c][5])&&view[CCW[c][5]].ant.type!==GATHERER)return D_FOOD;if(view[CCW[c][7]].food===1&&is_ally(CCW[c][1])&&c_at(CCW[c][1])===D_FOOD)return D_FOOD;if(view[CCW[c][5]].food===1&&is_ally(CCW[c][3])&&view[CCW[c][3]].ant.type!==QUEEN&&c_at(4)===D_MARCH)return U_REALIGN;return null;}
function lchk2(c)
{if(is_ally(CCW[c][6])&&view[CCW[c][6]].ant.type===GATHERER)
if(is_ally(CCW[c][5])&&view[CCW[c][5]].ant.type!==GATHERER)return D_GATHERER;if(is_ally(CCW[c][7])&&view[CCW[c][7]].ant.type===GATHERER&&is_ally(CCW[c][1]))return D_GATHERER;if(is_ally(CCW[c][5])&&view[CCW[c][5]].ant.type===GATHERER)
if(is_ally(CCW[c][3])&&c_at(4)===D_MARCH)return D_STALLED;if(is_ally(CCW[c][2])&&view[CCW[c][2]].ant.type===GATHERER)
if(is_ally(CCW[c][1])&&view[CCW[c][1]].ant.type!==GATHERER)return D_GATHERER;if(is_ally(CCW[c][3])&&view[CCW[c][3]].ant.type===GATHERER)
if(is_ally(CCW[c][5])&&c_at(CCW[c][5])===D_GATHERER)return D_GATHERER;if(is_ally(CCW[c][1])&&view[CCW[c][1]].ant.type===GATHERER)
if(is_ally(CCW[c][7])&&c_at(4)===D_MARCH)return D_STALLED;if(view[CCW[c][6]].food===1&&is_ally(CCW[c][5])&&view[CCW[c][5]].ant.type!==GATHERER)return D_FOOD;if(view[CCW[c][7]].food===1&&is_ally(CCW[c][1])&&c_at(CCW[c][1])===D_FOOD)return D_FOOD;if(view[CCW[c][5]].food===1&&is_ally(CCW[c][3])&&view[CCW[c][3]].ant.type!==QUEEN&&c_at(4)===D_MARCH)return U_REALIGN;if(view[CCW[c][2]].food===1&&is_ally(CCW[c][1])&&view[CCW[c][1]].ant.type!==GATHERER)return D_FOOD;if(view[CCW[c][3]].food===1&&is_ally(CCW[c][5])&&c_at(CCW[c][5])===D_FOOD)return{cell:4,color:D_FOOD};if(view[CCW[c][1]].food===1&&is_ally(CCW[c][7])&&view[CCW[c][7]].ant.type!==QUEEN&&c_at(4)===D_MARCH)return U_REALIGN;return null;}
function sigc(output,order,c)
{if(c_at(4)===output)
for(cell_off of order)
{var tcell=CCW[c][cell_off];if(!is_ally(tcell)&&c_at(tcell)!==D_MARCH)
{if(view[tcell].food!==0&&view[tcell].color===D_FOOD)
{for(alt_cell of SCAN_MOVES)
{var n_wt=NEARS[tcell][alt_cell];if(n_wt>3&&n_wt<6&&is_ally(alt_cell))
if(view[alt_cell].ant.type===QUEEN||view[alt_cell].ant.type===GATHERER)
continue;}}
return{cell:tcell,color:D_MARCH};}}
return{cell:4,color:output};}
function is_gatherer_marcher(cell)
{if(!is_ally(cell)||view[cell].ant.food>0||view[cell].ant.type!==GATHERER)return false;if(this_ant().type===QUEEN)return true;lh_cell=CCW[cell][1];rh_cell=CCW[cell][7];if(is_ally(lh_cell)&&view[lh_cell].ant.type===QUEEN)return!is_ally(rh_cell)
else if(is_ally(rh_cell)&&view[rh_cell].ant.type===QUEEN)return!is_ally(lh_cell)
else return false;}
function is_like(cell)
{if(c_at(cell)===U_PANIC)return false;if(is_ally(CCW[cell][1])&&c_at(CCW[cell][1])===U_PANIC)return false;if(is_ally(CCW[cell][7])&&c_at(CCW[cell][7])===U_PANIC)return false;if(CORNERS.includes(cell)&&is_ally(cell))
{switch(view[cell].ant.type)
{case MARCHER_A:return view[cell].ant.food===0&&this_ant().type!==MARCHER_B;case MARCHER_B:return view[cell].ant.food===0&&this_ant().type!==MARCHER_A;case GATHERER:return is_gatherer_marcher(cell)&&this_ant().type!==GATHERER;case QUEEN:return true;default:return false;}}
return false;}
function is_other(cell)
{if(c_at(cell)===U_PANIC)return false;if(EDGES.includes(cell)&&is_ally(cell))
{switch(view[cell].ant.type)
{case MARCHER_A:return view[cell].ant.food===0&&this_ant().type!==MARCHER_A;case MARCHER_B:return view[cell].ant.food===0&&this_ant().type!==MARCHER_B;case GATHERER:return this_ant().type===QUEEN
case QUEEN:return true;default:return false;}}
return false;}
function view_corner()
{var scores=[0,0,0,0];for(var i=0;i<4;i++)
for(var j=0;j<8;j++)
{scores[i]*=2;var tcell=CCW[CORNERS[i]][j];if(is_ally(tcell)&&(is_like(tcell)||is_other(tcell)))scores[i]++;}
if(scores[0]>scores[1]&&scores[0]>scores[2]&&scores[0]>scores[3])return CORNERS[0];else if(scores[1]>scores[2]&&scores[1]>scores[3])return CORNERS[1];else if(scores[2]>scores[3])return CORNERS[2];else return CORNERS[3];}
const ONE_EDGE=10;const ONE_CORNER=11;const EE_BENT=20;const EE_STRAIGHT=21;const EC_LEFT=22;const EC_RIGHT=23;const EC_SKEWED=24;const EC_SPAWN=25;const CC_EDGED=26;const CC_LINE=27;const THREE_MARCH=30;const THREE_STAND=31;const THREE_RECOVER=32;const THREE_UNSTAND=33;const THREE_BLOCK=34;const THREE_HANG=35;const THREE_UNHANG=36;const THREE_SIDE=37;const FOUR_Z=40;const FOUR_STAIRS=41;const FOUR_BENT=42;function neighbor_type(top_left)
{var corners=[];for(tcell of CORNERS)
if(is_ally(tcell)&&is_like(tcell))corners.push(tcell);var edges=[];for(tcell of EDGES)
if(is_ally(tcell)&&is_other(tcell))edges.push(tcell);if(corners.length===1&&edges.length===0)return ONE_CORNER;if(corners.length===0&&edges.length===1)return ONE_EDGE;if(corners.length===0&&edges.length===2)return(edges[1]===CCW[edges[0]][4])?EE_STRAIGHT:EE_BENT;if(corners.length===2&&edges.length===0)return(corners[1]===CCW[corners[0]][4])?CC_LINE:CC_EDGED;else if(corners.length===1&&edges.length===1)
{if(edges[0]===CCW[top_left][1])return EC_LEFT;if(edges[0]===CCW[top_left][3])return EC_SPAWN;if(edges[0]===CCW[top_left][5])return EC_SKEWED;if(edges[0]===CCW[top_left][7])return EC_RIGHT;return null;}
else if(corners.length===1&&edges.length===2)
{if(edges.includes(CCW[top_left][1])&&edges.includes(CCW[top_left][3]))return THREE_MARCH;if(edges.includes(CCW[top_left][3])&&edges.includes(CCW[top_left][7]))return THREE_STAND;if(edges.includes(CCW[top_left][1])&&edges.includes(CCW[top_left][5]))return THREE_RECOVER;if(edges.includes(CCW[top_left][5])&&edges.includes(CCW[top_left][7]))return THREE_UNSTAND;if(edges.includes(CCW[top_left][1])&&edges.includes(CCW[top_left][7]))return THREE_BLOCK;return null;}
else if(corners.length===2&&edges.length===1)
{if(corners.includes(CCW[top_left][4])&&edges.includes(CCW[top_left][3]))return THREE_HANG;if(corners.includes(CCW[top_left][4])&&edges.includes(CCW[top_left][1]))return THREE_UNHANG;if(corners.includes(CCW[top_left][2])&&edges.includes(CCW[top_left][1]))return THREE_SIDE;}
else if(corners.length===2&&edges.length===2)
{if(edges.includes(CCW[top_left][3])&&edges.includes(CCW[top_left][7])&&corners.includes(CCW[top_left][4]))
return FOUR_Z;if(edges.includes(CCW[top_left][1])&&edges.includes(CCW[top_left][3])&&corners.includes(CCW[top_left][4]))
return FOUR_STAIRS;if(edges.includes(CCW[top_left][1])&&edges.includes(CCW[top_left][3])&&corners.includes(CCW[top_left][2]))
return FOUR_BENT;return null;}
return null;}
function sok(cand)
{if(cand===4)return true;if(view[cand].food!==0&&this_ant().food!==0)return false;if(view[cand].ant!==null)return false;return true;}
function spref(cand)
{var okscore=0;if(cand===4)okscore-=9;if(this_ant().type===GATHERER)
{for(tcell of SCAN_MOVES)
if(NEARS[cand][tcell]>1)
if(is_ally(tcell)&&view[tcell].ant.type===QUEEN)okscore-=1;}
else
{if(this_ant().food===0&&view[cand].food!==0)
{for(tcell of SCAN_MOVES)
if(is_ally(tcell)&&view[tcell].ant.food===0)
{if([MARCHER_A,MARCHER_B].includes(view[tcell].ant.type))
{var has_common_enemy=false;for(var i=0;i<9;i++)
if(is_enemy(i)&&NEARS[tcell][i]>=4)has_common_enemy=true;if(!has_common_enemy)
{var wt=(view[tcell].ant.type===this_ant().type)?1:-1;if(NEARS[4][tcell]===5)okscore+=wt;if(NEARS[4][tcell]===4)okscore-=wt;if(NEARS[cand][tcell]===5)okscore-=wt;if(NEARS[cand][tcell]===4)okscore+=wt;}}}
if(okscore>0)okscore=0;}}
return okscore*SPREFWT;}
function ssep()
{var has_ally=false;var cands=[0,0,0,0,0,0,0,0,0];for(var i=0;i<9;i++)cands[i]+=spref(i);for(tcell of SCAN_MOVES)
{if(is_ally(tcell))
{has_ally=true;var wt=(is_like(tcell)||is_other(tcell))?3:1;for(var i=0;i<9;i++)cands[i]-=NEARS[tcell][i]*wt;}}
if(!has_ally)return null;var prox_order=index_sort(cands);for(var i=8;i>=0;i--)
{var i_cell=prox_order[i];if(sok(i_cell))return{cell:i_cell};}
return null;}
function sstep(col)
{if(c_at(4)===1)return{cell:4,color:col};var cands=[0,0,0,0,0,0,0,0,0];for(tcell of SCAN_MOVES)
if(c_at(tcell)===col)
for(var i=0;i<9;i++)cands[i]-=NEARS[tcell][i];for(var i=0;i<9;i++)cands[i]+=spref(i);var prox_order=index_sort(cands);for(var i=8;i>=0;i--)
{var i_cell=prox_order[i];if(sok(i_cell))return{cell:i_cell};}
return{cell:4,color:col};}
function smove()
{for(tcell of rand_perm(SCAN_MOVES))
if(sok(tcell))return{cell:tcell};return{cell:4};}
function sdec_alone()
{var try_sep=ssep();if(try_sep!==null)return try_sep;var c=U_PANIC;for(tcell of rand_sub(SCAN_MOVES,7))
if(c_at(tcell)>1&&c_at(tcell)!==c)
{c=c_at(tcell);break;}
return sstep(c);}
function sdec_erase()
{var try_sep=ssep();if(try_sep!==null)return try_sep;for(tcell of rand_perm(SCAN_MOVES))
if(c_at(tcell)!==1)return{cell:tcell,color:1};if(c_at(4)!==1)return{cell:4,color:1};return sdec_alone();}
function sdec_discolor()
{if(c_at(1)!==c_at(6)&&c_at(6)!==1)return{cell:1,color:c_at(6)};if(c_at(2)!==c_at(3))return{cell:3,color:c_at(2)};var proximities=[0,0,0,0,0,0,0,0,0];for(var i=0;i<9;i++)proximities[i]+=ant_rand()%SOBSTRUCT_FUZZ+spref(i);for(tcell of SCAN_MOVES)
if(is_ally(tcell))
for(var i=0;i<9;i++)proximities[i]+=NEARS[tcell][i];var prox_order=index_sort(proximities);for(var i=8;i>=0;i--)
if(sok(prox_order[i]))return{cell:prox_order[i]};return smove();}
function sdec_stride()
{if(rand_choice(SONSTRIDE_PROB))
{var stride_scores=[0,0,0,0,0,0,0,0,0];for(tcell of SCAN_MOVES)
{for(var i=0;i<9;i++)
if(c_at(tcell)!==c_at(i)&&c_at(i)!==1)stride_scores[i]+=NEARS[tcell][i];}
for(var i=0;i<9;i++)
stride_scores[i]+=ant_rand()%SSTRIDE_FUZZ+spref(i);var prox_order=index_sort(stride_scores);for(var i=8;i>=0;i--)
if(sok(prox_order[i]))return{cell:prox_order[i]};}
return smove();}
function sdec_obstruct_textured()
{var proximities=[0,0,0,0,0,0,0,0,0];for(tcell of SCAN_MOVES)
{if(is_enemy(tcell))
{var wt=(view[tcell].ant.type===QUEEN)?OBSTRUCT_QWT:1;for(var i=0;i<9;i++)proximities[i]+=NEARS[tcell][i]*wt;}}
for(var i=0;i<9;i++)proximities[i]+=ant_rand()%SOBSTRUCT_FUZZ;var prox_order;if(rand_choice(SRECOLOR_PROB))
{prox_order=index_sort(proximities);for(var i=8;i>0;i--)
{var i_cell=prox_order[i];for(var j=0;j<i;j++)
{var j_cell=prox_order[j];if(c_at(i_cell)!==c_at(j_cell))return{cell:i_cell,color:c_at(j_cell)};}}}
for(tcell of SCAN_MOVES)
if(is_ally(tcell))
for(var i=0;i<9;i++)proximities[i]+=NEARS[tcell][i];for(var i=0;i<9;i++)proximities[i]+=spref(i);prox_order=index_sort(proximities);for(var i=8;i>=0;i--)
if(sok(prox_order[i]))return{cell:prox_order[i]};return{cell:4,color:1};}
function sdec_obstruct_flat()
{var proximities=[0,0,0,0,0,0,0,0,0];for(tcell of SCAN_MOVES)
{if(is_enemy(tcell))
{var wt=(view[tcell].ant.type===QUEEN)?OBSTRUCT_QWT:1;for(var i=0;i<9;i++)proximities[i]+=NEARS[tcell][i]*wt;}}
for(var i=0;i<9;i++)proximities[i]+=ant_rand()%SOBSTRUCT_FUZZ;var prox_order;if(rand_choice(SRECOLOR_PROB))
{prox_order=index_sort(proximities);for(var i=8;i>0;i--)
{var i_cell=prox_order[i];if(c_at(i_cell)!==D_MARCH)return{cell:i_cell,color:D_MARCH};}}
for(tcell of SCAN_MOVES)
if(is_ally(tcell))
for(var i=0;i<9;i++)proximities[i]+=NEARS[tcell][i];for(var i=0;i<9;i++)proximities[i]+=spref(i);prox_order=index_sort(proximities);for(var i=8;i>=0;i--)
if(sok(prox_order[i]))return{cell:prox_order[i]};return{cell:4,color:1};}
function saboteur()
{var colored_neighbors=0;for(tcell of SCAN_MOVES)
if(c_at(tcell)>1)colored_neighbors++;if(colored_neighbors<=2)return sdec_alone();else
{var num_enemies=0;for(tcell of SCAN_MOVES)
if(is_enemy(tcell))num_enemies++;var diversity=0;var counts=[0,0,0,0,0,0,0,0,0];for(var i=0;i<9;i++)
{diversity+=5-counts[c_at(i)];counts[c_at(i)]++;}
if(num_enemies>0)
{if(diversity>=ERASET)return sdec_obstruct_textured();else return sdec_obstruct_flat();}
else
{if(diversity>=DISCOLORT)return sdec_discolor();else if(diversity>=ERASET)return sdec_stride();else return sdec_erase();}}}
function gwatch(cand)
{if(cand.cell===4)return cand;if(cand.hasOwnProperty("color"))return cand;if(view[cand.cell].food!==0&&this_ant().food!==0)return sigc(U_PANIC,S_SIDE,0);if(view[cand.cell].ant!==null)return sigc(U_PANIC,S_SIDE,0);return cand;}
function egwatch(cand)
{if(cand.cell===4)return cand;if(cand.hasOwnProperty("color"))return cand;if(view[cand.cell].food!==0&&this_ant().food!==0)return gwatch(sdec_erase());if(view[cand.cell].ant!==null)return gwatch(sdec_erase());return cand;}
function gdec_ee_bent(c)
{return{cell:CCW[c][4]};}
function gdec_ec_left(c)
{if(c_at(c)===D_FOOD&&c_at(CCW[c][1])===D_FOOD)return{cell:CCW[c][7]};if(c_at(c)===D_STALLED&&c_at(CCW[c][1])===D_STALLED)return sigc(U_READY,S_GATHERER,c);if(c_at(c)===D_MARCH&&c_at(CCW[c][1])===D_MARCH)return sigc(D_MARCH,S_GATHERER,c);return sigc(c_at(4),S_GATHERER,c);}
function gdec_ec_right(c)
{if([D_MARCH,D_FOOD].includes(c_at(c))&&[D_MARCH,D_FOOD].includes(c_at(CCW[c][7])))
return{cell:CCW[c][6]};if(is_ally(c)&&view[c].ant.type===QUEEN)
return{cell:CCW[c][1]};if(c_at(c)===D_STALLED&&c_at(CCW[c][7])===D_STALLED)
return sigc(U_READY,S_GATHERER,c);return sigc(c_at(4),S_GATHERER,c);}
function gdec_cc_edged(c)
{if(view[CCW[c][2]].ant.type!==QUEEN)return saboteur();return{cell:CCW[c][1]};}
function gdec_three_block(c)
{if(c_at(CCW[c][7])==D_FOOD)return{cell:CCW[c][6]};return{cell:CCW[c][2]};}
function gdec_three_unstand(c)
{if(view[CCW[c][5]].ant.type!==QUEEN)return saboteur();return{cell:CCW[c][4]};}
function gdec_four_bent(c)
{return{cell:CCW[c][4]};}
function early_gatherer()
{var qcell=null;var food_count=0;for(tcell of SCAN_MOVES)
{if(is_ally(tcell)&&view[tcell].ant.type===QUEEN)qcell=tcell;else if(is_enemy(tcell))return saboteur();}
if(qcell===null)return saboteur();if(c_at(qcell)===D_FOOD)return{cell:CCW[qcell][7]};if(this_ant().food===0)
{for(tcell of rand_perm(CORNERS))
if(view[tcell].food>0&&NEARS[tcell][qcell]===5)
{if(c_at(tcell)===D_FOOD)return{cell:tcell};else return{cell:tcell,color:D_FOOD};}
for(tcell of rand_perm(EDGES))
if(view[tcell].food>0)
{if(c_at(tcell)!==D_FOOD&&NEARS[tcell][qcell]===4)
return{cell:tcell,color:D_FOOD};}}
return{cell:CCW[qcell][1]};}
function gatherer_retrieve()
{if(c_at(4)===U_PANIC)return saboteur();var c=view_corner();switch(neighbor_type(c))
{case EC_LEFT:return gwatch({cell:CCW[c][2]});case THREE_BLOCK:{if(c_at(CCW[c][7])===D_FOOD)return gwatch({cell:CCW[c][6]});return gwatch({cell:CCW[c][2]});}
case FOUR_BENT:return gwatch(sigc(c_at(4),S_FRONT,c));default:return early_gatherer();}}
function gatherer_return()
{if(c_at(4)===U_PANIC)return saboteur();var c=view_corner();switch(neighbor_type(c))
{case EC_LEFT:return gwatch({cell:CCW[c][2]});case THREE_BLOCK:return gwatch({cell:CCW[c][2]});case FOUR_BENT:return gwatch({cell:CCW[c][4]});default:return early_gatherer();}}
function gatherer_formation()
{if(c_at(4)===U_PANIC)return saboteur();var c=view_corner();switch(neighbor_type(c))
{case EC_LEFT:return gwatch(gdec_ec_left(c));case EC_RIGHT:return gwatch(gdec_ec_right(c));case CC_EDGED:return gwatch(gdec_cc_edged(c));case EE_BENT:return gwatch(gdec_ee_bent(c));case THREE_BLOCK:return gwatch(gdec_three_block(c));case THREE_UNSTAND:return gwatch(gdec_three_unstand(c));case FOUR_BENT:return gwatch(gdec_four_bent(c));default:return egwatch(early_gatherer());}}
function gatherer_decision()
{var marcher_count=0;var gatherer_count=0;var queen_pos=null;for(tcell of SCAN_MOVES)
if(is_ally(tcell))
{if(view[tcell].ant.type===MARCHER_A||view[tcell].ant.type===MARCHER_B)marcher_count++;if(view[tcell].ant.type===GATHERER)gatherer_count++;if(view[tcell].ant.type===QUEEN)queen_pos=tcell;}
if(gatherer_count>0)return saboteur();if(this_ant().food>0&&marcher_count>0)return gwatch(gatherer_return());else if(queen_pos!==null&&marcher_count>0)return gwatch(gatherer_formation());else if(marcher_count>0)return gwatch(gatherer_retrieve());else if(queen_pos!==null)return egwatch(early_gatherer());else return saboteur();}
function mdec_one_corner(c)
{if(view[c].ant.type===QUEEN)
return sigc(c_at(4),S_SIDE,c);else return saboteur();}
function mdec_one_edge(c)
{if([U_REALIGN,D_MARCH].includes(c_at(CCW[c][1])))
{if(view[CCW[c][2]].food===1)return{cell:c};if(is_ally(CCW[c][2])&&view[CCW[c][2]].ant.type===GATHERER)return{cell:c};}
return saboteur();}
function mdec_ee_bent(c)
{if(view[CCW[c][1]].ant.type===GATHERER&&view[CCW[c][3]].ant.type===QUEEN)return saboteur();if(view[CCW[c][1]].ant.type===QUEEN&&view[CCW[c][3]].ant.type===GATHERER)return saboteur();var u_sig=c_at(CCW[c][1]);var d_sig=c_at(CCW[c][3]);if(is_ally(c)&&view[c].ant.type===GATHERER)return sigc(c_at(4),S_SIDE,CCW[c][4]);var provisional=lchk(c);if(provisional!==null)
{if(provisional===U_REALIGN)return sigc(U_SENTINEL,S_END,c);return sigc(provisional,S_END,c);}
if(u_sig===D_STALLED)
{if([D_STALLED,U_READY,D_GATHERER].includes(d_sig)&&[D_STALLED,U_READY].includes(c_at(4)))
return sigc(D_STALLED,S_SIDE,c);if(d_sig===U_REALIGN&&c_at(4)===D_STALLED)
return sigc(D_STALLED,S_SIDE,c);}
if(view[CCW[c][1]].ant.type===QUEEN)
{var provisional=lchk(CCW[c][4]);if(provisional!==null)return sigc(provisional,S_END,CCW[c][4]);if(u_sig===D_GATHERER&&d_sig===U_REALIGN&&c_at(4)===D_GATHERER)
return sigc(D_GATHERER,S_END,CCW[c][4]);}
if(u_sig===U_SENTINEL)
{if(d_sig===U_REALIGN&&[D_MARCH,U_SENTINEL].includes(c_at(4)))return sigc(U_SENTINEL,S_SIDE,c);if(d_sig===D_STALLED&&[U_SENTINEL,D_STALLED].includes(c_at(4)))return sigc(U_SENTINEL,S_SIDE,c);if(d_sig===D_MARCH&&[U_SENTINEL,D_MARCH].includes(c_at(4)))return sigc(D_MARCH,S_SIDE,c);}
if(u_sig===D_GATHERER&&d_sig===D_STALLED&&c_at(4)===D_GATHERER)return sigc(D_STALLED,S_SIDE,c);return{cell:CCW[c][2]};}
function mdec_ee_straight(c)
{return sigc(U_REALIGN,S_SIDE,c);}
function mdec_ec_left(c)
{if(view[CCW[c][1]].ant.type===GATHERER&&view[c].ant.type===QUEEN)return saboteur();if(view[CCW[c][1]].ant.type===QUEEN&&view[c].ant.type===GATHERER)return saboteur();if(is_other(CCW[c][1])&&view[c].ant.type===QUEEN)return{cell:CCW[c][3]};var d_sig=PDOWNS[c_at(c)][c_at(CCW[c][1])];if(is_ally(CCW[c][4])&&view[CCW[c][4]].ant.type===GATHERER&&d_sig===D_STALLED&&c_at(4)===D_STALLED)
return sigc(D_STALLED,S_END,c);var provisional=lchk(CCW[c][4]);if(provisional!==null)
{if(provisional===U_REALIGN)return sigc(U_SENTINEL,S_END,CCW[c][4]);return sigc(provisional,S_END,CCW[c][4]);}
if(d_sig===U_REALIGN)
{if(c_at(4)===D_MARCH)return sigc(U_SENTINEL,S_END,CCW[c][4]);if(c_at(4)===U_SENTINEL)
{if(c_at(c)===D_MARCH)return{cell:CCW[c][2]};return sigc(U_SENTINEL,S_END,CCW[c][4]);}}
if(d_sig===D_STALLED)
{if([D_MARCH,D_STALLED].includes(c_at(4)))return sigc(D_STALLED,S_END,CCW[c][4]);if(c_at(4)===U_SENTINEL)return sigc(U_SENTINEL,S_END,CCW[c][4]);}
if(d_sig===D_GATHERER)
{if(c_at(4)===D_FOOD)return sigc(D_GATHERER,S_END,CCW[c][4]);if(c_at(4)===D_GATHERER)return sigc(D_STALLED,S_END,CCW[c][4]);}
if(d_sig===U_READY)
{if(c_at(4)===D_STALLED)
{if(c_at(CCW[c][2])!==D_MARCH)return{cell:CCW[c][2],color:D_MARCH};return sigc(D_MARCH,S_END,CCW[c][4]);}
if(c_at(4)===U_SENTINEL)return sigc(D_MARCH,S_END,CCW[c][4]);}
return{cell:CCW[c][2]};}
function mdec_ec_right(c)
{if(view[c].ant.type===GATHERER&&view[CCW[c][7]].ant.type===QUEEN)
if(is_ally(CCW[c][4])&&view[CCW[c][4]].ant.type!==this_ant().type)return{cell:CCW[c][5]};var d_sig=PDOWNS[c_at(c)][c_at(CCW[c][7])];var provisional=lchk(CCW[c][4]);if(provisional!==null)
{if(provisional===U_REALIGN)return sigc(U_SENTINEL,S_END,CCW[c][4]);return sigc(provisional,S_END,CCW[c][4]);}
if(d_sig===D_MARCH)
{if(c_at(4)===D_MARCH)return sigc(D_MARCH,S_END,CCW[c][4]);if([D_FOOD,D_GATHERER,U_READY].includes(c_at(4)))return sigc(D_MARCH,S_END,CCW[c][4]);}
if(d_sig===D_FOOD)
{if([U_SENTINEL,D_STALLED].includes(c_at(4)))return sigc(D_STALLED,S_END,CCW[c][4]);if([D_FOOD,D_GATHERER,U_READY].includes(c_at(4)))return sigc(D_STALLED,S_END,CCW[c][4]);}
if(d_sig===D_GATHERER)
{if([D_FOOD,D_GATHERER,U_READY].includes(c_at(4)))return sigc(D_MARCH,S_END,CCW[c][4]);if([U_SENTINEL,D_STALLED].includes(c_at(4)))return sigc(D_STALLED,S_END,CCW[c][4]);}
if(d_sig===D_STALLED)
{if(c_at(4)===D_STALLED)return sigc(D_STALLED,S_END,CCW[c][4]);if([D_FOOD,D_GATHERER,U_READY].includes(c_at(4)))return sigc(D_STALLED,S_END,CCW[c][4]);}
if(d_sig===U_READY)
{if(c_at(4)===D_STALLED)return sigc(D_MARCH,S_END,CCW[c][4]);if([D_FOOD,D_GATHERER,U_READY].includes(c_at(4)))return sigc(D_MARCH,S_END,CCW[c][4]);}
if(d_sig===U_REALIGN)
{if(c_at(4)===U_SENTINEL)return{cell:CCW[c][6]};if([D_FOOD,D_GATHERER,U_READY].includes(c_at(4)))return sigc(D_STALLED,S_END,CCW[c][4]);}
return sigc(d_sig,S_END,CCW[c][4]);}
function mdec_ec_spawn(c)
{if(view[c].ant.type===QUEEN&&c_at(c)===D_MARCH&&c_at(CCW[c][3])===D_STALLED)
if(c_at(4)===D_STALLED)return sigc(D_STALLED,S_SIDE,c);return saboteur();}
function mdec_three_march(c)
{var d_sig=PDOWNS[c_at(c)][c_at(CCW[c][1])];var u_sig=c_at(CCW[c][3]);var provisional=lchk2(c);if(provisional!==null)return sigc(provisional,S_FRONT,c);if(u_sig===U_SENTINEL)
{if(d_sig===D_GATHERER&&[D_GATHERER,D_STALLED].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,c);if(d_sig===D_STALLED&&[D_MARCH,D_STALLED].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,c);}
if(u_sig===U_REALIGN)
{if(d_sig===U_REALIGN&&c_at(4)===U_REALIGN)
if(c_at(c)===U_SENTINEL)
{if(c_at(CCW[c][7])===D_MARCH)return sigc(U_REALIGN,S_FRONT,c);return{cell:CCW[c][2]};}
if(d_sig===D_FOOD&&[D_MARCH,D_FOOD].includes(c_at(4)))return sigc(D_FOOD,S_FRONT,c);if(d_sig===U_READY&&c_at(4)===D_STALLED)return sigc(D_MARCH,S_FRONT,c);if(d_sig===D_STALLED&&[D_MARCH,D_STALLED].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,c);if(d_sig===D_GATHERER&&[D_GATHERER,D_STALLED].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,c);if(d_sig===D_MARCH&&c_at(4)===D_STALLED)return sigc(D_STALLED,S_FRONT,c);}
if(u_sig===D_MARCH)
{if(d_sig===D_FOOD&&c_at(4)===D_FOOD)return sigc(D_FOOD,S_FRONT,c);if(d_sig===U_REALIGN&&c_at(4)===D_MARCH)
if(c_at(c)===U_SENTINEL)return sigc(U_REALIGN,S_FRONT,c);if(d_sig===U_READY&&c_at(4)===U_READY)return sigc(D_MARCH,S_FRONT,c);}
if(u_sig===D_STALLED)
{if(d_sig===U_READY&&c_at(4)===D_STALLED)return sigc(U_READY,S_FRONT,c);if(d_sig===D_STALLED&&[D_STALLED,D_MARCH].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,c);if(d_sig===D_GATHERER&&c_at(4)===D_GATHERER)return sigc(D_STALLED,S_FRONT,c);if(d_sig===D_MARCH&&c_at(4)===D_STALLED)return sigc(D_STALLED,S_FRONT,c);if(d_sig===U_REALIGN&&[D_STALLED,D_MARCH].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,c);}
if(u_sig===D_GATHERER)
{if(d_sig===D_STALLED&&c_at(4)===D_GATHERER)
if(view[CCW[c][3]].ant.type===QUEEN)return sigc(D_STALLED,S_FRONT,c);if(d_sig===D_GATHERER&&c_at(4)===D_GATHERER)return sigc(D_GATHERER,S_FRONT,c);if(d_sig===D_FOOD&&c_at(4)===D_GATHERER)return sigc(D_FOOD,S_FRONT,c);}
if(u_sig===D_FOOD)
{if(d_sig===D_FOOD&&c_at(4)===D_FOOD)return sigc(D_FOOD,S_FRONT,c);if(d_sig===D_GATHERER&&c_at(4)===D_GATHERER)return sigc(D_GATHERER,S_FRONT,c);}
return{cell:CCW[c][2]};}
function mdec_three_stand(c)
{var provisional=lchk2(c);if(provisional!==null)return sigc(provisional,S_SIDE,c);var u_sig=c_at(CCW[c][3]);var d_sig=PSIDES[c_at(c)][c_at(CCW[c][7])];if(u_sig===U_REALIGN)
{if([D_MARCH,D_STALLED].includes(d_sig)&&c_at(4)===D_MARCH)return sigc(U_REALIGN,S_SIDE,CCW[c][4]);if(c_at(4)===U_REALIGN)return sigc(U_REALIGN,S_SIDE,CCW[c][4]);}
if(u_sig===D_MARCH&&d_sig===U_REALIGN&&c_at(4)===D_MARCH)return sigc(U_REALIGN,S_SIDE,CCW[c][4]);if(u_sig===D_STALLED&&[D_STALLED,U_REALIGN].includes(d_sig)&&c_at(4)===D_STALLED)
return sigc(D_STALLED,S_SIDE,CCW[c][4]);return sigc(D_MARCH,S_SIDE,CCW[c][4]);}
function mdec_three_unstand(c)
{if(view[CCW[c][5]].ant.type===QUEEN)
{var provisional=lchk(c);if(provisional!==null)return sigc(provisional,S_FRONT,c);var d_sig=PUPS[c_at(c)][c_at(CCW[c][7])];return sigc(d_sig,S_FRONT,c);}
else
{var provisional=lchk(CCW[c][4]);if(provisional!==null)return sigc(provisional,S_FRONT,CCW[c][4]);var u_sig=c_at(CCW[c][5]);var d_sig=PDOWNS[c_at(c)][c_at(CCW[c][7])];if(u_sig===D_MARCH)
{if(d_sig===U_READY&&c_at(4)===U_READY)return sigc(D_MARCH,S_FRONT,CCW[c][4]);if(d_sig===D_FOOD&&c_at(4)===D_MARCH)return sigc(U_REALIGN,S_FRONT,CCW[c][4]);if([D_FOOD,D_GATHERER].includes(c_at(4)))return sigc(D_MARCH,S_FRONT,CCW[c][4]);}
if(u_sig===D_FOOD)
{if(d_sig===D_FOOD&&c_at(4)===D_MARCH)return sigc(U_REALIGN,S_FRONT,CCW[c][4]);if([D_FOOD,D_GATHERER].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,CCW[c][4]);}
if(u_sig===D_GATHERER)
{if(d_sig===D_FOOD&&c_at(4)===D_MARCH)return sigc(U_REALIGN,S_FRONT,CCW[c][4]);if([D_FOOD,D_GATHERER].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,CCW[c][4]);}
if(u_sig===D_STALLED)
{if(d_sig===U_READY)
{if(c_at(4)===D_STALLED)return sigc(U_READY,S_FRONT,CCW[c][4]);if(c_at(4)===D_GATHERER)return sigc(D_STALLED,S_FRONT,CCW[c][4]);}
if(d_sig===D_FOOD)
{if(c_at(4)===D_MARCH)return sigc(U_REALIGN,S_FRONT,CCW[c][4]);if(c_at(4)===D_GATHERER)return sigc(D_STALLED,S_FRONT,CCW[c][4]);}
if(d_sig===D_STALLED)
{if(c_at(4)===D_STALLED)return sigc(D_STALLED,S_FRONT,CCW[c][4]);if(c_at(4)===D_GATHERER)return sigc(D_STALLED,S_FRONT,CCW[c][4]);}
if(d_sig===D_GATHERER&&c_at(4)===D_GATHERER)return sigc(D_STALLED,S_FRONT,CCW[c][4]);if([D_MARCH,U_REALIGN].includes(d_sig)&&c_at(4)===D_GATHERER)
return sigc(D_STALLED,S_FRONT,CCW[c][4]);if(c_at(4)===D_FOOD)return sigc(D_STALLED,S_FRONT,CCW[c][4]);}
if(u_sig===U_REALIGN)
{if(d_sig===D_FOOD&&c_at(4)===D_MARCH)return sigc(U_REALIGN,S_FRONT,CCW[c][4]);if([D_FOOD,D_GATHERER].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,CCW[c][4]);}
if(u_sig===U_SENTINEL)
{if(d_sig===D_FOOD)
{if(c_at(4)===U_REALIGN)return sigc(D_STALLED,S_FRONT,CCW[c][4]);if(c_at(4)===D_MARCH)return sigc(U_REALIGN,S_FRONT,CCW[c][4]);}
if(d_sig===D_GATHERER&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_FRONT,CCW[c][4]);if(d_sig===D_MARCH&&c_at(4)===U_SENTINEL)return sigc(D_MARCH,S_FRONT,CCW[c][4]);if(d_sig===D_STALLED&&c_at(4)===U_SENTINEL)return sigc(D_STALLED,S_FRONT,CCW[c][4]);if(d_sig===U_READY&&c_at(4)===D_STALLED)return sigc(U_READY,S_FRONT,CCW[c][4]);if([D_FOOD,D_GATHERER].includes(c_at(4)))return sigc(D_STALLED,S_FRONT,CCW[c][4]);}
if(u_sig===U_READY)
{if(d_sig===D_FOOD&&c_at(4)===D_MARCH)return sigc(U_REALIGN,S_FRONT,CCW[c][4]);if([D_FOOD,D_GATHERER].includes(c_at(4)))return sigc(D_MARCH,S_FRONT,CCW[c][4]);}
return sigc(c_at(4),S_FRONT,CCW[c][4]);}}
function mdec_three_recover(c)
{return sigc(U_SENTINEL,S_FRONT,c);}
function mdec_three_hang(c)
{return sigc(c_at(4),S_SIDE,CCW[c][4]);}
function mdec_three_unhang(c)
{return sigc(c_at(4),S_SIDE,c);}
function mdec_four_z(c)
{var provisional=lchk2(CCW[c][4]);if(provisional!==null)return sigc(provisional,S_SIDE,CCW[c][4]);var u_sig=PSIDES[c_at(c)][c_at(CCW[c][7])];var d_sig=PSIDES[c_at(CCW[c][4])][c_at(CCW[c][3])];if(u_sig===D_FOOD)
{if([D_FOOD,D_STALLED,U_REALIGN].includes(d_sig)&&c_at(4)===U_REALIGN)
return sigc(U_REALIGN,S_SIDE,CCW[c][4]);if(d_sig===D_GATHERER&&[U_REALIGN,D_GATHERER].includes(c_at(4)))
return sigc(U_REALIGN,S_SIDE,CCW[c][4]);}
if(u_sig===D_STALLED)
{if(d_sig===U_REALIGN)return sigc(U_REALIGN,S_SIDE,CCW[c][4]);if(d_sig===D_FOOD&&c_at(4)===U_REALIGN)return sigc(U_REALIGN,S_SIDE,CCW[c][4]);}
if(u_sig===D_GATHERER)
{if(d_sig===U_REALIGN&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,CCW[c][4]);if(d_sig===D_FOOD&&[U_REALIGN,D_GATHERER].includes(c_at(4)))
return sigc(U_REALIGN,S_SIDE,CCW[c][4]);}
if(u_sig===U_REALIGN)
{if(d_sig===D_FOOD&&c_at(4)===U_REALIGN)return sigc(U_REALIGN,S_SIDE,CCW[c][4]);if(d_sig===D_STALLED)return sigc(U_REALIGN,S_SIDE,CCW[c][4]);if(d_sig===D_GATHERER&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,CCW[c][4]);if(d_sig===U_READY&&c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,CCW[c][4]);}
if(u_sig===U_READY&&d_sig===U_REALIGN&&c_at(4)===U_REALIGN)
return sigc(D_MARCH,S_SIDE,CCW[c][4]);return sigc(D_MARCH,S_SIDE,CCW[c][4]);}
function mdec_four_stairs(c)
{var provisional=lchk2(c);if(provisional!==null)return sigc(provisional,S_SIDE,c);var u_sig=PSIDES[c_at(c)][c_at(CCW[c][1])];var d_sig=PSIDES[c_at(CCW[c][4])][c_at(CCW[c][3])];if(u_sig===D_MARCH)
{if(d_sig===D_FOOD)
{if(c_at(4)===D_MARCH)return sigc(D_FOOD,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);}
if(d_sig===U_READY)
{if(c_at(4)===U_READY)return sigc(D_MARCH,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);}
if(d_sig===D_STALLED)
{if(c_at(4)===D_MARCH)return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(d_sig===D_GATHERER&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);if([D_MARCH,U_REALIGN].includes(d_sig)&&c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);}
if(u_sig===D_FOOD)
{if(d_sig===D_MARCH)
{if(c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);if(c_at(4)===D_MARCH)return sigc(D_FOOD,S_SIDE,c);}
if(d_sig===U_READY)
{if(c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);if(c_at(4)===D_STALLED)return sigc(D_STALLED,S_SIDE,c);}
if(d_sig===D_GATHERER&&[U_REALIGN,D_GATHERER].includes(c_at(4)))return sigc(D_FOOD,S_SIDE,c);if([U_REALIGN,D_STALLED].includes(d_sig)&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(u_sig===D_STALLED)
{if(d_sig===D_STALLED)
{if(c_at(4)===D_STALLED)return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===D_MARCH)return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);}
if(d_sig===D_MARCH)
{if(c_at(4)===D_MARCH)return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(d_sig===D_GATHERER)
{if(c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);if([D_STALLED,D_GATHERER].includes(c_at(4)))return sigc(D_STALLED,S_SIDE,c);}
if(d_sig===U_READY)
{if(c_at(4)===D_STALLED)return sigc(U_READY,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);}
if(d_sig===U_REALIGN&&[U_REALIGN,D_MARCH].includes(c_at(4)))return sigc(D_STALLED,S_SIDE,c);if(d_sig===D_FOOD&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(u_sig===D_GATHERER)
{if(d_sig===D_STALLED)
{if([D_STALLED,D_GATHERER].includes(c_at(4)))return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(d_sig===U_READY)
{if(c_at(4)===D_STALLED)return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(d_sig===D_FOOD&&[D_GATHERER,U_REALIGN].includes(c_at(4)))return sigc(D_FOOD,S_SIDE,c);if([D_MARCH,U_REALIGN].includes(d_sig)&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);if(d_sig===D_GATHERER&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(u_sig===U_REALIGN)
{if(d_sig===U_REALIGN)
{if(c_at(4)===D_MARCH)return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(d_sig===D_STALLED)
{if(c_at(4)===D_MARCH)return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(d_sig===D_MARCH&&c_at(4)===U_REALIGN)return sigc(D_MARCH,S_SIDE,c);if([D_FOOD,D_GATHERER,U_READY].includes(d_sig)&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);}
if(u_sig===U_READY)
{if(d_sig===D_MARCH)
{if(c_at(4)===U_READY)return sigc(D_MARCH,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(U_READY,S_SIDE,c);}
if([D_FOOD,D_GATHERER].includes(d_sig))
{if(c_at(4)===D_STALLED)return sigc(D_STALLED,S_SIDE,c);if(c_at(4)===U_REALIGN)return sigc(U_READY,S_SIDE,c);}
if(d_sig===D_STALLED&&c_at(4)===D_STALLED)return sigc(U_READY,S_SIDE,c);if(d_sig===U_REALIGN&&c_at(4)===U_REALIGN)return sigc(D_STALLED,S_SIDE,c);if(d_sig===U_READY&&c_at(4)===U_REALIGN)return sigc(U_READY,S_SIDE,c);}
return sigc(c_at(4),S_SIDE,c);}
function mwatch(cand)
{if(cand.cell===4)return cand;if(cand.hasOwnProperty("color"))return cand;if(view[cand.cell].food!==0)return sigc(D_FOOD,S_SIDE,0);if(is_harvestable(cand.cell))return sigc(D_FOOD,S_SIDE,0);if(view[cand.cell].ant!==null)return sigc(U_PANIC,S_SIDE,0);return cand;}
function marcher_decision()
{if(c_at(4)===U_PANIC||this_ant().food>0)return saboteur();var gatherer_count=0;var enemy_count=0;for(tcell of SCAN_MOVES)
{if(is_ally(tcell)&&view[tcell].ant.type===GATHERER)gatherer_count++;else if(is_enemy(tcell)&&!is_harvestable(tcell))enemy_count++;}
if(gatherer_count>1||enemy_count>0)return saboteur();var colored_neighbors=0;for(tcell of SCAN_MOVES)
if(c_at(tcell)>1)colored_neighbors++;if(colored_neighbors>5)return saboteur();var c=view_corner();switch(neighbor_type(c))
{case ONE_CORNER:return mwatch(mdec_one_corner(c));case ONE_EDGE:return mwatch(mdec_one_edge(c));case EE_BENT:return mwatch(mdec_ee_bent(c));case EE_STRAIGHT:return mwatch(mdec_ee_straight(c));case EC_LEFT:return mwatch(mdec_ec_left(c));case EC_RIGHT:return mwatch(mdec_ec_right(c));case EC_SPAWN:return mwatch(mdec_ec_spawn(c));case THREE_MARCH:return mwatch(mdec_three_march(c));case THREE_STAND:return mwatch(mdec_three_stand(c));case THREE_RECOVER:return mwatch(mdec_three_recover(c));case THREE_UNSTAND:return mwatch(mdec_three_unstand(c));case THREE_HANG:return mwatch(mdec_three_hang(c));case THREE_UNHANG:return mwatch(mdec_three_unhang(c));case FOUR_Z:return mwatch(mdec_four_z(c));case FOUR_STAIRS:return mwatch(mdec_four_stairs(c));default:return saboteur();}}
function opening_queen()
{for(tcell of rand_perm(SCAN_MOVES))
if(view[tcell].food===1)return{cell:tcell};var has_ally=false;var proxs=[0,0,0,0,0,0,0,0,0];for(tcell of SCAN_MOVES)
{if(view[tcell].ant!==null)
{has_ally=true;for(var i=0;i<9;i++)proxs[i]-=NEARS[tcell][i];}}
if(has_ally)
{var prox_order=index_sort(proxs);for(var i=8;i>=0;i--)
{var i_cell=prox_order[i];if(view[i_cell].ant===null&&view[i_cell].food===0)return{cell:i_cell};}}
if(this_ant().food>0)
{var num_ants=0;for(tcell of SCAN_MOVES)
if(view[tcell].ant!==null)num_ants++;if(num_ants===0)
{var is_clear=true;var num_black_corners=0;var black_corner=null;for(var tcell=0;tcell<9;tcell++)
{if(CORNERS.includes(tcell))
{if(c_at(tcell)===8)
{num_black_corners++;black_corner=tcell;}
else if(c_at(tcell)!==1)is_clear=false;}
else if(c_at(tcell)!==1)is_clear=false;}
if(num_black_corners===1&&is_clear)return{cell:CCW[black_corner][7],type:GATHERER};}}
if(c_at(4)!==8)return{cell:4,color:8};var cands=[0,0,0,0,9,0,0,0,0];for(tcell of SCAN_MOVES)
if(c_at(tcell)===8)
for(var i=0;i<9;i++)cands[i]-=NEARS[tcell][i];var cand_order=index_sort(cands);for(var i=8;i>=0;i--)
{var i_cell=cand_order[i];if(view[i_cell].ant===null&&view[i_cell].food===0)return{cell:i_cell};}
return{cell:4,color:8};}
function early_queen()
{var gcell=null;var ally_count=0;for(tcell of rand_perm(SCAN_MOVES))
{if(is_ally(tcell))
{ally_count++;if(view[tcell].ant.type===GATHERER&&EDGES.includes(tcell))gcell=tcell;}}
if(gcell===null)return opening_queen();for(tcell of rand_perm(CORNERS))
if(view[tcell].food>0&&NEARS[tcell][gcell]===5)
{if(c_at(tcell)===D_FOOD)return{cell:tcell};else return{cell:tcell,color:D_FOOD};}
for(tcell of rand_perm(EDGES))
if(view[tcell].food>0)
{if(c_at(tcell)!==D_FOOD&&NEARS[tcell][gcell]===4)
return{cell:tcell,color:D_FOOD};}
if(c_at(4)===D_FOOD)
{if(c_at(CCW[gcell][2])===D_FOOD&&view[CCW[gcell][2]].food===0)
return{cell:CCW[gcell][2],color:D_MARCH};return{cell:4,color:D_MARCH};}
if(c_at(CCW[gcell][6])===D_FOOD&&view[CCW[gcell][6]].food===0)
return{cell:CCW[gcell][6],color:D_MARCH};if(EDGES.includes(gcell)&&this_ant().food>2&&ally_count===1)
{var num_clear_cells=0;var num_down_food=0;var is_valid=true;for(var tcell=0;tcell<9;tcell++)
{if(c_at(tcell)===D_FOOD)
{num_down_food++;if(tcell!==4&&tcell!==gcell)is_valid=false;}
if(c_at(tcell)===D_MARCH)num_clear_cells++;}
if(is_valid&&num_down_food===1&&num_clear_cells===8)
{var food_factor=QFORMP_MAX-QFORMP_MIN
var food_coefficient=QFORMP_DECAY/food_factor
var actual_prob=food_factor/(food_coefficient*(this_ant().food-3)+1)+QFORMP_MIN;if(rand_choice(actual_prob))return{cell:CCW[gcell][1],type:rand_choice(.5)?MARCHER_A:MARCHER_B};else return{cell:gcell,color:D_MARCH};}}
return{cell:CCW[gcell][7]};}
function qwatch(cand)
{if(cand.hasOwnProperty("type")&&this_ant().food===0)return sigc(U_PANIC,S_SIDE,0);if(cand.hasOwnProperty("type")&&view[cand.cell].food!==0)return sigc(U_PANIC,S_SIDE,0);if(cand.cell===4)return cand;if(cand.hasOwnProperty("color"))return cand;if(is_enemy(cand.cell))return sigc(U_PANIC,S_SIDE,0);if(is_ally(cand.cell))return sigc(c_at(4),S_SIDE,0);return cand;}
function eqwatch(cand)
{if(cand.hasOwnProperty("type")&&this_ant().food===0)return qwatch(opening_queen());if(cand.hasOwnProperty("type")&&view[cand.cell].food!==0)return qwatch(opening_queen());if(cand.cell===4)return cand;if(cand.hasOwnProperty("color"))return cand;if(is_enemy(cand.cell))return qwatch(opening_queen());if(is_ally(cand.cell))return qwatch(opening_queen());return cand;}
function qdec_ee_straight(c)
{return sigc(c_at(4),S_SIDE,c);}
function qdec_ee_bent(c)
{return{cell:CCW[c][2]};}
function qdec_ec_skewed(c)
{if(view[CCW[c][5]].ant.type!==GATHERER)return opening_queen();if(this_ant().food>0&&view[c].ant.type===MARCHER_A)return{cell:CCW[c][7],type:MARCHER_B};if(this_ant().food>0&&view[c].ant.type===MARCHER_B)return{cell:CCW[c][7],type:MARCHER_A};return opening_queen();}
function qdec_ec_spawn(c)
{if(view[CCW[c][3]].ant.type!==GATHERER)return opening_queen();if(this_ant().food>0&&view[c].ant.type===MARCHER_A)return{cell:CCW[c][1],type:MARCHER_B};if(this_ant().food>0&&view[c].ant.type===MARCHER_B)return{cell:CCW[c][1],type:MARCHER_A};return opening_queen();}
function qdec_cc_edged(c)
{if(view[c].ant.type!==GATHERER)return opening_queen();if(this_ant().food>0&&view[CCW[c][2]].ant.type===MARCHER_A)return{cell:CCW[c][1],type:MARCHER_B};if(this_ant().food>0&&view[CCW[c][2]].ant.type===MARCHER_B)return{cell:CCW[c][1],type:MARCHER_A};return opening_queen();}
function qdec_three_march(c)
{var u_sig=PUPS[c_at(c)][c_at(CCW[c][1])];if(u_sig===D_STALLED)
{if(c_at(CCW[c][3])===D_MARCH&&[D_MARCH,D_GATHERER].includes(c_at(4)))
return sigc(D_STALLED,S_FRONT,c);if(c_at(CCW[c][3])===U_READY&&c_at(4)===D_STALLED)return sigc(U_READY,S_FRONT,c);}
if(u_sig===D_MARCH&&c_at(CCW[c][3])===U_READY&&c_at(4)===U_READY)
return sigc(D_MARCH,S_FRONT,c);if(u_sig===U_READY&&c_at(CCW[c][3])===U_REALIGN&&c_at(4)===U_READY)
if(c_at(CCW[c][1])===D_MARCH)return sigc(D_MARCH,S_FRONT,c);return sigc(c_at(4),S_FRONT,c);}
function qdec_three_stand(c)
{var u_sig=PUPS[c_at(c)][c_at(CCW[c][7])];if(u_sig===D_STALLED)
{if(c_at(CCW[c][3])===D_MARCH&&c_at(4)===D_GATHERER)return sigc(D_STALLED,S_FRONT,c);if(c_at(CCW[c][3])===U_READY&&c_at(4)===D_STALLED)return sigc(U_READY,S_FRONT,c);}
if(u_sig===D_MARCH&&c_at(CCW[c][3])===U_READY&&c_at(4)===U_READY)
return sigc(D_MARCH,S_FRONT,c);if(u_sig===U_READY&&c_at(CCW[c][3])===U_REALIGN&&c_at(4)===U_READY)
if(c_at(CCW[c][1])===D_MARCH)return sigc(D_MARCH,S_FRONT,c);return sigc(c_at(4),S_FRONT,c);}
function qdec_three_recover(c)
{var u_sig=PUPS[c_at(c)][c_at(CCW[c][1])];if(u_sig===D_FOOD)return sigc(D_FOOD,S_FRONT,c);if(this_ant().food>0&&[D_STALLED,U_READY].includes(u_sig))
{var food_factor=QFSPAWNP_MAX-QFSPAWNP_MIN
var food_coefficient=QFSPAWNP_DECAY/food_factor
var actual_prob=food_factor/(food_coefficient*(this_ant().food-1)+1)+QFSPAWNP_MIN;if(rand_choice(actual_prob))return{cell:CCW[c][3]};}
var provisional=lchk(c)
if(provisional!==null)return sigc(provisional,S_FRONT,c);return sigc(c_at(4),S_FRONT,c);}
function qdec_three_unstand(c)
{var u_sig=PUPS[c_at(c)][c_at(CCW[c][7])];if(this_ant().food>0&&u_sig===D_STALLED&&c_at(CCW[c][5])===D_MARCH&&c_at(4)===D_STALLED)
{var food_factor=QBSPAWNP_MAX-QBSPAWNP_MIN
var food_coefficient=QBSPAWNP_DECAY/food_factor
var actual_prob=food_factor/(food_coefficient*(this_ant().food-1)+1)+QBSPAWNP_MIN;if(rand_choice(actual_prob))return{cell:CCW[c][3]};}
if(u_sig===D_STALLED&&c_at(CCW[c][5])===U_READY&&c_at(4)===D_STALLED)
return sigc(U_READY,S_FRONT,c);return sigc(u_sig,S_FRONT,c);}
function qdec_three_block(c)
{var u_sig=PUPS[c_at(c)][c_at(CCW[c][1])];return sigc(u_sig,S_FRONT,c);}
function qdec_three_side(c)
{var u_sig=PUPS[c_at(CCW[c][1])][c_at(CCW[c][2])];return sigc(u_sig,S_FRONT,CCW[c][2]);}
function queen_wait()
{var c=view_corner();switch(neighbor_type(c))
{case ONE_EDGE:{if(this_ant().food>1)return{cell:CCW[c][3],type:GATHERER};}
break;case EC_LEFT:{var u_sig=PUPS[c_at(c)][c_at(CCW[c][1])];if(u_sig===D_GATHERER)return sigc(D_GATHERER,S_FRONT,c);if(u_sig===U_REALIGN&&[U_REALIGN,U_SENTINEL].includes(c_at(c)))
if([U_REALIGN,U_SENTINEL].includes(c_at(CCW[c][1])))
return eqwatch(early_queen());var provisional=lchk(c);if(provisional!==null)return sigc(provisional,S_FRONT,c);if(this_ant().food>1)
{if(c_at(CCW[c][3])!==D_MARCH)return{cell:CCW[c][3],color:D_MARCH};return{cell:CCW[c][3],type:GATHERER};}}
break;case EC_RIGHT:{var u_sig=PUPS[c_at(c)][c_at(CCW[c][7])];if(u_sig===D_GATHERER)return sigc(D_GATHERER,S_FRONT,c);if(u_sig===U_REALIGN&&[U_REALIGN,U_SENTINEL].includes(c_at(c)))
if([U_REALIGN,U_SENTINEL].includes(c_at(CCW[c][7])))
return eqwatch(early_queen());var provisional=lchk(c);if(provisional!==null)return sigc(provisional,S_FRONT,c);if(this_ant().food>1)
{if(c_at(CCW[c][5])!==D_MARCH)return{cell:CCW[c][5],color:D_MARCH};return{cell:CCW[c][5],type:GATHERER};}}
break;}
if(c_at(4)!==U_PANIC)return sigc(U_PANIC,S_SIDE,c);else return opening_queen();}
function queen_march()
{var c=view_corner();switch(neighbor_type(c))
{case EE_STRAIGHT:return qwatch(qdec_ee_straight(c));case EE_BENT:return qwatch(qdec_ee_bent(c));case EC_SKEWED:return qwatch(qdec_ec_skewed(c));case EC_SPAWN:return qwatch(qdec_ec_spawn(c));case CC_EDGED:return qwatch(qdec_cc_edged(c));case THREE_MARCH:return qwatch(qdec_three_march(c));case THREE_STAND:return qwatch(qdec_three_stand(c));case THREE_RECOVER:return qwatch(qdec_three_recover(c));case THREE_UNSTAND:return qwatch(qdec_three_unstand(c));case THREE_BLOCK:return qwatch(qdec_three_block(c));case THREE_SIDE:return qwatch(qdec_three_side(c));default:return eqwatch(early_queen());}}
function queen_decision()
{marcher_count=0;gatherer_count=0;excess_gatherers=0;for(tcell of SCAN_MOVES)
{if(is_ally(tcell))
{if(view[tcell].ant.type===MARCHER_A||view[tcell].ant.type===MARCHER_B)marcher_count++;if(view[tcell].ant.type===GATHERER)
{if(EDGES.includes(tcell)||is_gatherer_marcher(tcell))gatherer_count++;else excess_gatherers++;}}
else if(is_enemy(tcell))return opening_queen();}
if(marcher_count>0&&gatherer_count===1&&excess_gatherers===0)return qwatch(queen_march());else if(marcher_count>0&&gatherer_count===0&&excess_gatherers===0)return qwatch(queen_wait());else if(gatherer_count===1&&excess_gatherers===0)return eqwatch(early_queen());else return opening_queen();}
function main_decide()
{switch(this_ant().type)
{case QUEEN:return queen_decision();case GATHERER:return gatherer_decision();case MARCHER_A:case MARCHER_B:return marcher_decision();default:return sanitize(saboteur());}}
return main_decide();

Gambaran

Pengajuan ini bertujuan untuk membuat garis semut yang dapat menyapu area tersebut. Warna digunakan sebagai sinyal untuk membantu ratu mengoordinasikan garis, bukan sebagai penanda jalan.

Pengajuan ini menggunakan tiga jenis pekerja selain ratu:

  • Tipe 1: Formasi marcher, fase A
  • Tipe 2: Formasi marcher, fase B
  • Tipe 3: Pengumpul
  • Tipe 4: Dicadangkan untuk penggunaan di masa mendatang

Semut dibuat dalam garis diagonal lebar penuh sebagai berikut:

    A
    BA
     BA
      BA
       BA
        BA
         BA
          QG

Empat semut pertama yang diciptakan semut adalah pengumpul dan pawai, A atau B dengan probabilitas yang sama, kemudian masing-masing satu. Setelah itu, semut dibuat dengan probabilitas setelah menemukan makanan, berganti-ganti antara A dan B. Dalam formasi, pengumpul ratu bergantian antara dua fase berbaris, tergantung pada formasi pawai yang terakhir dibuat.

Fase awal

Ketika sang ratu muncul, ia melakukan jalan lurus dengan kecepatan separuh rawa standar, mencoba menghindari menelusuri jalannya. Setelah ini memberinya sepotong makanan, dia melahirkan pengumpul. Setelah mengumpulkan 3 potong makanan, untuk setiap potongan makanan tambahan, sang ratu memiliki kemungkinan moderat untuk menelurkan 3 pekerja dalam rutinitas pembentukan formasi yang tersandi kode, dan sambungannya pun lepas landas.

Perilaku umum

Semut berbaris berbaris, dengan semut fase A dan fase B bergantian antara berhenti dan bergerak. Fase ini dikenali melalui pencocokan pola sekutu tetangga mereka, karena semut tidak dapat menyimpan keadaan. Semut selalu bergerak sedemikian rupa sehingga mereka tetap berdekatan dengan setidaknya dua semut lainnya.

Ketika seekor semut berada di belakang penghalang secara diagonal, ia menghabiskan gilirannya menembakkan sinyal yang sesuai. Sinyal bergerak secara instan ke arah ratu (terima kasih kepada semua pekerja yang berada dalam urutan pembuatan), sementara ke atas hanya dapat mengatur kecepatan lampu. Marcher hulu yang berdekatan mengenali sinyal dan mempropagandakan sinyal untuk menembak sinyal yang berbeda di atas garis.

Buruh yang dipisahkan dari garis (baik karena kesalahan atau karena kepanikan) menjadi penyabot, mengacak-acak sarang dan berusaha menghalangi pekerja musuh yang mereka temui. Mereka akan secara aktif menghindari formasi jika mereka melewatinya, karena penggabungan kembali tidak praktis.

Koleksi makanan

Ketika makanan ditemukan, garis berhenti. Namun, karena sinyal hanya dapat mengatur kecepatan cahaya ke hulu, dibutuhkan waktu untuk sinyal untuk bergerak ke atas, yang menyebabkan hulu ditekuk menjadi garis lurus. Semut di ujungnya mencegahnya sepenuhnya melengkung dengan menekuk ke kail, tetapi mereka juga terhenti.

Pekerja yang menemukan makanan menembakkan sinyal makanan alih-alih sinyal jelas yang biasa, yang disiarkan dan diperhatikan semua semut hilir. Belokan berikutnya, sinyal yang sama dikenali hulu dan diterjemahkan menjadi sinyal penyelarasan kembali, yang bergerak ke hulu dan menyebabkan pekerja di hulu juga berhenti ketika mereka menerima sinyal. Pekerja terakhir pada akhirnya mempertahankan tikungan pada akhirnya untuk menjaga kedekatan dengan pekerja lain.

Sinyal hilir membuatnya ke ratu dalam satu putaran, yang pengumpul mengakui sebagai sinyal untuk berjalan di garis. Mengikuti tepi sinyal makanan, pengumpul berjalan maju sampai menemukan makanan, dan kemudian pergi ke arah yang berlawanan untuk mengembalikannya ke ratu. Jika ada lebih banyak makanan yang tertunda, sinyal makanan tetap ada; jika tidak, sinyal yang terhenti sekarang menembak ke bawah garis, menandakan sang ratu untuk menembak sinyal yang siap, yang pada gilirannya menandakan titik tikungan untuk menembak ke bawah sinyal pawai.

Begitu antrean kembali ke barisan, para pekerja yang macet melanjutkan pawai mereka saat antrean mengejar mereka. Proses terjadi pada semut ketika sinyal datang kepada mereka, sehingga sangat praktis (dan memang terjadi) bahwa garis dilanjutkan sebelum sinyal menyelaraskan kembali mencapai akhir garis, atau untuk bagian hulu dari garis untuk berhenti di hilir sebelum itu menerima sinyal meluruskan kembali hilir.

Di luar jalur

Garis ini dirancang untuk bertahan dari pencukuran, dan setiap barisan pekerja yang bersebelahan lebih lama 3, bersama dengan seorang ratu dan pengumpul, dalam urutan penciptaan, adalah formasi gerak maju yang berfungsi penuh. Meskipun garis ini cukup dapat diandalkan, itu rentan terhadap penghalang oleh pekerja musuh. Ketika ini terjadi, semut yang paling langsung di depan pekerja musuh memancarkan sinyal panik. Belokan berikutnya, ia memotong jalur dan berjalan sendiri, diikuti oleh semua pekerja hulu yang kemudian menemukan ketidakkonsistenan dalam tidak memiliki siapa pun di sebelah kanan mereka, dan melakukan hal yang sama. Para pawai hilir tidak terganggu oleh acara ini, dan terus berbaris.

Begitu keluar dari jalur, pekerja menjadi penyabot. Mereka mencoba untuk mewarnai ulang area berwarna dengan warna sekitarnya untuk membuat kekacauan yang agak menyerupai area asli tetapi tidak mengandung pola yang penting untuk fungsi sarang. Mereka akan secara aktif bertahan di sekitar pekerja musuh dan mencoba untuk menghalangi atau menyesatkan mereka, dan secara aktif akan menghindari sekutu untuk mencegah mereka mengganggu garis. Jika mereka tidak dikelilingi oleh warna, mereka melakukan jalan lurus setengah-kecepatan untuk mencari area berwarna baru agar bercampur.

Pekerja sabotase mungkin berakhir dalam formasi secara kebetulan, tetapi kurangnya seorang ratu yang menopang ujung kanan harus berarti bahwa mereka pecah segera setelahnya. Jika tidak, ajukan bug.

Ekstra

Penjarahan ratu adalah pekerjaan yang sedang berlangsung. Para pekerja akan mengenali ratu musuh sebagai makanan daripada sebagai pekerja musuh, tetapi bagaimana tepatnya interaksi ini setelahnya tidak dicoba dan dilepaskan.

Untuk mencegah sinyal warna yang ada mengganggu garis, pekerja akan mengubah warna daerah sekitarnya putih jika mereka akan mengirim sinyal warna, tetapi sudah berdiri di atas sinyal warna yang ingin mereka kirim. Pembersihan lingkungan ini sangat kuat sehingga formasi berjalan dapat menembus sarang berwarna dengan kecepatan penuh tanpa masalah.

Pemijahan Ratu dikendalikan oleh probabilitas. Seiring berjalannya permainan dan sang ratu memiliki lebih banyak makanan, ia menjadi kurang bersemangat untuk menelurkan baris baru dan menambah pekerja ke jalur yang ada, hingga probabilitas batas asimptotik yang dapat disempurnakan.

Melakukan

  • Bersihkan celah logis
  • Uji dan perbaiki penjarahan ratu
  • Lihat apakah pekerja musuh dapat berjalan-jalan
  • Selidiki pengurangan status sinyal
  • Lihat apakah memangkas secara intensif membantu pekerja akhir

Catatan rilis

1.0: Versi pertama disiapkan untuk pengiriman, rilis awal

1.0.1: Melakukan reduksi logis, dibuat kompatibel dengan lebih banyak pengontrol

1.1: Memadatkan banyak hal, meningkatkan logika terkait dengan kasus kesalahan

1.1.1: Perbaikan terbaru untuk memecahkan masalah diskualifikasi

1.2: Menghilangkan kebuntuan lebih lanjut, penyabot sekarang dirombak

1.3: Mengurangi tingkat pemijahan ratu, memberi penyabot tune

1.3.1: Selanjutnya mengurangi ratu bertelur, dan memperbaiki bug yang didiskualifikasi

1.4: Penyetelan parameter


5

Semut Giling

/*Ants will try to move diagonally in the following fashion:
 * 2
 * 51
 *
 *Type 1 and queen are the two core ants
 */


switch (view[4].ant.type) {

  case 1: //Guiding ant
    //Look for queen, try to move diagonally
    if (view[7].ant && view[7].ant.friend && view[7].ant.type === 5 && !view[8].ant) return {cell: 8};
    else if (view[5].ant && view[5].ant.friend && view[5].ant.type === 5 && !view[2].ant) return {cell: 2};
    else if (view[3].ant && view[3].ant.friend && view[3].ant.type === 5 && !view[6].ant) return {cell: 6};
    else if (view[1].ant && view[1].ant.friend && view[1].ant.type === 5 && !view[0].ant) return {cell: 0};
    else return {cell: 4};
  case 2: //Other wing
    //Look for queen, try to move diagonally. If there is food, rotate the other way to start rotating procedure
    if (view[7].ant && view[7].ant.friend && view[7].ant.type === 5 && !view[6].ant) {
      if (view[6].food) {
        if (!view[8].ant) return {cell: 8};
        else return {cell: 4};
      } else return {cell: 6};
    } else if (view[5].ant && view[5].ant.friend && view[5].ant.type === 5 && !view[8].ant) {
      if (view[8].food) {
        if (!view[2].ant) return {cell: 2};
        else return {cell: 4};
      } else return {cell: 8};
    } else if (view[3].ant && view[3].ant.friend && view[3].ant.type === 5 && !view[0].ant) {
      if (view[0].food) {
        if (!view[6].ant) return {cell: 6};
        else return {cell: 4};
      } else return {cell: 0};
    } else if (view[1].ant && view[1].ant.friend && view[1].ant.type === 5 && !view[2].ant) {
      if (view[2].food) {
        if (!view[0].ant) return {cell: 0};
        else return {cell: 4};
      } else return {cell: 2};
    } else return {cell: 4};
  case 5: //Queen ant

    //If forever alone
    if (!view[1].ant && !view[3].ant && !view[5].ant && !view[7].ant) {
      if (view[4].color === 2) { //If on colored square, try to move
        if (view[0].color === 2 && !view[8].ant) return {cell: 8};
        else if (view[2].color === 2 && !view[6].ant) return {cell: 6};
        else if (view[6].color === 2 && !view[2].ant) return {cell: 2};
        else if (view[8].color === 2 && !view[0].ant) return {cell: 0};
        //Can't find color, or path is blocked? try diagonals regardless of color
        else if (!view[0].ant) return {cell: 0};
        else if (!view[2].ant) return {cell: 2};
        else if (!view[6].ant) return {cell: 6};
        else if (!view[8].ant) return {cell: 8};
        //Everything else failed? Stay put.
        else return {cell: 4};
      } else { //If not on colored square, look for food, or set current color to 2.
        if (view[4].ant.food >= 1) { //Try to make Guiding ant
          if (!view[1].ant && !view[1].food) return {cell: 1, type: 1};
          else if (!view[3].ant && !view[3].food) return {cell: 3, type: 1};
          else if (!view[5].ant && !view[5].food) return {cell: 5, type: 1};
          else if (!view[7].ant && !view[7].food) return {cell: 7, type: 1};
        }
        for (var i = 0; i < 9; i++) { //Look for food
          if (view[i].food) return {cell: i};
        }
        return {cell: 4, color:2};
      }
    } else { //Queen has partner
      //Make other wing
      if (view[4].ant.food >= 1) {
        if (view[1].ant && view[1].ant.friend && view[1].ant.type === 1 && !view[3].ant && !view[3].food && !view[5].ant) return {cell: 3, type: 2};
        else if (view[3].ant && view[3].ant.friend && view[3].ant.type === 1 && !view[7].ant && !view[7].food && !view[1].ant) return {cell: 7, type: 2};
        else if (view[5].ant && view[5].ant.friend && view[5].ant.type === 1 && !view[1].ant && !view[1].food && !view[7].ant) return {cell: 1, type: 2};
        else if (view[7].ant && view[7].ant.friend && view[7].ant.type === 1 && !view[5].ant && !view[5].food && !view[3].ant) return {cell: 5, type: 2};
      }

      //If food is orthogonal to Queen, stay put
      if (view[1].food || view[3].food || view[5].food || view[7].food) return {cell: 4};

      //Look for guiding type 1 ant, try to move diagonally
      else if (view[7].ant && view[7].ant.friend && view[7].ant.type === 1 && !view[6].ant) return {cell: 6};
      else if (view[5].ant && view[5].ant.friend && view[5].ant.type === 1 && !view[8].ant) return {cell: 8};
      else if (view[3].ant && view[3].ant.friend && view[3].ant.type === 1 && !view[0].ant) return {cell: 0};
      else if (view[1].ant && view[1].ant.friend && view[1].ant.type === 1 && !view[2].ant) return {cell: 2};
    }
  default: return {cell: 4};
}

Semut-semut ini bekerja dengan konsep yang mirip dengan Semut Forensik Dave . Namun, mereka bergerak secara diagonal, dan bergerak dalam kelompok 3.

Fase 1: Perebutan makanan

Semut Ratu hanya bergerak secara diagonal sampai ia dapat melihat satu potong makanan. Ini melakukan ini menggunakan konsep yang mirip dengan Romanesco Road , di mana jejak warna yang diletakkan di belakang Ratu dapat membantunya mencari tahu jalan mana yang maju.

Fase 2: 2 semut

Sang Ratu membuat semut "pemandu" tipe 1 yang baru, yang bermitra dengan sang Ratu untuk bergerak secara diagonal. Mereka masing-masing mencari tahu jalan mana yang maju relatif terhadap mitra masing-masing.

Fase 3: Steamrolling

Setelah Ratu dan pasangannya menemukan sepotong makanan, sang ratu menggunakannya untuk membuat semut tipe 2. Semut ini memiliki instruksi khusus untuk mengikuti Ratu, dan juga ikut. Ini membuat 3 barisan semut yang bergerak secara diagonal lebar, membuatnya cukup cepat untuk mendapatkan makanan.

Berputar

Jika semut tipe 2 melihat bahwa ia akan pindah ke beberapa makanan, ia malah akan bergerak ke arah lain, di mana semut tipe 1 dulu. Ini berarti bahwa semua semut akan memutar arah mereka bergerak, dan semut harus memiliki probabilitas yang sangat rendah untuk kembali ke titik awal.

Catatan: jika karena alasan tertentu (mungkin bertabrakan dengan semut lain?) Semut tipe 2 lahir sebelum semut tipe 1, maka rotasi ini akan menghasilkan semut tipe 2 yang mencoba pindah ke semut tipe 1. Untuk mengatasi ini, semut tipe 2 malah akan meninggalkan dirinya sendiri, dan biarkan Ratu membuat semut tipe 2 yang lain.


Selama salah satu percobaan saya, saya menemukan bahwa mengubah arah secara acak ketika menemukan makanan bisa sangat efektif untuk menghindari pembungkus tak terbatas. Ini mengurangi kemungkinan setiap langkah yang mencakup tanah baru, tapi mungkin itu bisa berguna di sini?
Dave

@Dave bagian tentang mengubah arah adalah hal yang menjadi perhatian saya. Jika saya dapat menemukan cara untuk mengubah arah tanpa kehilangan salah satu pekerja saya, maka ide ini pasti akan berhasil. Jika tidak, saya mungkin mencoba untuk memiliki warna sebagai penanda kapan harus meninggalkan pekerja, dan meminta ratu berubah arah.
K Zhang

@trichoplax Apakah Anda tahu benih yang digunakan selama diskualifikasi itu, dan mungkin nomor pindah juga? Akan sangat bermanfaat untuk mengetahui keadaan yang menyebabkan masalah itu.
K Zhang

@trichoplax Nevermind, saya baru saja pergi dan menambahkan beberapa cek kewarasan tambahan. (Mudah-mudahan) tidak akan didiskualifikasi lagi.
K Zhang

Anda dapat menggunakan seed untuk pengujian, yang memungkinkan rerunning untuk melihat apa yang terjadi jika ada yang salah, tetapi itu akan memberikan hasil yang sama setiap pertandingan, yang tidak baik untuk leaderboard. Turnamen papan peringkat dijalankan tanpa centang di kotak centang unggulan yang diunggulkan, yang artinya menggunakan cryptorandom untuk membuatnya seadil mungkin.
trichoplax

5

Medusa

function clean(move) {
    if (move["color"] == undefined) {
        if (view[move["cell"]].ant != null) {
            move = {
                cell: 4
            }
        }
        if (move["type"] == undefined) {
            if (view[4].ant.type == 5 && move["cell"] != 4 && view[move["cell"]].color > 2) {
                move["color"] = 1
            }
            if (view[move["cell"]].food == 1 && view[4].ant.type < 5 && view[4].ant.food > 0) {
                move = {
                    cell: 4
                }
            }
        } else if (view[4].ant.type != 5 || view[4].ant.food == 0 || view[move["cell"]].food != 0) {
            move = {
                cell: 4
            }
        }
    }
    return move
}

function coord(cell) {
    var x = (cell % 3) - 1
    var y = 1 - (cell - (cell % 3)) / 3
    return {
        x: x,
        y: y
    }
}

function getcell(x, y) {
    return (x + 1) + (1 - y) * 3
}

var diags = [0, 2, 8, 6]

var colorcounts = [0, 0, 0, 0, 0, 0, 0, 0, 0];
for (var i = 0; i < 9; i++) {
    colorcounts[view[i].color]++
}

var queen = -1
for (var i = 0; i < 9; i++) {
    if (view[i].ant != null && view[i].ant.friend == true && view[i].ant.type == 5) {
        queen = i
    }
}

var guard = -1
for (var i = 0; i < 9; i++) {
    if (view[i].ant != null && view[i].ant.friend == true && view[i].ant.type == 1) {
        guard = i
    }
}

var forager = -1
for (var i = 0; i < 9; i++) {
    if (view[i].ant != null && view[i].ant.friend == true && view[i].ant.type == 2) {
        forager = i
    }
}

var black = -1
for (var i = 0; i < 9; i++) {
    if (view[i].color == 8) {
        black = i
    }
}

var yellow = -1
for (var i = 0; i < 9; i++) {
    if (view[i].color == 2) {
        yellow = i
    }
}


if (view[4].ant.type == 5) {
    if (forager >= 0 && view[forager].color == 8) {
        return clean({
            cell: forager,
            color: 2
        })
    }

    if (guard == -1) {
        if (view[4].color == 3) {
            if (view[4].ant.food > 1) {
                return clean({
                    cell: 0,
                    type: 2
                })
            }
            return clean({
                cell: 0,
                type: 1
            })
        }
        if (view[4].ant.food >= 3) {
            return clean({
                cell: 4,
                color: 3
            })
        }
        if (view[4].color == 1) {
            return clean({
                cell: 4,
                color: 2
            })
        }
        for (var i = 0; i < 9; i++) {
            if (view[i].food == 1) {
                return clean({
                    cell: i
                })
            }
        }
        for (var i = 0; i < 4; i++) {
            if (view[diags[i]].color != 2 && view[diags[(i + 2) % 4]].color == 2) {
                return clean({
                    cell: diags[i]
                })
            }
        }
        return clean({
            cell: 0
        })
    }

    var state = 3
    var max = 0
    for (var i = 3; i <= 4; i++) {
        if (colorcounts[i] > max) {
            max = colorcounts[i]
            state = i
        }
    }

    if (state == 3) {
        if (black >= 0 && forager == -1) {
            return clean({
                cell: black,
                type: 2
            })
        }
        if (forager >= 0 && view[forager].color != 2) {
            return clean({
                cell: 0,
                color: 8
            })
        }
        if (colorcounts[3] == 9) {
            return clean({
                cell: 4,
                color: 4
            })
        }
    }
    if (state == 4) {
        if (colorcounts[4] == 9) {
            return clean({
                cell: 4,
                color: 3
            })
        }
    }
    return clean({
        cell: 4
    })
}
if (view[4].ant.type == 1) {
    var dest = 0
    var destmap = [1, 0, 1, 1, 4, 1, 7, 8, 7]
    dest = destmap[queen]
    if (view[queen].color != view[dest].color && (view[queen].color == view[4].color || view[4].color == view[dest].color)) {
        if (queen < 4 && view[dest].color > 2 && view[dest].color < 5) {
            return clean({
                cell: queen,
                color: view[dest].color
            })
        }
        return clean({
            cell: dest,
            color: view[queen].color
        })
    }
    return clean({
        cell: dest
    })
}
if (view[4].ant.type == 2) {
    if (queen >= 0 && view[4].color == 8) {
        return clean({
            cell: 4
        })
    }
    var state = 3
    var max = 0
    for (var i = 5; i <= 7; i++) {
        if (colorcounts[i] > max) {
            max = colorcounts[i]
            state = i
        }
    }
    var flowx = 0
    var flowy = 0
    for (var i = 0; i < 9; i++) {
        for (var j = i + 1; j < 9; j++) {
            var loci = coord(i)
            var locj = coord(j)
            var dx = locj.x - loci.x
            var dy = locj.y - loci.y
            var cyc = 0
            if (view[i].color >= 5 && view[i].color <= 7 && view[j].color >= 5 && view[j].color <= 7) {
                var cyc = ((view[j].color - view[i].color) % 3 + 3) % 3
                if (cyc == 2) {
                    cyc = -1
                }
            } else if (view[i].color >= 5 && view[i].color <= 7) {
                cyc = 0.1
            } else {
                cyc = -0.1
            }
            flowx += cyc * dx / (dx * dx + dy * dy)
            flowy += cyc * dy / (dx * dx + dy * dy)

        }
    }
    if (flowx * flowx > flowy * flowy) {
        flowy = 0
    } else {
        flowx = 0
    }
    if (flowx < 0) {
        flowx = -1
    }
    if (flowy < 0) {
        flowy = -1
    }
    if (flowx > 0) {
        flowx = 1
    }
    if (flowy > 0) {
        flowy = 1
    }
    if (queen >= 0) {
        var locq = coord(queen)
        flowx = -locq.x
        flowy = -locq.y
        state = 5
    }
    if (view[4].ant.food > 0) {
        if (guard >= 0) {
            var destmap = [1, 0, 1, 1, 4, 1, 7, 8, 7]
            return clean({
                cell: destmap[guard]
            })
        }
        dest = getcell(-flowx, -flowy)
        if (dest != 7) {
            dest = 1
        }
        if (view[dest].color >= 5 && view[dest].color <= 7) {
            return clean({
                cell: dest
            })
        }
        if (view[dest - 1].color >= 5 && view[dest - 1].color <= 7) {
            return clean({
                cell: dest - 1
            })
        }
        return clean({
            cell: 4
        })
    }
    if (view[4].color >= 5 && view[4].color <= 7) {
        state = view[4].color
    }
    var nextc = ((state - 4) % 3 + 5)
    var prevc = ((state - 3) % 3 + 5)
    var centerdest
    centerdest = getcell(flowx, flowy)
    if (view[centerdest].color != state && view[centerdest].color != nextc) {
        return clean({
            cell: centerdest,
            color: nextc
        })
    }
    for (var dest = 1; dest < 9; dest++) {
        var locd = coord(dest)
        var net = locd.x * flowx + locd.y * flowy
        if (net > 0 && view[dest].color != view[centerdest].color) {
            return clean({
                cell: dest,
                color: view[centerdest].color
            })
        }
    }
    for (var dest = 0; dest < 9; dest++) {
        if (view[dest].food == 1) {
            if (view[dest].color >= 5 && view[dest].color <= 7) {
                return clean({
                    cell: dest
                })
            }
            return clean({
                cell: dest,
                color: state
            })
        }
    }
    if (centerdest == 4 && view[0].color >= 5 && view[0].color <= 7) {
        return clean({
            cell: 0
        })
    }
    if (centerdest > 0 && view[centerdest - 1].color >= 5 && view[centerdest - 1].color <= 7) {
        return clean({
            cell: centerdest - 1
        })
    }
    return clean({
        cell: centerdest
    })
}

Bot ini ... tidak baik, tetapi menggunakan beberapa strategi keren yang saya pikir akan dimasukkan dalam bot semut masa depan saya. Namanya berasal dari bentuk koloni di papan permainan.

Medusa beraksi

Fase 1: Investasi Awal

Ratu bergerak lurus secara diagonal hingga mengambil 3 potong makanan, cukup untuk memulai koloni. Setelah mengumpulkan potongan-potongan itu, ia mengendap (menjadi ratu stasioner) dan kemudian membuat 2 penjelajah dan 1 penjaga. Sebagai bagian strategi yang menarik, kehadiran semut penjaga itu sendiri yang memicu fase berikutnya, dan inilah yang mencegah sang ratu untuk tidak lagi bergerak lagi.

Fase 2: Berkolonisasi

Di sini, tiga jenis semut memainkan peran yang berbeda:

Ratu

Sang ratu perlahan berosilasi antara dua negara, dengan bantuan penjaga. Keadaan saat ini adalah apa yang menentukan apakah sepotong makanan yang baru diperoleh diubah menjadi pekerja, sehingga sekitar 50% makanan diinvestasikan kembali di koloni. Seluruh area 3x3 yang berisi ratu digunakan untuk menyimpan status, sehingga setiap penghapusan dapat dibatalkan dan kondisi pulih.

Menjaga

Penjaga itu menjalani seluruh hidupnya yang berdekatan dengan sang ratu, mengitarinya secara acak.

Penjaga itu memainkan peran kunci dalam menjaga keadaan ratu. Ia mencoba untuk memperbaiki kesalahan di area 3x3 ratu. Ketika ada dua warna alternatif yang valid di area tersebut, yang mana dari dua warna tersebut menjadi kondisi "terkoreksi" relatif acak. Namun, setelah tercapai konsensus, ratu membalik kotaknya ke warna yang berlawanan, memulai kembali prosesnya. Inilah yang menyebabkan osilasi keadaan ratu, dan dilakukan dengan cara yang sangat tahan kesalahan.

Penjaga juga berfungsi sebagai penjaga gerbang "istana" ratu. Ketika seorang penjelajah melihat penjaga, ia dapat bergerak di sebelah ratu meskipun ratu berada di luar jangkauan visual.

Pencuri

Para pengumpul membuat pola merah-hijau-biru siklik ketika mereka meninggalkan koloni, dan mengikutinya ke belakang ketika mereka membawa makanan. Mereka akhirnya mengecat area yang cukup besar, karena jalur yang sangat luas diperlukan untuk memastikan bahwa jalur tidak menjadi kusut dan bahwa mereka dapat menemukan jalan mereka kembali bahkan jika beberapa sel rusak.

Lintasan khas seorang penjelajah:

Jalan penjelajah

Perhatikan bagaimana biasanya bergerak dalam garis lurus tetapi kadang-kadang berubah 90 derajat. Ini adalah hasil dari cara ia berjalan secara acak di jalannya sendiri saat meletakkannya.


Saya bertanya-tanya apakah penghapus jejak saya akan dapat membuat para pengumpul itu terdampar. Ini bagus untuk menghapus jalan lebar, seperti yang Anda lihat ketika itu bertentangan dengan lubang hitam.
pppery

@pperry Terkadang memang demikian.
PhiNotPi

Mungkinkah membuat pekerja kembali lebih efisien dengan hanya bergerak dalam siklus r-> b-> g-> r?
CalculatorFeline

@trichoplax Saya tidak menyadari bahwa itu adalah sesuatu yang tidak boleh dilakukan. Tetap diperbaiki sekarang.
PhiNotPi

Itu di bawah "Diskualifikasi": "Sel untuk menghasilkan pekerja tidak kosong." tetapi saya telah mengedit spec untuk membuatnya sedikit lebih eksplisit sekarang: "Sel untuk menghasilkan pekerja tidak kosong (berisi makanan atau semut)."
trichoplax

5

Brownian Jig

Pemain ini tidak menghasilkan pekerja, dan sang ratu bergerak secara acak. Gerakan acak adalah karena ratu mengembalikan arah yang sama setiap kali, tetapi input sel yang terlihat disajikan pada orientasi acak setiap gerakan, mencegah gerakan berada dalam garis lurus.

Blok kode pertama dalam jawaban adalah yang secara otomatis termasuk dalam permainan:

// Full version that won't be disqualified for moving onto another ant

// Move to food if visible
for (var i=0; i<9; i+=1) {
    if (view[i].food) {
        return {cell:i}
    }
}

// Otherwise move to one of the diagonal cells if not occupied
for (var i=0; i<9; i+=2) {
    if (!view[i].ant) {
        return {cell:i}
    }
}

// Otherwise one of the vertical or horizontal cells if not occupied
for (var i=1; i<9; i+=2) {
    if (!view[i].ant) {
        return {cell:i}
    }
}

// Otherwise don't move at all
return {cell:4}

Berikut ini adalah versi yang lebih sederhana yang tidak memeriksa semut lain, tetapi memiliki perilaku yang identik sampai titik itu didiskualifikasi karena mencoba melangkah ke semut lain:

// Basic version for an intuitive understanding

// Move to food if visible
for (var i=0; i<9; i+=1) {
    if (view[i].food) {
        return {cell:i}
    }
}

// Otherwise move "up and left", which will be a random direction
return {cell:0}

Blok kode kedua ini tidak akan diambil oleh game - ini berarti Anda dapat memasukkan blok kode tambahan sebagai bagian dari penjelasan jawaban Anda. Pastikan bahwa blok kode yang ingin Anda lawan dalam gim adalah yang pertama dalam jawabannya.

Untuk contoh menghasilkan gerakan garis lurus meskipun orientasi input acak, lihat Jalan Romanesco .


Mengapa harus menandai warna?
Solomon Ucko

1
Pertanyaan bagus. Ini hanyalah contoh awal jawaban untuk memberi orang sesuatu untuk membantu mengambil aturan. Skornya bisa dua kali lebih tinggi dengan hanya tidak menandai warna apa pun, tetapi sebagai contoh saya ingin jalurnya terlihat jelas untuk membantu pemahaman.
trichoplax

Begitu ya, masuk akal.
Solomon Ucko

Saya sekarang telah diedit untuk menghapus tanda warna, karena ini adalah gangguan yang tidak perlu (dan berlebihan). Ratu sekarang akan pindah ke makanan jika terlihat, dan mendukung gerakan diagonal untuk menutupi lebih banyak tanah. Tidak ada warna yang digunakan sama sekali.
trichoplax

4

La Reine Bleue

var Queen = 5;
var QueenTrail = [];
var EnemyAnts = [];
var EnemyColors = [];
var QueenTrailColor = 7;
var QueenTrailColor2 = 8;
var QueensPosition = -1; //Future use...

var rotations =   
[ 0,1,2,
  3,4,5,
  6,7,8,

  6,3,0,
  7,4,1,
  8,5,2,

  8,7,6,
  5,4,3,
  2,1,0,

  2,5,8,
  1,4,7,
  0,3,6];

var moves = [];
getMoves();
return findBestMove();

function getMoves()
{
    var matchIdx = -1;
    //Initialization of current state
    for(ii = 0; ii < 9; ii++)
    {
        if(ii != 4)
        {
            if(view[ii].color == QueenTrailColor)
            {
                QueenTrail.push(ii);
            }
            else if(view[ii].color == QueenTrailColor2)
            {
                QueenTrail.push(ii);
            }
            else if(view[ii].color != 1)
            {
                EnemyColors.push(ii);
            }
        }

        if(ii != 4 && view[ii].ant != null)
        {
            if(view[ii].ant.friend)
            {
                if(view[ii].ant.type == Queen)
                {
                    QueensPosition = ii * ii;
                }
            }
            else
            {
                EnemyAnts.push(ii);
            }
        }
    }

    switch (view[4].ant.type) 
    {
        case Queen:
        {        
            //first get the food
            for (var ii = 0; ii < 9; ii++) 
            {
                if (view[ii].food > 0 && view[ii].ant == null) 
                {
                    moves.push(getCell(ii)) ;
                }
            }
            if(EnemyAnts.length == 0)
            {
                lm(AA(-QueenTrailColor),AA(4), {cell:4, color:QueenTrailColor});
            }

            if(QueenTrail.length >= 5 || EnemyAnts.length > 0)
            {
                lm(AA(-QueenTrailColor), AA(0,1,2),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,1,3),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,1,5),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,1,6),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,1,7),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,1,8),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,2,3),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,2,6),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,2,7),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,3,7),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,3,8),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,5,7),{cell:0});
                lm(AA(-QueenTrailColor), AA(1,2,7),{cell:1});
                lm(AA(-QueenTrailColor), AA(1,3,5),{cell:1});
                lm(AA(-QueenTrailColor), AA(0,1),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,2),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,3),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,5),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,7),{cell:0});
                lm(AA(-QueenTrailColor), AA(0,8),{cell:0});
                lm(AA(-QueenTrailColor), AA(1,3),{cell:1});
                lm(AA(-QueenTrailColor), AA(1,7),{cell:1});
            }
            if(QueenTrail.length == 4)
            {
                lmQT(AA(0,1,2,3),{cell:7});
                lmQT(AA(0,1,2,5),{cell:7});
                lmQT(AA(0,1,2,6),{cell:7});
                lmQT(AA(0,1,2,7),{cell:5});
                lmQT(AA(0,1,2,8),{cell:7});
                lmQT(AA(0,1,3,5),{cell:7});
                lmQT(AA(0,1,3,7),{cell:8});
                lmQT(AA(0,1,3,8),{cell:2});
                lmQT(AA(0,1,5,6),{cell:8});
                lmQT(AA(0,1,5,7),{cell:6});
                lmQT(AA(0,1,5,8),{cell:3});
                lmQT(AA(0,1,6,7),{cell:8});
                lmQT(AA(0,1,6,8),{cell:2});
                lmQT(AA(0,1,7,8),{cell:2});
                lmQT(AA(0,2,3,7),{cell:8});
                lmQT(AA(0,2,3,8),{cell:6});
                lmQT(AA(0,2,6,8),{cell:1});
                lmQT(AA(0,3,5,7),{cell:2});
                lmQT(AA(0,3,5,8),{cell:2});
                lmQT(AA(1,3,5,7),{cell:0});
            }
            if(QueenTrail.length == 1)
            {                
                lmQT(AA(0), {cell:8});
                lmQT(AA(1), {cell:7});
            }
            else if(QueenTrail.length == 0)
            {
                moves.push(getCell(1));
            }

            if(QueenTrail.length == 0) // starting out or someone is messing with us
            {
                moves.push( getCellColor(1, QueenTrailColor));
            }    
            else if (QueenTrail.length >= 5) //queen is stuck? move her randomly until we get a straight trail
            {    
                moves.push( getCellColor(1, QueenTrailColor2));
            }
            else if (QueenTrail.length >= 3)
            {
                lmQT(AA(0,1,2),{cell:3});
                lmQT(AA(0,1,3),{cell:7});
                lmQT(AA(0,1,5),{cell:3});
                lmQT(AA(0,1,6),{cell:7});
                lmQT(AA(0,1,7),{cell:3});
                lmQT(AA(0,1,8),{cell:3});
                lmQT(AA(0,2,3),{cell:6});
                lmQT(AA(0,2,6),{cell:3});
                lmQT(AA(0,2,7),{cell:1});
                lmQT(AA(0,3,7),{cell:8});
                lmQT(AA(0,3,8),{cell:5});
                lmQT(AA(0,5,7),{cell:6});
                lmQT(AA(1,2,7),{cell:0});
                lmQT(AA(1,3,5),{cell:6});
            }
            else if(QueenTrail.length == 2)
            {
                lmQT(AA(0,1),{cell:7});
                lmQT(AA(0,2),getCellColor(1, QueenTrailColor));
                lmQT(AA(0,3),{cell:6});
                lmQT(AA(0,5),{cell:1});
                lmQT(AA(0,7),{cell:3});
                lmQT(AA(0,8),{cell:3});
                lmQT(AA(1,3),{cell:6});
                lmQT(AA(1,7),{cell:0});
            }
            else if(QueenTrail.length == 1) //we are either going in a straight line or trapped?
            {    
                if(view[4].ant.food > 0)
                {
                    lmQT(AA(0), getCell(0));
                    //clear out the area for the ants
                    if(EnemyColors.length > 0)
                    {
                        moves.push( getCellColor(EnemyColors[0],1));
                    }
                }
                lmQT(AA(0), getCell(8));
                lmQT(AA(1), getCell(7));
                lmQT(AA(2), getCell(6));
            }
            break;
        }
    }
    moves.push( getCell(4));
}

function leftOfPos(x)
{
    if (x == 0)
    {
        return 3;
    }
    else if (x == 1)
    {
        return 0;
    }
    else if (x == 2)
    {
        return 1;
    }
    else if (x == 3)
    {
        return 6;
    }
    else if (x == 5)
    {
        return 2;
    }
    else if (x == 6)
    {
        return 7;
    }
    else if (x == 7)
    {
        return 8;
    }
    else if (x == 8)
    {
        return 5;
    }
}

function findBestMove() 
{
    var keeper = 0;
    for(var ii = 0; ii < moves.length ; ii++)
    {
        if(moves[ii].cell < 0 || moves[ii].cell > 8 || (moves[ii].cell != 4 && (moves[ii].color == null || moves[ii].color == 0) && view[moves[ii].cell].ant != null) || (view[moves[ii].cell].food > 0 && (view[4].ant.food > 0 && view[4].ant.type < 5))) 
        {
            continue;
        }
        else if(moves[ii].type != null && (view[moves[ii].cell].ant != null || view[moves[ii].cell].food > 0 || view[0].color == 1)) //semi random here. 
        {
            continue;
        }
        else
        {
            keeper = ii;
            break;
        }
    }
    return moves[keeper];
}

function lm(matchingColors, coords, matchCell)
{
    var matchTarget = coords.length ;
    var matchCount = [0,0,0,0];
    var returnVal = -1;
    for(var ii = 0; ii < coords.length; ii++)
    {        
        for(var jj = 0; jj < 4; jj++)
        {
            var actualIndex = rotations[coords[ii] + (jj * 9)];
            var foundMatch = false;
            for(var kk = 0; kk < matchingColors.length; kk++)
            {
                var matchingColor = matchingColors[kk];

                if(matchingColor >= 1 && matchingColor <= 8 && view[actualIndex].color == matchingColor)
                {
                    foundMatch = true;
                    break;
                }    
                else if(matchingColor < 0 && view[actualIndex].color != -matchingColor)
                {
                    foundMatch = true;
                    break;
                }
            }
            if(foundMatch)
            {
                matchCount[jj] = matchCount[jj] + 1;
                if(matchCount[jj] == matchTarget)
                {
                    matchCell.cell = rotations[matchCell.cell + (jj * 9)];
                    moves.push(matchCell);
                    returnVal = jj;
                }
            }
        }
    }
    return returnVal;
}

function lmQT(coords, matchCell)
{
    return lm(AA(QueenTrailColor, QueenTrailColor2), coords,matchCell);
}

function AA()
{
    return arguments;
}

function getCell(x)
{
    return {cell:x};
}

function getCellColor(x, y)
{
    return {cell:x, color:y};
}

Ratu biru akan meninggalkan jejak biru, ia menghindari hitam dan meninggalkan remah-remah hitam jika 'macet'.

Ia mencari "bentuk" biru / hitam tertentu menggunakan keempat rotasi dan membuat daftar gerakan potensial. Gerakan yang mendiskualifikasi dihilangkan dan satu hasil dipilih. Ratu agak dapat diprediksi tetapi bentuk tertentu akan menyebabkan keacakan. Dia sebagian besar akan mengubah arah setelah menemukan makanan karena fakta bahwa orientasi akan mengubah gerakan mana yang ditemukan pertama kali.


3
Selamat datang di situs ini! :)
DJMcMayhem

Senang berada di sini!
Beberapa pelari

4

FireFlyMkII

Perhatikan bahwa sampai entri saya selesai, kode ini akan jelek karena trans-dikompilasi dari sumber pengembangan master saya yang sedang dilakukan di java.

    // maps current view's cells' indecies to the rotated cell's location's indecies for each direction
    var rotate = 
            [[0,1,2,3,4,5,6,7,8],
             [2,5,8,1,4,7,0,3,6],
             [8,7,6,5,4,3,2,1,0],
             [6,3,0,7,4,1,8,5,2]];

    // the colours that form the pattern of the trail back to the queen
    var TRAIL_COLOR_A = 8;
    var TRAIL_COLOR_B = 2;
    var TRAIL_COLOR_C = 5;
    var trailColours = [TRAIL_COLOR_A,TRAIL_COLOR_B,TRAIL_COLOR_C];
    var trailColoursLookUp = [-1,-1,1,-1,-1,2,-1,-1,0];

    var ORIENTATION_MARKER = 8;

    // Queens Modes
    var QUEEN_MODE_HUNTING_MOVING = 6;
    var QUEEN_MODE_HUNTING_PAINTING = 1;
    var QUEEN_MODE_RESETTING = 5;
    var QUEEN_MODE_RESETTING_SPAWNING = 3;
    var QUEEN_MODE_COUNTING_EVEN = 7;
    var QUEEN_MODE_COUNTING_ODD = 4;
    var QUEEN_MODE_NESTING = 2;

    // the number of non-blank (i.e. not colour 1 ) colours to used to encode the queen's worker spawn counter. Min of 1. Max of 7
    var SPAWN_COUNTER_NON_BLANK_COLOURS_COUNT = 7;


    // the maximum number that can be encoded using the queen's worker spawn counter
    var SPAWN_COUNTER_MAX = SPAWN_COUNTER_NON_BLANK_COLOURS_COUNT*SPAWN_COUNTER_NON_BLANK_COLOURS_COUNT*SPAWN_COUNTER_NON_BLANK_COLOURS_COUNT -1;//SPAWN_COUNTER_NON_BLANK_COLOURS_COUNT * SPAWN_COUNTER_USED_CELLS_COUNT_MAX;

    // the minimum game ticks between spawning a worker. Min of 0, Max of SPAWN_COUNTER_MAX
    var TICKS_BETWEEN_FOOD_RETURN_MAX = 50;

    // No Operation... i.e. stay put do nothing
    var NO_OP = {cell:4};

    var ANT_TYPE_WORKER = 1; 
    var ANT_TYPE_QUEEN = 5;

    var orientationMarkerRotation = -1;

    var i=0;
    var j=0;

    // returns true of the provided colour is a trail colour
function isTrailColour(colour)
    {
        return colour === trailColours[0] || colour === trailColours[1] || colour === trailColours[2];
    }

    // returns the colour of the colour in the trail away from queen
function nextTrailColor(currentTrailColour)
    {
        return trailColours[(trailColoursLookUp[currentTrailColour]+1)%3];
    }

    // returns the colour of the colour in the trail toward from queen
function prevTrailColor(currentTrailColour)
    {
        return trailColours[(3+trailColoursLookUp[currentTrailColour]-1)%3];
    }

    // RNG
function randomNumberGenerator(seed)
    {
        return (1103515245 * seed + 12345) % 2147483647;
    }

    // returns a positive random integer based on the provided ant's view and seed
function randomInt(view,seed)
    {
        for (var i=0;i<9;i++)
        {
            if (view[i].ant !=null)
            {
                seed=randomNumberGenerator(seed+view[i].ant.food);
                seed=randomNumberGenerator(seed+view[i].ant.type);
                seed=randomNumberGenerator(seed+(view[i].ant.friend?1:0));
            }
            seed=randomNumberGenerator(seed+view[i].color);
            seed=randomNumberGenerator(seed+view[i].food);
        }
        return seed<0?-seed:seed;
    }

    // SHUFFLE *NOT* IMPLEMENTED 
function shuffleIndecies(view,seed,range)
    {
        var indecies = new Array(range);

        for (var i=0;i<range;i++)
        {
            indecies[i]=i;
        }

        return indecies;
    }

function processOrientation(view)
    {
        // count orientation markers
        var orientationMarkerCount = 0;
        for (var i=0; i<rotate.length;i++)
        {
            if (view[rotate[i][1]].color === ORIENTATION_MARKER)
            {
                orientationMarkerCount++;
                orientationMarkerRotation = i;
            }
        }

        // corruption detected
        if (orientationMarkerCount >1)
        {
            return {cell:4, color: QUEEN_MODE_RESETTING};
        }

        // place the orientation marker
        if (orientationMarkerCount === 0)
        {
            return {cell:1, color: ORIENTATION_MARKER};
        }
        return null;
    }

function incrementSpawnCounter(view)
    {
        var action = processOrientation(view);
        if (action != null)
        {
            return action;
        }

        var newCount = decodeThreeCellsToInt(view) + 1;

        var MSD = view[rotate[orientationMarkerRotation][3]].color-1;
        var NSD = view[rotate[orientationMarkerRotation][7]].color-1;
        var LSD = view[rotate[orientationMarkerRotation][5]].color-1;

        var MSDisEven =(MSD & 1) ===0;
        var NSDisEven =(NSD & 1) ===0; 

        var MSDdelta =  Math.floor(newCount / 49) - MSD;
        var NSDdelta =  (MSDisEven ? Math.floor(Math.floor(newCount%49)/7) :( 6 - Math.floor(Math.floor(newCount%49)/7))) - NSD;
        var LSDdelta =  (((MSDisEven && NSDisEven) || (!MSDisEven && !NSDisEven)) ? Math.floor(newCount%7) :( 6 - Math.floor(newCount%7))) - LSD;

        // check for roll over 
        if (MSDdelta > 6)
        {
            return {cell:rotate[orientationMarkerRotation][3], color:1};
        }

        // Most Significant Digit (cell) update
        if (MSDdelta != 0)
        {
            return {cell:rotate[orientationMarkerRotation][3], color:(MSD+MSDdelta)+1};
        }

        // Next Significant Digit (cell) update
        if (NSDdelta != 0)
        {
            return {cell:rotate[orientationMarkerRotation][7], color:(NSD+NSDdelta)+1};
        }

        // Least Significant Digit (cell) update
        if (LSDdelta != 0)
        {
            return {cell:rotate[orientationMarkerRotation][5], color:(LSD+LSDdelta)+1};
        }

        return null;
    }

function decodeThreeCellsToInt(view)
    {
        var MSD = view[rotate[orientationMarkerRotation][3]].color-1;
        var NSD = view[rotate[orientationMarkerRotation][7]].color-1;
        var LSD = view[rotate[orientationMarkerRotation][5]].color-1;

        var MSDisEven =(MSD & 1) ===0;
        var NSDisEven =(NSD & 1) ===0; 
        return MSD * 49 + 
               (MSDisEven?NSD:6-NSD) * 7 + 
               ((MSDisEven && NSDisEven) || (!MSDisEven && !NSDisEven)?LSD:6-LSD);
    }
    // Performs a paint command to reset the queen's worker spawn counter to 0.
    // NOTE that is may take multiple calls on sequential game ticks to complete the reset.
    // returns null if the counter is reset
function resetSpawnCounter(view)
    {
        var orientationMarkerCount = 0;
        for (i=1; i<9; i+=2) 
        {
            if (view[i].color === ORIENTATION_MARKER && orientationMarkerCount ===0)
            {
                orientationMarkerCount++;
            }
            else if (view[i].color!=1)
            {
                return {cell:i, color:1};
            }
        }

        // place the orientation marker
        if (orientationMarkerCount === 0)
        {
            return {cell:1, color: ORIENTATION_MARKER};
        }

        return null;
    }


function spawnNewWorker(view,type,defaultAction)
    {
        // ensure that we do not try and create a worker when having no food
        if (view[4].ant.food > 0)
        {
            // now try to spawn an ant
            if (view[1].ant===null && view[1].food===0)
            {
                return {cell:1, type:type};
            }

            // previous spawn cell was blocked, try another 
            if (view[3].ant===null && view[3].food===0)
            {
                return {cell:3, type:type};
            }

            // previous spawn cell was blocked, try another
            if (view[5].ant===null && view[5].food===0)
            {
                return {cell:5, type:type};
            }

            // previous spawn cell was blocked, try another
            if (view[7].ant===null && view[7].food===0)
            {
                return {cell:7, type:type};
            }
        }
        return defaultAction;
    }

function isCellTrailToQueen(cell,currentAntCellColour)
    {
        // is cell containing our queen, or is the cell the next cell colour on the trail back
        return (cell.ant!=null && cell.ant.friend && cell.ant.type === ANT_TYPE_QUEEN) ||
                cell.color === prevTrailColor(currentAntCellColour);
    }

    // entry point into ant logic
function getAction(view)
    {
        var random = 1;
        var food = view[4].ant.food;
        var currentCellColour = view[4].color;

/////////////////////////////////////// QUEEN ///////////////////////////////////////
        if (view[4].ant.type === ANT_TYPE_QUEEN)
        {
            // move to visible food. this queen is greedy!
            for (i=0; i<9; i++) 
            {
                if (view[i].food>0) {
                    return {cell:i};
                }
            }

            // see if we have spawned a worker in the last few turns
            var workerSpawned = false;
            // look in orthogonal cells
            for (i=1; i<9; i+=2) 
            {
                // ant detected and is friendly and of worker type
                if (view[i].ant !=null && view[i].ant.friend && view[i].ant.type===ANT_TYPE_WORKER)
                {
                    workerSpawned=true;
                    break;
                }
            }

            var queenMode = currentCellColour;
            var foodModRemainder=1;

            var minFoodLatch = 0;
            if (food>=500) minFoodLatch = 500;
            else if (food>=400) minFoodLatch = 400;
            else if (food>=300) minFoodLatch = 300;
            else if (food>=200) minFoodLatch = 200;
            else if (food>=100) minFoodLatch = 100;
            else if (food>=50) minFoodLatch = 50;
            else if (food>=20) minFoodLatch = 30;
            else if (food>=20) minFoodLatch = 20;
            else if (food>=10) minFoodLatch = 10;
            else if (food>=5) minFoodLatch = 5;

            switch (queenMode)
            {
                case QUEEN_MODE_HUNTING_MOVING:
                {


                    // move to the cell mirror of the trail cell
                    for (i=0; i<9; i++) {
                        if (view[i].ant===null && view[i].color===QUEEN_MODE_HUNTING_MOVING) {
                            if (view[8-i].ant==null)
                            {
                                return {cell:8-i};
                            }
                        }
                    }

                    // Otherwise move to one of the diagonal cells if not occupied
                    for (i=0; i<9; i+=2) 
                    {
                        if (view[i].ant===null) 
                        {
                            return {cell:i};
                        }
                    }

                    // Otherwise move to one of the vertical or horizontal cells if not occupied
                    for (i=1; i<9; i+=2)
                    {
                        if (view[i].ant===null)
                        {
                            return {cell:i};
                        }
                    }
                    return {cell:4};
                }
                case QUEEN_MODE_HUNTING_PAINTING:
                {
                    // no food found, change to move mode
                    if (food ===0)
                    {
                        // Queenie places a trail
                        return {cell:4, color:QUEEN_MODE_HUNTING_MOVING};
                    }
                    // found food, now change to nesting mode
                    else
                    {
                        return {cell:4, color:QUEEN_MODE_NESTING};
                    }
                }

                // initialise colony
                case QUEEN_MODE_NESTING:
                {
                    // we have spawned a worker so change to counting mode
                    if (workerSpawned===true)
                    {
                        return {cell:4, color:QUEEN_MODE_COUNTING_ODD};
                    }

                    var action = processOrientation(view);
                    if (action != null)
                    {
                        return action;
                    }

                    // ensure that we have the initial band constructed around the queen
                    for (i=0; i<9; i+=2) 
                    {
                        if (i!=4 && view[i].color!=TRAIL_COLOR_A)
                        {
                            return {cell:i, color:TRAIL_COLOR_A};
                        }
                    }

                    // ensure that the counter cells are reset.
                    action = resetSpawnCounter(view);
                    if (action != null)
                    {
                        return action;
                    }

                    // spawn initial worker
                    return spawnNewWorker(view, ANT_TYPE_WORKER, NO_OP);
                }


                case QUEEN_MODE_RESETTING_SPAWNING:

                    // spawn the worker if we have not spawned a worker in the last few turns 
                    if (!workerSpawned===true)
                    {
                        return spawnNewWorker(view, ANT_TYPE_WORKER, NO_OP);
                    }
                    // must have spawned a worker previously, so reset the counter 
                    var action = resetSpawnCounter(view);

                    // still in process of resetting counter...
                    if (action != null)
                    {
                        return action;
                    }


                    // spawn counter as been reset. We will set the queen back to counting mode;
                    return {cell:4, color:food%2===0?QUEEN_MODE_COUNTING_EVEN:QUEEN_MODE_COUNTING_ODD};

                case QUEEN_MODE_RESETTING:

                    action = resetSpawnCounter(view);

                    // still in process of resetting counter...
                    if (action != null)
                    {
                        return action;
                    }


                    // spawn counter as been reset. We will set the queen back to counting mode;
                    return {cell:4, color:food%2===0?QUEEN_MODE_COUNTING_ODD:QUEEN_MODE_COUNTING_EVEN};

                case QUEEN_MODE_COUNTING_ODD:
                    foodModRemainder = 2;
                case QUEEN_MODE_COUNTING_EVEN:
                {
                    foodModRemainder--;

                    action = processOrientation(view);
                    if (action != null)
                    {
                        return action;
                    }

                    var spawnCounter = decodeThreeCellsToInt(view);

                    // repair any damage to the initial band constructed around the queen
                    for (i=0; i<9; i+=2) 
                    {
                        if (i!=4 && view[i].color!=TRAIL_COLOR_A)
                        {
                            return {cell:i, color:TRAIL_COLOR_A};
                        }
                    }

//                    // spawn interval time threshold as been reached and we have food to convert into workers...
//                    if (spawnCounter>=TICKS_BETWEEN_FOOD_RETURN_MAX && food>minFoodLatch)
//                    {
//                        // change to reset spawn counter mode to spawn a new worker
//                        return new Paint(4,QUEEN_MODE_RESETTING_SPAWNING);
//                    }
//
//                    // Check to see if a worker has just returned some food.
//                    if (food>0 && food%2 == foodModRemainder)
//                    {
//                        // change to reset spawn counter mode
//                        return new Paint(4,QUEEN_MODE_RESETTING);
//                    }

                    // Check to see if a worker has just returned some food.
                    if (food>0 && food%2 === foodModRemainder)
                    {

                        // spawn interval time threshold as been reached and we have food to convert into workers...
                        if (spawnCounter>=TICKS_BETWEEN_FOOD_RETURN_MAX && food>0)
                        {
                            // change to reset spawn counter mode to spawn a new worker
                            return {cell:4, color:QUEEN_MODE_RESETTING_SPAWNING};
                        }

                        // change to reset spawn counter mode
                        return {cell:4, color:QUEEN_MODE_RESETTING};
                    }


                    if (spawnCounter < SPAWN_COUNTER_MAX)
                    {
                        // simply increment the counter
                        return incrementSpawnCounter(view);
                    }

                    return NO_OP;
                }
                default:
                {
                }

            }


        }

/////////////////////////////////////// WORKER ///////////////////////////////////////



        var expectedNextPathColourToEdge = nextTrailColor(currentCellColour);

        // worker is looking for food
        if (food===0)
        {
/////////////////////////////////// WORKER HUNTNING //////////////////////////////////    

            // determine whether we are a recently spawned worker
            for (var i=1;i<9;i+=2)
            {
                // are we orthogonal to the queen?
                if (view[i].ant!=null && view[i].ant.friend && view[i].ant.type === ANT_TYPE_QUEEN)
                {
                    // test to see whether queen's counter has reset so worker is free to move
                    if (view[i].color === QUEEN_MODE_COUNTING_ODD || view[i].color === QUEEN_MODE_COUNTING_EVEN )
                    {
                        for (var j=1;j<9;j+=2)
                        {
                            if (view[j].ant===null && view[j].color===TRAIL_COLOR_A)
                            {
                                return {cell:j};
                            }
                        }
                    }
                    // wait until queen's counter has reset
                    return NO_OP;
                }
            }

//            // this is to try and unstick stuck ants... not overly well i might add
//            if (randomInt(view,1)%20==1)
//            {
//                // attempt to pick an empty random trail-cell
//                for (i = randomInt(view,666)%9;i>0;i--)
//                {
//                    if (view[i].ant==null && view[i].food==0 && isTrailColour(view[i].color))
//                    {
//                        return new Move(i);
//                    }
//                }
//            }

            // see if there is any food off band that is enclosed or almost enclosed by trail cells
            // if so, then move to claim the food
            if (view[1].food>0 &&
                isTrailColour(view[0].color) &&
                isTrailColour(view[2].color))
            {
                return {cell:1};
            }

            if (view[3].food>0 &&
                isTrailColour(view[0].color) &&
                isTrailColour(view[6].color))
            {
                return {cell:3};
            }

            if (view[5].food>0 &&
                isTrailColour(view[2].color) &&
                isTrailColour(view[8].color))
            {
                return {cell:5};
            }

            if (view[7].food>0 &&
                isTrailColour(view[6].color) &&
                isTrailColour(view[8].color))
            {
                return {cell:7};
            }


            // if not on trail, attempt see if cells surrounding are trail colours... and set our own cell accordingly
            if (!isTrailColour(currentCellColour))
            {
                for (var priority = 0; priority <4;priority++)
                {
                    // repeat for each rotation
                    var indecies = shuffleIndecies(view,random,4);
                    for (var j=0;j<4;j++)
                    {
                        var r = indecies[j];
                        switch(priority)
                        {
                        // C??  ...
                        // ???  .P.
                        // P?N  ...
                        case 0:
                            if (isTrailColour(view[rotate[r][0]].color) && 
                                nextTrailColor(view[rotate[r][0]].color) === view[rotate[r][8]].color &&
                                prevTrailColor(view[rotate[r][0]].color) === view[rotate[r][6]].color)
                            {
                                return {cell:4, color: view[rotate[r][6]].color};
                            }
                            break;

                        // ??C  ...
                        // ???  .C.
                        // N?C  ...
                        case 1:
                            if (isTrailColour(view[rotate[r][2]].color) && 
                                view[rotate[r][2]].color === view[rotate[r][8]].color &&
                                nextTrailColor(view[rotate[r][2]].color) === view[rotate[r][6]].color)
                            {
                                return {cell:4, color: view[rotate[r][2]].color};
                            }
                            break;

                        // C??  ...
                        // ???  .C.
                        // P??  ...
                        case 2:
                            if (isTrailColour(view[rotate[r][0]].color) && 
                                prevTrailColor(view[rotate[r][0]].color) === view[rotate[r][6]].color)
                            {
                                return {cell:4, color: view[rotate[r][0]].color};
                            }
                            break;

                        // C??  ...
                        // ???  .C.
                        // ???  ...
                        case 3:
                            if (isTrailColour(view[rotate[r][0]].color))
                            {
                                return {cell:4, color: view[rotate[r][0]].color};
                            }
                            break;
                        }
                    }
                }
                // we are completely lost! lets perform a random walk and hopefully find the surface again
                return {cell:view[2].ant === null?2:4};
            }

            // decide worker action...
            for (var priority = 0; priority <13;priority++)
            {
                // repeat for each rotation
                var indecies = shuffleIndecies(view,random,4);
                for (var j=0;j<4;j++)
                {
                    var r = indecies[j];
                    var cellToMoveTo =-1;

                    switch(priority)
                    {
                    /////// AVOID MOVING/PAINTING LOCK-STEP ///////

                    // X?X  ...
                    // ?C?  .M.
                    // C?W  ...

                    case 0:
                        if (view[rotate[r][6]].color === currentCellColour &&
                            !isTrailColour(view[rotate[r][0]].color) &&
                            !isTrailColour(view[rotate[r][2]].color) &&
                            view[rotate[r][8]].ant!=null &&
                            view[rotate[r][8]].ant.type!=ANT_TYPE_QUEEN) 
                        {
                            // step back on path back to queen
                            return NO_OP;
                        }
                        break;

                    /////// REPAIR PATH ///////

                    // P?X  ..C
                    // ?C?  ...
                    // C??  ...

                    case 1:
                        if (view[rotate[r][2]].color != currentCellColour &&
                            view[rotate[r][6]].color === currentCellColour &&
                            isCellTrailToQueen(view[rotate[r][0]],currentCellColour))
                        {
                            return {cell:rotate[r][2], color:currentCellColour};
                        }
                        break;

                    // ??C  N..
                    // ?C?  ...
                    // X?P  ...

                    case 2:
                        if (view[rotate[r][2]].color === currentCellColour &&
                            isCellTrailToQueen(view[rotate[r][8]],currentCellColour) &&
                            !isTrailColour(view[rotate[r][6]].color) &&
                            view[rotate[r][0]].color != expectedNextPathColourToEdge)
                        {
                            return {cell:rotate[r][0], color:expectedNextPathColourToEdge};
                        }
                        break;

                    // C?N  ...
                    // ?C?  .N.
                    // ??P  ...

                    case 3:
                        if (view[rotate[r][0]].color === currentCellColour &&
                            view[rotate[r][2]].color === expectedNextPathColourToEdge &&
                            isCellTrailToQueen(view[rotate[r][8]],currentCellColour))
                        {
                            return {cell:4, color:expectedNextPathColourToEdge};
                        }
                        break;

                    // C??  N..
                    // ?C?  ...
                    // ??P  ...

                    case 4:
                        if (view[rotate[r][0]].color === currentCellColour &&
                            isCellTrailToQueen(view[rotate[r][8]],currentCellColour))
                        {
                            return {cell:rotate[r][0], color:expectedNextPathColourToEdge};
                        }
                        break;

                    /////// MOVING ///////

                    // C?P  ...
                    // ?C?  ...
                    // ??C  N..
                    case 5:
                        if (view[rotate[r][0]].color === currentCellColour &&
                            view[rotate[r][8]].color === currentCellColour &&
                            isCellTrailToQueen(view[rotate[r][2]],currentCellColour) &&
                            view[rotate[r][6]].color != expectedNextPathColourToEdge)
                        {

                            if (view[rotate[r][6]].ant === null)
                            {
                                return {cell:rotate[r][6], color:expectedNextPathColourToEdge};
                            }
                            else
                            {
                                return NO_OP;
                            }
                        }
                        break;

                    // C?P  ...
                    // ?C?  ...
                    // N?C  M..
                    case 6:
                        if (view[rotate[r][0]].color === currentCellColour &&
                            view[rotate[r][8]].color === currentCellColour &&
                            isCellTrailToQueen(view[rotate[r][2]],currentCellColour) &&
                            view[rotate[r][6]].color === expectedNextPathColourToEdge)
                        {
                            cellToMoveTo = rotate[r][6];
                        }
                        break;

                    // Special case. we need to first paint the cell prior to moving other wise will cause corruption
                    // C??  ...
                    // ?C?  ...
                    // C??  ..N
                    case 7:
                        if (view[rotate[r][0]].color === currentCellColour &&
                            view[rotate[r][6]].color === currentCellColour &&
                            view[rotate[r][8]].color != expectedNextPathColourToEdge)
                        {
                            if (view[rotate[r][8]].ant === null)
                            {
                                return {cell:rotate[r][8], color:expectedNextPathColourToEdge};
                            }
                            else
                            {
                                return NO_OP;
                            }
                        }
                        break;
                    // C??  ...
                    // ?C?  ...
                    // C?N  ..M
                    case 8:
                        if (view[rotate[r][0]].color === currentCellColour &&
                            view[rotate[r][6]].color === currentCellColour &&
                            view[rotate[r][8]].color === expectedNextPathColourToEdge)
                        {
                            cellToMoveTo = rotate[r][8];
                        }
                        break;
                    // C??  ..M
                    // ?C?  ...
                    // P?C  ...
                    case 9:
                        if (view[rotate[r][0]].color === currentCellColour &&
                            view[rotate[r][8]].color === currentCellColour &&
                            isCellTrailToQueen(view[rotate[r][6]],currentCellColour))
                        {
                            cellToMoveTo = rotate[r][2];
                        }
                        break;
                    // C??  ...
                    // ?C?  ...
                    // P??  ..M
                    case 10:
                        if (view[rotate[r][0]].color === currentCellColour &&
                            isCellTrailToQueen(view[rotate[r][6]],currentCellColour))
                        {
                            cellToMoveTo = rotate[r][8];
                        }
                        break;
                    // C??  ...
                    // ?C?  ...
                    // ???  M..
                    case 11:
                        if (view[rotate[r][0]].color === currentCellColour)

                        {
                            cellToMoveTo = rotate[r][6];
                        }
                        break;
                    // ???  ...
                    // ?C?  ...
                    // P??  ?.M
                    case 12:
                        if (isCellTrailToQueen(view[rotate[r][6]],currentCellColour))

                        {
                            cellToMoveTo = rotate[r][8];
                        }
                        break;
                    }
                    if (cellToMoveTo>-1)
                    {
                        return {cell:view[cellToMoveTo].ant === null?cellToMoveTo:4};
                    }
                }
            }
            return NO_OP;
        }

        // worker is transporting food
        else
        {
            // worker deadlock avoidance
            for (i=0; i<9; i+=2)
            {
                // does the cell have another ant in it?
                if (i!=4 && view[i].ant!=null && !(view[i].ant.type===ANT_TYPE_QUEEN && view[i].ant.friend))
                {
                    // attempt to pick an empty random trail-cell
                    for (i = randomInt(view,54321)%9;i>0;i--)
                    {
                        if (view[i].ant===null && view[i].food===0 && isTrailColour(view[i].color))
                        {
                            return {cell:i};
                        }
                    }

                    // no luck... just pick a random empty cell
                    for (i = randomInt(view,12345)%9;i>0;i--)
                    {
                        if (view[i].ant===null && view[i].food===0)
                        {
                            return {cell:i};
                        }
                    }

                    // deadlock unavoidable!
                    return NO_OP;
                }
            }

//            // this is to try and unstick stuck ants
//            if (randomInt(view,1)%20==1)
//            {
//                // attempt to pick an empty random trail-cell
//                for (i = randomInt(view,666)%9;i>0;i--)
//                {
//                    if (view[i].ant==null && view[i].food==0 && isTrailColour(view[i].color))
//                    {
//                        return new Move(i);
//                    }
//                }
//            }


            // decide move action
            for (var priority = 0; priority <14;priority++)
            {
                // repeat for each rotation
                var indecies = shuffleIndecies(view,random,4);
                for (var j=0;j<4;j++)
                {
                    var r = indecies[j];
                    var cellToMoveTo =-1;

                    switch(priority)
                    {
                        // PRE-EMPTIVE PATH PRUNING

                    // C?X  X..
                    // ?X?  ...
                    // P?X  ...
                    case 0:
                        if (isTrailColour(view[rotate[r][0]].color) &&
                            !isTrailColour(view[rotate[r][2]].color) &&
                            !isTrailColour(view[rotate[r][4]].color) && 
                            prevTrailColor(view[rotate[r][0]].color) === view[rotate[r][5]].color)
                        {
                            return {cell:rotate[r][0], color: 1};
                        }
                        break;


                        // ?C?  ...
                        // CXN  X..
                        // ?X?  ...
                        case 1:
                            if (!isTrailColour(view[rotate[r][4]].color) &&
                                !isTrailColour(view[rotate[r][7]].color) &&
                                isTrailColour(view[rotate[r][1]].color) && 
                                view[rotate[r][3]].color === view[rotate[r][1]].color &&
                                nextTrailColor(view[rotate[r][1]].color) === view[rotate[r][5]].color)
                            {
                                return {cell:rotate[r][3], color: 1};
                            }
                            break;

                        // ?C?  ...
                        // PXC  ..X
                        // ?X?  ...
                        case 2:
                            if (!isTrailColour(view[rotate[r][4]].color) &&
                                !isTrailColour(view[rotate[r][7]].color) &&
                                isTrailColour(view[rotate[r][1]].color) && 
                                view[rotate[r][5]].color === view[rotate[r][1]].color &&
                                prevTrailColor(view[rotate[r][1]].color) === view[rotate[r][3]].color)
                            {
                                return {cell:rotate[r][5], color: 1};
                            }
                            break;

                        // N??  ..N
                        // ?C?  ...
                        // C?C  ...

                        case 3:
                            if (isTrailColour(view[rotate[r][4]].color) &&
                                view[rotate[r][2]].color != expectedNextPathColourToEdge &&
                                view[rotate[r][8]].color === currentCellColour &&
                                view[rotate[r][0]].color === expectedNextPathColourToEdge &&
                                view[rotate[r][6]].color === currentCellColour)
                            {
                                return {cell:rotate[r][2], color:expectedNextPathColourToEdge};
                            }
                            break;

                        // N??  ...
                        // ?X?  .C.
                        // C?C  ...


                        case 4:
                            if (!isTrailColour(view[rotate[r][4]].color) && 
                                isTrailColour(view[rotate[r][8]].color) &&
                                view[rotate[r][0]].color === nextTrailColor(view[rotate[r][8]].color) &&
                                view[rotate[r][8]].color === view[rotate[r][6]].color)
                            {
                                return {cell:rotate[r][4], color:view[rotate[r][8]].color};
                            }
                            break;

                        // N??  ..C
                        // ?C?  ...
                        // C?P  ...

                        case 5:
                            if (isTrailColour(view[rotate[r][4]].color) && 
                                view[rotate[r][0]].color === expectedNextPathColourToEdge &&
                                view[rotate[r][6]].color === currentCellColour &&
                                isCellTrailToQueen(view[rotate[r][8]],currentCellColour) &&
                                view[rotate[r][2]].color != currentCellColour)
                            {
                                return {cell:rotate[r][2], color:currentCellColour};
                            }
                            break;

                        // C?N  ...
                        // ?X?  .N.
                        // ??P  ...

                        case 6:
                            if (!isTrailColour(view[rotate[r][4]].color) && 
                                isTrailColour(view[rotate[r][0]].color) && 
                                view[rotate[r][2]].color === nextTrailColor(view[rotate[r][0]].color) &&
                                view[rotate[r][8]].color === prevTrailColor(view[rotate[r][0]].color))
                            {
                                return {cell:rotate[r][4], color:nextTrailColor(view[rotate[r][0]].color)};
                            }
                            break;

                        // ??P  ...
                        // ?X?  .N.
                        // ??N  ...

                        case 7:
                            if (!isTrailColour(view[rotate[r][4]].color) && 
                                isTrailColour(view[rotate[r][2]].color) &&
                                nextTrailColor(view[rotate[r][2]].color) === view[rotate[r][8]].color)
                            {
                                return {cell:4, color:view[rotate[r][8]].color};
                            }
                            break;



                        // if we are on the corner of a trail band... move opposite to the apex
                        case 8:
                            if (isTrailColour(currentCellColour) &&
                                view[rotate[r][0]].color === currentCellColour &&
                                view[rotate[r][2]].color === currentCellColour)
                            {
                                if (randomInt(view,currentCellColour)%2 === 0)
                                {
                                    cellToMoveTo = rotate[r][0];
                                }
                                else
                                {
                                    cellToMoveTo = rotate[r][2];
                                }
                            }
                            break;

                        // if we on the opposite corner of a trail band... move back toward the apex
                        case 9:
                            if (isTrailColour(currentCellColour) &&
                                isCellTrailToQueen(view[rotate[r][0]],currentCellColour) &&
                                isCellTrailToQueen(view[rotate[r][2]],currentCellColour))
                            {
                                if (randomInt(view,currentCellColour)%2 === 0)
                                {
                                    cellToMoveTo = rotate[r][0];
                                }
                                else
                                {
                                    cellToMoveTo = rotate[r][2];
                                }
                            }
                            break;
                        // if an adjacent cell is a trail to the queen, move that way
                        case 10:
                            if (isTrailColour(currentCellColour) &&
                                isCellTrailToQueen(view[rotate[r][0]],currentCellColour))
                            {
                                cellToMoveTo = rotate[r][0];
                            }
                            break;
                        // if we are not on a trail and an adjacent cell is a trail, then move that way
                        case 11:
                            if (!isTrailColour(currentCellColour) && isTrailColour(view[rotate[r][8]].color))
                            {
                                cellToMoveTo = rotate[r][8];
                            }
                            break;
                        // are we on a cell between trail cells? if so then move onto a cell on the trail.
                        case 12:
                            if (isTrailColour(view[rotate[r][1]].color))
                            {
                                cellToMoveTo = rotate[r][1];
                            }
                            break;
                        // are we on a terminal trail cell? if so then move onto a cell on the trail.
                        case 13:
                            if (isTrailColour(view[rotate[r][0]].color))
                            {
                                cellToMoveTo = rotate[r][0];
                            }
                            break;

                    }
                    if (cellToMoveTo>-1)
                    {
                        if (view[cellToMoveTo].ant != null || view[cellToMoveTo].food > 0) continue;
                        return {cell:cellToMoveTo};
                    }
                }
            }
            return NO_OP;
        }

    }

    return getAction(view);

VERSI 1

versi 1

Premis dasar dari entri ini adalah untuk mencari dalam spiral persegi yang terus berkembang. Ekspansi pencarian perimeter persegi akan menciptakan pola berulang tiga pita yang memungkinkan pengiriman makanan cepat dan kembali ke perimeter.

Di sana nama entri berasal dari musuh "kunang-kunang" dalam permainan puzzle klasik BolderDash, pola tiga warna yang berulang mengingatkan pada musuh.

Versi awal ini, walaupun memiliki bug laten, memenuhi persyaratan dasar untuk menjadi entri dasar: semut mengumpulkan dan mengembalikan makanan ke ratu. Namun, makanan segera dikonversi menjadi pekerja, sehingga tidak akan pernah berhasil di turnamen.

Namun untuk versi berikutnya saya pikir saya akan secara mendasar mengubah cara dan memesan semut bergerak / melukis: dari cat kemudian pindah, untuk pindah kemudian cat. Meskipun saya tidak bisa memastikan, saya pikir beberapa pola korupsi warna sel terjadi dari sel yang mengandung semut yang dicat ulang oleh semut yang berdekatan. Mengubah urutan akan membantu mengurangi risiko itu ... tetapi berpotensi akan lebih rumit / berisiko untuk bergerak sebelum melukis.

VERSI 2

versi 2

Versi ini lebih unggul, sekarang diputar 45 derajat memungkinkan eksplorasi lebih lanjut per langkah semut pekerja.

Itu juga sekarang mengumpulkan makanan (jika tidak terganggu)

Namun, tidak kuat dan mudah terganggu oleh jejak semut lain. Jadi itu bukan pesaing yang baik seperti saat ini.

Namun ketika dibiarkan sendiri, rata-rata 700 makanan dikumpulkan dengan 300 pekerja.

VERSI 2.1

Ditambahkan cek kewarasan untuk memastikan bahwa ratu tidak mencoba untuk membuat pekerja ketika tidak memiliki makanan.

VERSI 2.1.1

memperbaiki perbaikan di 2.1 (Saya tidak benar-benar menggunakan bidang "makanan" dari pandangan semut dan karena itu merujuk objek nol.

VERSI 2.1.1.1

/ Aku menyembunyikan kepala di tangan

Saya menemukan bug pengindeksan sederhana dalam fungsi pemijahan yang berarti bahwa ratu dapat mencoba untuk muncul di atas dirinya ... tidak perlu dikatakan bahwa itu adalah alasan untuk didiskualifikasi. sekarang diperbaiki.

VERSI 2.2

Memperbaiki kesalahan tempel salinan yang memperkenalkan kembali bug spawn ilegal

VERSI 2.2.1

Memperbaiki kemungkinan tindakan ilegal di permulaan ketika ratu berusaha menemukan makanan pertama.


Strategi Anda juga mengalami kelemahan yang sama seperti Zigurrat: pencurian. Saya dapat mengubah kode identifikasi Zigurrat di Vampir untuk menargetkan Firefly dalam waktu sekitar 90 detik. Ini benar-benar akan menjadi "salin 60 baris kode, tempelkan 60 baris kode, ubah 3 angka." Meskipun, saya lebih suka membuat kode Zigurrat-ID modular sebagai gantinya ("lakukan ini, dengan warna-warna ini").
Draco18s

Semut Anda juga banyak menumpuk: jika seseorang melukis, seseorang akan muncul di belakangnya dan menunggu sampai kombinasi pemicu yang tepat membuat pelayan menunggu keluar dan memulai baris baru. Mendeteksi bahwa ada semut yang ramah di depan dan muncul untuk memulai garis baru seharusnya mudah. Seharusnya tidak perlu menunggu sudut untuk melakukannya. Masalah kedua adalah ketika dua semut pada garis yang terpisah berada pada langkah undian yang sama. Yang di luar menggambar sudut, yang di dalam menggambarnya kembali. Seharusnya bisa mendeteksi semut tetangga dalam situasi ini juga (tindakan terbaik mungkin menunggu semut lainnya bergerak).
Draco18s

@ Draco18s Ya, ada beberapa kekurangan dengan versi awal ini :-) tetapi ini adalah tempat untuk memulai. Mengenai ancaman pencurian: saya berencana membuat semut penjaga yang akan mencari ratu pencuri musuh dan "mencuri" kembali. Itu harus meminimalkan kerugian.
Moogie

Oh, saya sadar ini adalah tempat untuk memulai, hanya melakukan beberapa pengamatan. :)
Draco18s

1
Apakah sudah selesai? Bisakah saya masuk dan menghancurkan tempat itu dengan kelelawar vampir? : D
Draco18s

4

Mandelbrant

Semua jawaban saya mengandung logika tingkat rendah yang serupa dalam bentuk Kerangka Fungsi Formik.

"TINGKAT LOGIKA TINGGI MULAI DI SINI" menandai akhir kode Kerangka ini.

PERINGATAN: Entri ini sebagian besar demo dari apa yang mungkin dalam set aturan Fungsi Formic. Itu tidak diuji sepenuhnya pada peta yang tidak kosong. Meskipun berhasil melewati dua pertandingan penuh, saya tidak berharap untuk tetap berkualitas untuk waktu yang lama.

// FORMIC FRAMEWORK \\
//  Version 7.0.4   \\
const QUEEN = 5;
const HERE = view[4];
const ME = HERE.ant;
const ORTHOGONALS = [1, 3, 5, 7];
const DIAGONALS = [0, 2, 6, 8];
const DIAGONALS_ORTHOGONALS = [0, 2, 6, 8, 1, 3, 5, 7];
const DIRECTIONS = [0, 1, 2, 3, 5, 6, 7, 8];
const CLOCKWISE_DIRECTIONS = [0, 1, 2, 5, 8, 7, 6, 3];
const ROTATIONS = [
    [0, 1, 2,
     3, 4, 5,
     6, 7, 8],

    [6, 3, 0,
     7, 4, 1,
     8, 5, 2],

    [8, 7, 6,
     5, 4, 3,
     2, 1, 0],

    [2, 5, 8,
     1, 4, 7,
     0, 3, 6]
];
const NEIGHBORS = [
    [1, 4, 3],
    [2, 5, 4, 3, 0],
    [5, 4, 1],
    [0, 1, 4, 7, 6],
    [0, 1, 2, 5, 8, 7, 6, 3],
    [8, 7, 4, 1, 2],
    [3, 4, 7],
    [6, 3, 4, 5, 8],
    [7, 4, 5]
];
const HORIZONTAL_FLIP = [2, 1, 0, 5, 4, 3, 8, 7, 6];
const VERTICAL_FLIP = [6, 7, 8, 3, 4, 5, 0, 1, 2];

const DEBUG_MODE = false;
const log = DEBUG_MODE ? console.log : () => { };

function cells(...indices) {
    return indices.map(i => view[i]);
}
function colors(...indices) {
    return cells(...indices).map(c => c.color);
}
function ants(...indices) {
    return cells(...indices).map(c => c.ant);
}

function isColor(color, index) {
    return view[index].color === color;
}
function isAnyColor(colors, index) {
    return colors.includes(view[index].color);
}
function hasFood(index) {
    return view[index].food === 1;
}
function hasAnt(qualifies, index) {
    const a = view[index].ant;
    return a && (!(qualifies instanceof Function) || qualifies(a));
}
function hasFriend(type, index) {
    return hasAnt(a => a.friend && (!type || a.type === type), index);
}
const hasAnyFriend = bind(hasFriend, null);
function bind(f, ...args) {
    return f.bind(null, ...args);
}

function noTransform() {
    return { revert() { }, detransformAction() { } };
}
function indexTransform(indices) {
    const revertedIndices = new Array(9);
    for (let i = 0; i < 9; ++i) {
        revertedIndices[indices[i]] = i;
    }

    view = indices.map(index => view[index]);

    return { revert() { view = revertedIndices.map(index => view[index]); }, detransformAction(action) { action.cell = indices[action.cell]; } };
}

const rotationTransformers = [noTransform, ...ROTATIONS.slice(1).map(r => bind(indexTransform, r))];

function bestTransformers(transformers, scorer) {
    let bestScore = 0;
    const bestIndices = [];
    const bestTransformers = [];
    for (let i = 0; i < transformers.length; ++i) {
        const t = transformers[i];
        const {revert} = t();
        const score = scorer();
        revert();
        if (score > bestScore) {
            bestScore = score;
            bestIndices.length = 0;
            bestTransformers.length = 0;
        }
        if (score >= bestScore) {
            bestIndices.push(i);
            bestTransformers.push(t);
        }
    }

    return {score: bestScore, indices: bestIndices, transformers: bestTransformers};
}
function* withBestTransformation(transformers, scorer, continuation) {
    const best = bestTransformers(transformers, scorer);
    if (best.score > 0) {
        const {revert, detransformAction} = best.transformers[0]();
        for (const output of continuation(best)) {
            if (isAction(output)) {
                detransformAction(output);
            }
            yield output;
        }
        revert();
    }
}
const withBestRotation = bind(withBestTransformation, rotationTransformers);

const wait = {cell: 4};
function move(index) {
    return index >= 0 && index < 9 && view[index].ant === null && (view[index].food === 0 || ME.food === 0 || ME.type === QUEEN) ? { cell: index } : null;
}
function moveMany(...indices) {
    return indices.map(move);
}
function paint(color, index) {
    return index >= 0 && index < 9 && color >= 1 && color <= 8 && view[index].color !== color ? { cell: index, color } : null;
}
function paintMany(colors, ...indices) {
    return pairMap(indices, colors, paint);
}
function spawn(type, index) {
    return index >= 0 && index < 9 && view[index].ant === null && view[index].food === 0 && ME.food > 0 && ME.type === QUEEN && type >= 1 && type <= 4 ? { cell: index, type } : null;
}
function spawnMany(types, ...indices) {
    return pairMap(indices, types, spawn);
}
function pairMap(mainArr, sideArr, func) {
    return mainArr.map((v, i) => func(sideArr[i % sideArr.length], v));
}

function isAction(value) {
    return value instanceof Object && value.cell !== undefined; // TODO: Make this more strict.
}

log('=== start logic ===');
for (const output of main()) {
    if (isAction(output)) {
        log('=== end logic ===');
        return output;
    }
}

throw 'Decision was omitted.';

function* main() {
    // HIGH-LEVEL LOGIC STARTS HERE \\

    // TARGET SIZE:  2^21 pixels -- SUPPORTED
    // STRETCH GOAL: 2497 x 996
    // MAX POSSIBLE: 2500 x 1000

    // How long the painting triplet will go on for until they begin returning to the shifting station.
    // This value should not exceed 997 for the painter to work in all cases, or 2497 if you don't care about being positioned vertically.
    // It also shouldn't be too low. The exact lowest value is unclear, but it's likely to be in the teens.
    const LENGTH = 6 * 11;

    // Which function will be used for painting in the pixels.
    const getPictureColorAt = mandelbrot;

    function notReallyRainbow(index) {
        return index % 6 + 2;
    }
    function fromColorString(index) {
        // Input your own color string ({ a, b, c, d, e, f, g, h } => { 8, 7, 6, 4, 5, 3, 2, 1 }).
        const colorString = '';

        return [8, 7, 6, 4, 5, 3, 2, 1][colorString.charCodeAt(index % colorString.length) - 'a'.charCodeAt(0)];
    }
    function mandelbrot(index) {
        const ESCAPE = 2 ** 2, MAX_I = 8 * 10 - 1;
        const x0 = (index % LENGTH) / (LENGTH - 1) * 3 - 2, y0 = Math.floor(index / LENGTH) / (Math.floor(LENGTH * 2 / 3) - 1) * 2 - 1;
        let x = 0, y = 0;
        for (let i = 0; i < MAX_I; ++i) {
            [x, y] = [x * x - y * y + x0, 2 * x * y + y0];
            if (x * x + y * y > ESCAPE) {
                return (i + 1) % 8 + 1;
            }
        }
        return 8;
    }

    // WARNING! Beyond likely lies awful code.
    // There are no more tunable parameters.
    // Continue reading at your own risk.

    const L1_OVERFLOW = 4096;
    const FILL_ORDER_INDEX = [1, 2, 3, 6];
    const FILL_ORDER_DIGIT = [0, 1, 2, 4];

    function parseNumber(...indices) {
        return indices.reduceRight((a, index) => (a << 3) + (index !== -1 ? view[index].color - 1 : 0), 0);
    }

    function colorAtDigit(n, d) {
        return ((n >>> (d * 3)) & 7) + 1;
    }

    function paintPictureFragment(number) {
        log(`initialized painter with ${number}`);
        return paint(getPictureColorAt(number), 0);
    }

    const COPIER = 1;
    const COUNTER = 2;
    const MAJOR = 3;
    const MINOR = 4;

    function* moveWait(index) {
        yield move(index);
        yield wait;
    }

    log(`type: ${ME.type}`);
    switch (ME.type) {
        case COPIER: {
            yield* withBestRotation(() => Math.max(hasFriend(QUEEN, 7) + hasFriend(MINOR, 3), hasFriend(QUEEN, 6) + hasFriend(MINOR, 0)) - 1, bind(moveWait, 5));
            yield* withBestRotation(bind(hasFriend, COUNTER, 7), function*() {
                if ([6, 3].findIndex(hasAnyFriend) === -1) {
                    yield paint(8, 3);
                    yield move(3);
                }
                yield wait;
            });
            yield* withBestRotation(bind(hasFriend, COUNTER, 8), function*() {
                const targetIndex = FILL_ORDER_INDEX[view[4].color - 3];
                yield paint(view[5].color, targetIndex);
                yield wait;
            });
            yield wait;
        }

        case COUNTER: {
            yield* withBestRotation(() => hasFriend(COPIER, 2) + hasFriend(MAJOR, 0) - 1, bind(moveWait, 5));
            yield* withBestRotation(bind(hasFriend, COPIER, 1), function*() {
                if (hasFriend(MAJOR, 0)) {
                    yield paint(view[6].color, 8);
                }
                yield wait;
            });
            yield* withBestRotation(bind(hasFriend, COPIER, 0), function*() {
                if (!hasAnyFriend(6)) {
                    const progress = view[0].color;
                    if (progress === 8) {
                        const number = parseNumber(8, 7, 4, 1) + 1;
                        yield* paintMany([1, 2, 3].map(bind(colorAtDigit, number)), 3, 5, 2);
                        yield paint(7, 0);
                    } else if (progress === 7) {
                        const number = parseNumber(8) + 1;
                        yield paint(colorAtDigit(number, 0), 4);
                        yield paint(6, 0);
                    } else {
                        const number = parseNumber(...progress === 6 ? [4, 3] : [7, 8], 5, 2) * LENGTH;
                        if (progress > 2) {
                            if (progress === 6) {
                                yield* paintMany(colors(4, 3), 7, 8);
                            } else if (progress === 5) {
                                yield* paintMany([5, 6].map(bind(colorAtDigit, number)), 3, 4);
                            }
                            yield paint(colorAtDigit(number, FILL_ORDER_DIGIT[progress - 3]), 1);
                            if (progress !== 3) {
                                yield paint(progress - 1, 0);
                            } else {
                                yield paint(number + 1 === L1_OVERFLOW ? 2 : 1, 0);
                            }
                        } else {
                            yield paint(colorAtDigit(number, 3), 1);
                            yield wait;
                        }
                    }
                }
            });
            yield wait;
        }

        case MAJOR: {
            yield* withBestRotation(bind(hasFriend, COPIER, 5), bind(moveWait, 6));
            yield* withBestRotation(() => hasFriend(QUEEN, 7) + hasFriend(MINOR, 1) - 1, bind(moveWait, 5));
            yield* withBestRotation(bind(hasFriend, QUEEN, 2), bind(moveWait, 1));
            yield* withBestRotation(bind(hasFriend, MINOR, 2), function*() {
                const number = parseNumber(-1, -1, -1, -1, 3, 4, 5) + (isColor(2, 1) ? L1_OVERFLOW : 0);
                yield* paintMany([4, 5, 6].map(bind(colorAtDigit, number)), 6, 7, 8);
                yield move(7);
            });
            yield wait;
        }

        case MINOR: {
            yield* withBestRotation(() => hasFriend(COPIER, 8) + hasFriend(QUEEN, 6) - hasFriend(MAJOR, 3) - 1, bind(moveWait, 7));
            yield* withBestRotation(() => hasFriend(MAJOR, 6) + hasFriend(QUEEN, 7) - 1, bind(moveWait, 3));
            yield* withBestRotation(() => hasFriend(MAJOR, 8) - hasFriend(QUEEN, 7), bind(moveWait, 5));
            yield* withBestRotation(() => hasFriend(MAJOR, 7) + hasFriend(QUEEN, 5) - 1, bind(moveWait, 1));
            yield* withBestRotation(bind(hasFriend, QUEEN, 6), function*() {
                if (hasFriend(MAJOR, 3)) {
                    yield wait;
                }
                const number = parseNumber(0, 1) + 1;
                yield* paintMany([0, 1].map(bind(colorAtDigit, number)), 3, 4);
                yield move(7);
            });
            yield wait;
        }

        case QUEEN: {
            if (DIRECTIONS.some(hasAnyFriend)) {
                yield* withBestRotation(bind(hasFriend, COPIER, 5), function*() {
                    if (!hasFriend(COUNTER, 7)) {
                        yield spawn(COUNTER, 8);
                    }
                    if (!hasFriend(MAJOR, 3) && !hasFriend(MAJOR, 0)) {
                        yield spawn(MAJOR, 6);
                    }
                    yield spawn(MINOR, 0);
                    if (!hasFriend(MAJOR, 0) && hasFriend(MAJOR, 3)) {
                        yield move(7);
                    }
                    yield wait;
                });

                yield* withBestRotation(bind(hasFriend, MAJOR, 2), function*() {
                    if (!hasFriend(MINOR, 8)) {
                        yield move(1);
                    }
                    yield wait;
                });

                yield* withBestRotation(bind(hasFriend, MINOR, 0), function*() {
                    if (hasFriend(MAJOR, 3)) {
                        yield move(1);
                        yield wait;
                    } else if (hasFriend(MAJOR, 6)) {
                        yield wait;
                    }
                });

                yield* withBestRotation(bind(hasFriend, MAJOR, 7), function*() {
                    if (!hasFriend(COPIER, 6)) {
                        yield paintPictureFragment(parseNumber(1, 2, 3, 5, 6, 7, 8));
                    }
                    yield wait;
                });

                yield* withBestRotation(bind(hasFriend, MINOR, 5), function*() {
                    if (isColor(3, 4)) {
                        const number = parseNumber(1, 2, 3, 5) + 1;
                        yield* paintMany([colorAtDigit(number, 2), number + 1 === L1_OVERFLOW ? 2 : 1, colorAtDigit(number, 3)], 6, 7, 8);
                        yield move(7);
                        yield wait;
                    } else {
                        const number = parseNumber(1, 2, 3, 5, 6, 7, 8);
                        yield paintPictureFragment(number);
                        if ((number + 1) % LENGTH === 0) {
                            yield move(8);
                            yield wait;
                        }
                        yield paint(3, 4);
                    }

                    throw 'illogical failure 1';
                });

                throw 'illogical failure 2';
            }

            yield* moveMany(...DIAGONALS_ORTHOGONALS.filter(hasFood));

            if (ME.food >= 4) {
                yield* spawnMany([COPIER], ...ORTHOGONALS);
            }

            yield* moveMany(...DIAGONALS_ORTHOGONALS.filter(bind(isColor, 1)), ...DIAGONALS_ORTHOGONALS); // TODO: Watch out for accidental entrapment.
            yield wait;
        }
    }
}

Galeri

Mandelbrot penuh Mandelbrot mungil


Penjelasan

Saya akan membuat penjelasannya cukup singkat, tetapi perlu diingat bahwa ada banyak detail buruk yang harus saya pecahkan saat membuat ini yang tidak akan saya bahas.

Fase 1

Pertama, ratu mengumpulkan 4 makanan untuk membuat 4 pekerja, masing-masing melayani tujuan yang berbeda. Dia tidak meninggalkan warna apapun untuk meminimalkan kemungkinan merusak lukisan itu. Anda akan melihat mengapa itu tidak lama.

Fase 2

Setelah melahirkan 4 pekerja, loop inti dari entri segera dimulai. Mulai sekarang saya akan mengasumsikan orientasi tampilan yang konsisten, karena entri melakukan itu juga.

Pertama tarian 5-semut terkoordinasi dilakukan untuk mencapai orientasi yang benar untuk melukis. Ini mungkin fase yang paling tidak stabil, dan kemungkinan entri akan didiskualifikasi karena intervensi musuh di sini. Setelah itu semut berpisah.

3 semut (ratu dan 2 pekerja) masuk ke lingkaran lukisan. Mereka membawa integer 21-bit (7 sel * 3 bit per sel) dalam bentuk warna, yang dapat digunakan untuk mengindeks ke dalam gambar yang diinginkan. Secara default, gambar ini adalah himpunan Mandelbrot. Selain itu, sel kiri atas dicadangkan untuk piksel yang dicat, dan sel pusat dicadangkan untuk hal-hal yang berada di luar ruang lingkup penjelasan ini. Triplet tidak membutuhkan warna untuk memandu mereka, karena mereka mencari tahu orientasi dengan menemukan satu sama lain. Setiap siklus mereka menggeser bilangan bulat dengan 1 sel ke bawah, memastikan untuk menambahnya 1 setiap kali mereka melakukannya. Loop berakhir ketika ujung garis yang dilukis tercapai, yang dikonfigurasikan olehLENGTHkonstan. Pada saat itu, tarian 3-semut terkoordinasi dimulai dan menghasilkan trio berakhir dalam konfigurasi kembali canggung. Sang ratu juga bergeser ke kanan sel selama tarian. Mereka melakukan perjalanan ke atas sampai mereka bertemu dengan semut yang menunggu mereka.

Sambil menunggu ratu dan pembantunya kembali, kata sepasang semut mengatur lingkungan warna setempat sehingga para pelukis dapat terus bekerja setelah kedatangan mereka. Ini adalah bagian yang paling sulit untuk diketahui. Satu semut memelihara integer 4-warna (12-bit), yang digunakannya untuk memberikan instruksi kepada semut lainnya. Instruksi-instruksi tersebut menggambarkan sel mana yang harus dicat olehnya dan untuk warna apa. Ini diperlukan karena seekor semut tidak memiliki ruang yang cukup dalam pandangannya untuk menyimpan semua informasi yang diperlukan untuk melakukan pewarnaan dengan sendirinya. Sebelum dan selama proses ini, juga perlu bagi integer maintainer untuk memindahkan angka 1 ke kanan, dan juga menambahkannya 1. Setelah relay instruksi selesai, integer maintainer mengisi sel-sel yang memiliki akses ke dan menyelesaikan pengaturan lingkungan. Hibernasi dimulai - pasangan idle,

Siklus selesai ketika ketiga semut kembali ke pasangan dan mulai melakukan tarian 5-semut lagi.


Entri ini menampilkan parameter. Saat ini disetel untuk menggambar set Mandelbrot kecil. Anda dapat menyesuaikan LENGTHgambar, menukar fungsi melukis gambar atau bahkan menggulirkan gambar Anda sendiri. Selamat bersenang-senang!

Pengontrol yang disarankan: dzaima's .


Changelog

Versi 1.0

  • Peluncuran pertama

Wow. Saya secara khusus merancang tantangan ini untuk membatasi dan informasi yang rendah, dan saya masih kagum dengan seberapa banyak ini dapat diselesaikan dengan kerja sama antara semut.
trichoplax

Saya menghargai ini adalah bukti konsep daripada entri kompetitif, tetapi juga membuat saya bertanya-tanya apakah itu bisa memiliki aplikasi praktis sebagai perangkap ratu, dengan pola yang cocok untuk mengarahkan ratu lain agar makanan mereka dicuri.
trichoplax

1
Setelah menjalankan beberapa permainan untuk melihat bagaimana kinerjanya melawan semua pemain lain, ada baiknya menunjukkan bahwa meskipun sebagai bukti konsep ini tidak mendapat skor terendah - lebih baik daripada beberapa pemain lainnya. Setelah turnamen penuh dijalankan, turnamen ini tidak akan menjadi yang terakhir.
trichoplax

1
Mengapa? MENGAPA? Mengapa kamu melakukan ini? Crikey, tantangan ini sudah diposting jawaban sementara saya sudah di tiga pekerjaan yang berbeda.
Draco18s

1
@ Draco18s Saya terlalu suka tantangan ini: P
Alion

3

Lone Wolf

Semua jawaban saya membagikan fungsi pembantu tingkat rendah yang sama. Cari "Logika tingkat tinggi dimulai di sini" untuk melihat kode spesifik untuk jawaban ini.

// == Shared low-level helpers for all solutions ==

var QUEEN = 5;

var WHITE = 1;
var COL_MIN = WHITE;
var COL_LIM = 9;

var CENTRE = 4;

var NOP = {cell: CENTRE};

var DIR_FORWARDS = false;
var DIR_REVERSE = true;
var SIDE_RIGHT = true;
var SIDE_LEFT = false;

function sanity_check(movement) {
  var me = view[CENTRE].ant;
  if(!movement || movement.cell < 0 || movement.cell > 8) {
    return false;
  }
  if(movement.type) {
    if(movement.color) {
      return false;
    }
    if(movement.type < 1 || movement.type > 4) {
      return false;
    }
    if(view[movement.cell].ant || view[movement.cell].food) {
      return false;
    }
    if(me.type !== QUEEN || me.food < 1) {
      return false;
    }
    return true;
  }
  if(movement.color) {
    if(movement.color < COL_MIN || movement.color >= COL_LIM) {
      return false;
    }
    if(view[movement.cell].color === movement.color) {
      return false;
    }
    return true;
  }
  if(view[movement.cell].ant) {
    return false;
  }
  if(view[movement.cell].food + me.food > 1 && me.type !== QUEEN) {
    return false;
  }
  return true;
}

function as_array(o) {
  if(Array.isArray(o)) {
    return o;
  }
  return [o];
}

function best_of(movements) {
  var m;
  for(var i = 0; i < movements.length; ++ i) {
    if(typeof(movements[i]) === 'function') {
      m = movements[i]();
    } else {
      m = movements[i];
    }
    if(sanity_check(m)) {
      return m;
    }
  }
  return null;
}

function play_safe(movement) {
  // Avoid disqualification: no-op if moves are invalid
  return best_of(as_array(movement)) || NOP;
}

var RAND_SEED = (() => {
  var s = 0;
  for(var i = 0; i < 9; ++ i) {
    s += view[i].color * (i + 1);
    s += view[i].ant ? i * i : 0;
    s += view[i].food ? i * i * i : 0;
  }
  return s % 29;
})();

var ROTATIONS = [
  [0, 1, 2, 3, 4, 5, 6, 7, 8],
  [6, 3, 0, 7, 4, 1, 8, 5, 2],
  [8, 7, 6, 5, 4, 3, 2, 1, 0],
  [2, 5, 8, 1, 4, 7, 0, 3, 6],
];

function try_all(fns, limit, wrapperFn, checkFn) {
  var m;
  fns = as_array(fns);
  for(var i = 0; i < fns.length; ++ i) {
    if(typeof(fns[i]) !== 'function') {
      if(checkFn(m = fns[i])) {
        return m;
      }
      continue;
    }
    for(var j = 0; j < limit; ++ j) {
      if(checkFn(m = wrapperFn(fns[i], j))) {
        return m;
      }
    }
  }
  return null;
}

function identify_rotation(testFns) {
  // testFns MUST be functions, not constants
  return try_all(
    testFns,
    4,
    (fn, r) => fn(ROTATIONS[r]) ? ROTATIONS[r] : null,
    (r) => r
  );
}

function near(a, b) {
  return (
    Math.abs(a % 3 - b % 3) < 2 &&
    Math.abs(Math.floor(a / 3) - Math.floor(b / 3)) < 2
  );
}

function try_all_angles(solverFns) {
  return try_all(
    solverFns,
    4,
    (fn, r) => fn(ROTATIONS[r]),
    sanity_check
  );
}

function try_all_cells(solverFns, skipCentre) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i === CENTRE && skipCentre) ? null : fn(i)),
    sanity_check
  );
}

function try_all_cells_near(p, solverFns) {
  return try_all(
    solverFns,
    9,
    (fn, i) => ((i !== p && near(p, i)) ? fn(i) : null),
    sanity_check
  );
}

function ant_type_at(i, friend) {
  return (view[i].ant && view[i].ant.friend === friend) ? view[i].ant.type : 0;
}

function friend_at(i) {
  return ant_type_at(i, true);
}

function foe_at(i) {
  return ant_type_at(i, false);
}

function foe_near(p) {
  for(var i = 0; i < 9; ++ i) {
    if(foe_at(i) && near(i, p)) {
      return true;
    }
  }
  return false;
}

function move_agent(agents) {
  var me = view[CENTRE].ant;
  var buddies = [0, 0, 0, 0, 0, 0];
  for(var i = 0; i < 9; ++ i) {
    ++ buddies[friend_at(i)];
  }

  for(var i = 0; i < agents.length; i += 2) {
    if(agents[i] === me.type) {
      return agents[i+1](me, buddies);
    }
  }
  return null;
}

function grab_nearby_food() {
  return try_all_cells((i) => (view[i].food ? {cell: i} : null), true);
}

function go_anywhere() {
  return try_all_cells((i) => ({cell: i}), true);
}

function colours_excluding(cols) {
  var r = [];
  for(var i = COL_MIN; i < COL_LIM; ++ i) {
    if(cols.indexOf(i) === -1) {
      r.push(i);
    }
  }
  return r;
}

function generate_band(start, width) {
  var r = [];
  for(var i = 0; i < width; ++ i) {
    r.push(start + i);
  }
  return r;
}

function colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function(c) {
      return colours[(colours.indexOf(c) + 1) % colours.length];
    }
  };
}

function random_colour_band(colours) {
  return {
    contains: function(c) {
      return colours.indexOf(c) !== -1;
    },
    next: function() {
      return colours[RAND_SEED % colours.length];
    }
  };
}

function fast_diagonal(colourBand) {
  var m = try_all_angles([
    // Avoid nearby checked areas
    (rot) => {
      if(
        !colourBand.contains(view[rot[0]].color) &&
        colourBand.contains(view[rot[5]].color) &&
        colourBand.contains(view[rot[7]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // Go in a straight diagonal line if possible
    (rot) => {
      if(
        !colourBand.contains(view[rot[0]].color) &&
        colourBand.contains(view[rot[8]].color)
      ) {
        return {cell: rot[0]};
      }
    },

    // When in doubt, pick randomly but avoid doubling-back
    (rot) => (colourBand.contains(view[rot[0]].color) ? null : {cell: rot[0]}),

    // Double-back when absolutely necessary
    (rot) => ({cell: rot[0]})
  ]);

  // Lay a colour track so that we can avoid doubling-back
  // (and mess up our foes as much as possible)
  if(!colourBand.contains(view[CENTRE].color)) {
    var prevCol = m ? view[8-m.cell].color : WHITE;
    return {cell: CENTRE, color: colourBand.next(prevCol)};
  }

  return m;
}

function follow_edge(obstacleFn, side) {
  // Since we don't know which direction we came from, this can cause us to get
  // stuck on islands, but the random orientation helps to ensure we don't get
  // stuck forever.

  var order = ((side === SIDE_LEFT)
    ? [0, 3, 6, 7, 8, 5, 2, 1, 0]
    : [0, 1, 2, 5, 8, 7, 6, 3, 0]
  );
  return try_all(
    [obstacleFn],
    order.length - 1,
    (fn, i) => (fn(order[i+1]) && !fn(order[i])) ? {cell: order[i]} : null,
    sanity_check
  );
}

function start_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => ((
      !protectedCols.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 5 : 3]].color) &&
      !colourBand.contains(view[rot[right ? 2 : 0]].color) &&
      !colourBand.contains(view[rot[1]].color)
    )
      ? {cell: rot[right ? 5 : 3], color: colourBand.next(WHITE)}
      : null)
  ]);
}

function lay_dotted_path(colourBand, side, protectedCols) {
  var right = (side === SIDE_RIGHT);
  return try_all_angles([
    (rot) => {
      var ahead = rot[right ? 2 : 0];
      var behind = rot[right ? 8 : 6];
      if(
        colourBand.contains(view[behind].color) &&
        !protectedCols.contains(view[ahead].color) &&
        !colourBand.contains(view[ahead].color) &&
        !colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        return {cell: ahead, color: colourBand.next(view[behind].color)};
      }
    }
  ]);
}

function follow_dotted_path(colourBand, side, direction) {
  var forwards = (direction === DIR_REVERSE) ? 7 : 1;
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    // Cell on our side? advance
    (rot) => {
      if(
        colourBand.contains(view[rot[right ? 5 : 3]].color) &&
        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[0]].color) &&
        !colourBand.contains(view[rot[2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    },

    // Cell ahead and behind? advance
    (rot) => {
      var passedCol = view[rot[right ? 8 : 6]].color;
      var nextCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(passedCol) &&
        nextCol === colourBand.next(passedCol) &&

        // Prevent sticking / trickery
        !colourBand.contains(view[rot[right ? 3 : 5]].color) &&
        !colourBand.contains(view[rot[right ? 0 : 2]].color)
      ) {
        return {cell: rot[forwards]};
      }
    }
  ]);
}

function escape_dotted_path(colourBand, side, newColourBand) {
  var right = (side === SIDE_RIGHT);
  if(!newColourBand) {
    newColourBand = colourBand;
  }

  return try_all_angles([
    // Escape from beside the line
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        !colourBand.contains(view[rot[right ? 8 : 6]].color) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[rot[7]].color) ||
        colourBand.contains(view[rot[right ? 6 : 8]].color)
      ) {
        // not oriented, or in a corner
        return null;
      }
      return best_of([
        {cell: rot[right ? 0 : 2], color: newColourBand.next(approachingCol)},
        {cell: rot[right ? 3 : 5]},
        {cell: rot[right ? 0 : 2]},
        {cell: rot[right ? 6 : 8]},
        {cell: rot[right ? 2 : 0]},
        {cell: rot[right ? 8 : 6]},
        {cell: rot[right ? 5 : 3]}
      ]);
    },

    // Escape from inside the line
    (rot) => {
      if(
        !colourBand.contains(view[rot[7]].color) ||
        !colourBand.contains(view[rot[1]].color) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      return best_of([
        {cell: rot[3]},
        {cell: rot[5]},
        {cell: rot[0]},
        {cell: rot[2]},
        {cell: rot[6]},
        {cell: rot[8]}
      ]);
    }
  ]);
}

function latch_to_dotted_path(colourBand, side) {
  var right = (side === SIDE_RIGHT);

  return try_all_angles([
    (rot) => {
      var approachingCol = view[rot[right ? 2 : 0]].color;
      if(
        colourBand.contains(approachingCol) &&
        view[rot[right ? 8 : 6]].color === colourBand.next(approachingCol) &&
        !colourBand.contains(view[rot[right ? 5 : 3]].color)
      ) {
        // We're on the wrong side; go inside the line
        return {cell: rot[right ? 5 : 3]};
      }
    },

    // Inside the line? pick a side
    (rot) => {
      var passedCol = view[rot[7]].color;
      var approachingCol = view[rot[1]].color;
      if(
        !colourBand.contains(passedCol) ||
        !colourBand.contains(approachingCol) ||
        colourBand.contains(view[CENTRE].color)
      ) {
        return null;
      }
      if((approachingCol === colourBand.next(passedCol)) === right) {
        return best_of([{cell: rot[3]}, {cell: rot[6]}, {cell: rot[0]}]);
      } else {
        return best_of([{cell: rot[5]}, {cell: rot[2]}, {cell: rot[8]}]);
      }
    }
  ]);
}


// == High-level logic begins here ==


var COLOURS = random_colour_band(colours_excluding([1]));
return play_safe([
  grab_nearby_food,
  fast_diagonal.bind(null, COLOURS),
  go_anywhere,
  {cell: 1, color: COLOURS.next()}
]);

Jadi terlintas dalam benak saya bahwa dalam beberapa jawaban saya, saya menggunakan langkah awal yang sama untuk mengumpulkan makanan awal dengan cepat. Apa yang terjadi jika seekor semut menggunakan strategi itu untuk seluruh permainan? Yah ternyata mereka melakukannya dengan cukup baik.

Ini hanya berlomba di sekitar lapangan dengan kecepatan setengah dari cahaya, meraih makanan di dekatnya. Ada kemungkinan arah akan berubah secara acak saat mengambil makanan, dan itu akan mencoba untuk menghindari area yang sudah tertutup dengan sendirinya atau orang lain. Tidak ada semut pekerja yang pernah dihasilkan.

Secara teori, ini mencakup 2,5 sel per frame, yang tidak mengerikan, dan itu cukup mustahil untuk menjebaknya atau mengacaukannya dengan cara apa pun. Tampaknya melakukan lebih baik daripada segalanya kecuali Black Hole (meskipun itu dapat berubah sekarang bahwa Black Hole memiliki penyabot).

Saya berjanji untuk menghentikan spamming tantangan ini dengan jawaban sekarang ...


Pembaruan terbaru membuatnya lebih kecil kemungkinannya untuk mencari kembali tanah tertutup dengan membuatnya menjauh dari lebih banyak jenis area yang diisi.


Ini bukan spam ketika semuanya merupakan strategi yang berbeda dan menarik!
trichoplax

Entri mana yang merupakan penyabot Black Hole?
Draco18s

@ Draco18s tidak yakin yang mana itu, tetapi dengan cepat menarik garis-garis biru diagonal yang sering membuat Black Hole berhenti memunculkan pekerja lebih awal dari yang diinginkan, dan menyebabkannya menjadi super memanjang, membuat semua pekerja membutuhkan waktu lama untuk pergi ke mana pun. Ada juga rudal putih-cat-yang menyebabkan celaka bagi semua, yang benar-benar dapat mengamuk jika mendapatkan tangannya di lubang hitam.
Dave

@Dave Bot yang melakukan itu adalah Antdom Walking Artist
pppery

3

Menembus

Bot ini secara konsisten mendapatkan hampir 90 makanan, mengalahkan sebagian besar bot lainnya.

var ORTHOGONALS = [1,3,5,7];
var CORNERS = [0,2,6,8];
var CENTER = 4;

var QUEEN = 5;

var no_op = {cell:CENTER};
var me = view[4].ant;

var ants;
var food;
var friendlies;
var unfriendlies;
var colors;

var j = 0;
var i = 0;
var cell = 0;
var rotation;

var out;

init_arrays();
var seed = rSeed();

var response;

var adjacents_all = {0:[1,3,4],1:[0,2,3,4,5],2:[1,4,5],3:[0,1,4,6,7],4:[0,1,2,3,4,5,6,7,8],5:[1,2,4,7,8],6:[3,4,7],7:[3,4,5,6,8],8:[4,5,7]};
var adjacents_ortho = {0:[1,3],1:[0,2,4],2:[1,5],3:[0,4,6],4:[1,3,5,7],5:[2,4,8],6:[3,7],7:[4,6,8],8:[5,7]};
var adjacents_diag = {0:[4],1:[3,5],2:[4],3:[1,7],4:[0,2,6,8],5:[1,7],6:[4],7:[3,5],8:[4]};

function valid_move(move) {
  if(!move || move.cell == undefined || move.cell < 0 || move.cell > 8) {return false;}
  if(move.type) {
    if(move.color) {return false;}
    if(move.type < 1 || move.type > 4) {return false;}
    if(view[move.cell].ant || view[move.cell].food) {return false;}
    if(me.type != QUEEN || me.food < 1) {return false;}
    return true;
  }
  if(move.color) {
    if(move.color < 1 || move.color > 8) {return false;}
    return true;
  }
  if(view[move.cell].ant){return false;}
  if(view[move.cell].food && me.food&& me.type != 5) {return false;}
  return true;
}

function steal_then_road(){
  if(count(unfriendlies, 5)>=1 && me.food==0){
    //steal from a queen with more than 30 food
    if(ants[unfriendlies.indexOf(5)].food>30){
      for(i=0;i<adjacents_ortho[unfriendlies.indexOf(5)].length;i++){
        if(ants[adjacents_ortho[unfriendlies.indexOf(5)][i]]==null){
          return {cell:adjacents_ortho[unfriendlies.indexOf(5)][i]};
        }
      }
    }
  }
  return road();
}

function try_corners(){
  for(i=0;i<4;i++){
    if(view[CORNERS[i]].ant==null){
      return {cell:CORNERS[i]};
    }
  }
}

function try_ortho(){
  for(i=0;i<4;i++){
    if(view[ORTHOGONALS[i]].ant==null){
      return {cell:ORTHOGONALS[i]};
    }
  }
}

function corner_then_ortho(){
  if(try_corners()){return try_corners();}
  if(try_ortho()){return try_ortho();}
  return {cell:4}; //PANIC!
}

function ortho_then_corner(){
  if(try_ortho()){return try_ortho();}
  if(try_corners()){return try_corners();}
  return {cell:4}; //PANIC!
}

function road(color){
  if (colors[color] != color) {
      return {cell:CENTER,color:color};
    }
  for (i = 0; i < 9; i++) {
    if (colors[i] == color && ants[8 - i] == null && i != color) {
      return {cell:8-i};
    }
  }
}

function color_self(color){
  return {cell:4,color:color};
}

function make_valid_move(move){
  if(valid_move(move)){return move;}
  return no_op;
  //return{cell:seed%9,color:seed%7+1};
}

function count(array, element) {
  out = 0;
  for (j = 0; j < array.length; j++) {
    if (array[j] == element) {
      out++;
    }
  }
  return out;
}

function target_ant(ant_type, location) {
  for (i = 0; i < 4; i++) {
    if (ants[location] != null) {
      if (ants[location].type == ant_type) {
        return i;
      }
    }
    ants = rot_left(ants);
    friendlies = rot_left(friendlies);
    unfriendlies = rot_left(unfriendlies);
    food = rot_left(food);
    colors = rot_left(colors);
  }
}


function target_color(color, location) {
  for (i = 0; i < 4; i++) {
    if (colors[location] != null) {
      if (colors[location].type == color) {
        return i;
      }
    }
    ants = rot_left(ants);
    friendlies = rot_left(friendlies);
    unfriendlies = rot_left(unfriendlies);
    food = rot_left(food);
    colors = rot_left(colors);
  }
}

function init_arrays() {
    ants = new Array(9);
  for (cell = 0; cell < 9; cell++) {ants[cell] = view[cell].ant;}


  food = new Array(9);
  for (cell = 0; cell < 9; cell++) {food[cell] = view[cell].food;}

  colors = new Array(9);
  for (cell = 0; cell < 9; cell++) {colors[cell] = view[cell].color;}

  friendlies = new Array(9);
  for (cell = 0; cell < 9; cell++) {
    if (ants[cell] != null) {
      if (ants[cell].friend) {friendlies[cell] = ants[cell].type;}
    }
  }

  unfriendlies = new Array(9);
  for (cell = 0; cell < 9; cell++) {
    if (ants[cell] != null) {
      if (!ants[cell].friend) {unfriendlies[cell] = ants[cell].type;}
    }
  }
}

function rot_n_pos(pos, n) {
  for (i = 0; i < n; i++) {
    pos = [2, 5, 8, 1, 4, 7, 0, 3, 6][pos];
  }
  return pos;
}

function rot_left(a) {
  return [a[2], a[5], a[8], a[1], a[4], a[7], a[0], a[3], a[6]];
} 

function rot_right(a) {
  return [a[6], a[3], a[0], a[7], a[4], a[1], a[8], a[5], a[2]];
}

function rSeed(){
  out=23;
  for(i=0;i<9;i++){
    if(food[i]){
      out+=17;
    }
    out += 3 * colors[i];
    if(ants[i]){
      out *= 19;
    }
  }
  return out;
}

function get_response(){
  if (me.type == 5) { //Queen Case:
    return type5();
  }
  else if (me.type == 1) {
    return type1();
  }
  else if (me.type == 2) {
    return type2();
  }
  else if (me.type == 3) {
    return type3();
  }
  else if(me.type == 4){
    return type4();
}

function type5(){
  if (me.food == 0 && count(friendlies, 1) == 0 && count(friendlies, 2) == 0) {
    if (count(food, 1) > 0) {
      for (j = 0; j < 9; j++) {
        if (food[j]) {
          return {cell: j};
        }
      }
    }
    // travel up
    // color own cell if not 4

    if(road()){return road();}

    //move
    for (i = 0; i < 9; i++) {
      if (ants[i] == null && i != 4) {
        return {cell:i};
      }
    }
    return corner_then_ortho();
  }
  if (me.food >= 1 && count(friendlies, 1) == 0 && count(friendlies, 2) == 0) {
    if (ants[5] == null) {
      return {cell:5,type:1};
    }
    if (ants[1] == null) {
      return {cell:5, type:1};
    }
    if (ants[3] == null) {
      return {cell:5,type:1};
    }
    if (ants[7] == null) {
      return {cell:5, type:1};
    }
    return color_self(5);
  }
  if (me.food == 0 && count(friendlies, 1) == 1 && count(friendlies, 2) == 0) {
    if (friendlies.indexOf(1) % 2 == 0) {
      return ortho_then_corner();//PANIC!!! TODO: FIX
    }
    rotation = target_ant(1, 1);
    if (ants[0] == null) {
      return {cell: rot_n_pos(0, rotation)};
    } else {
      return corner_then_ortho;
    }
  }
  if (me.food >= 1 && count(friendlies, 1) == 1 && count(friendlies, 2) == 0) {
    if (friendlies.indexOf(1) % 2 == 0) {
      return corner_then_ortho(); //PANIC!!! TODO: FIX
    }
    rotation = target_ant(1, 1);
    if (ants[3] == null) {
      return {cell: rot_n_pos(3, rotation),type: 2};
    }
    if (ants[0] == null) {
      return { cell: rot_n_pos(0, rotation)};
    }
    return {cell: 4};
  }
  if (count(friendlies, 1) == 1 && count(friendlies, 2) == 1) {
    if (friendlies.indexOf(1) % 2 == 0) {
      return ortho_then_corner();
    }
    rotation = target_ant(1, 1);
    if(food[0] || food[8]){
      return no_op;
    }
    if(ants[5]!=null){
      if(ants[5].type == 2 && ants[2]==null){
        return {cell: rot_n_pos(2, rotation)};
      }
    }
    return corner_then_ortho();
  }
  return corner_then_ortho();
}

function type1(){
//right flank
  if (count(friendlies, 5) == 0 && count(friendlies, 2) == 0 && count(friendlies,1) == 1) {
    //no friends = destruction
    return steal_then_road();
  }
  if (count(friendlies, 5) == 1 && count(friendlies, 2) == 0 && count(friendlies,1) == 1) {
    if (friendlies.indexOf(5) % 2 == 0) {
      return ortho_then_corner();
    }
    rotation = target_ant(5, 3);
    if (ants[0] == null) {
      return {cell: rot_n_pos(0, rotation)};
    }
    return corner_then_ortho(); // PANIC!! TODO: FIX
  }
  if (count(friendlies, 5) == 1 && count(friendlies, 2) == 1 && count(friendlies,1) == 1) {
    if (friendlies.indexOf(5) % 2 == 0) {
      return ortho_then_corner(); 
    }
    rotation = target_ant(5, 3);
    if(friendlies[8] !=null){
      if(friendlies[8].type==2){
        if (ants[0] == null){
          return {cell: rot_n_pos(0, rotation)};
        }
      }
    }
    if (ants[0] != null) {
      if (ants[0].type == 2 && ants[6] == null) {
        return {cell: rot_n_pos(6, rotation)};
      }
    }
    if (ants[6] != null) {
      if (ants[6].type == 2 && ants[0] == null) {
        return {cell: rot_n_pos(0, rotation)};
      }
    }
    return corner_then_ortho();
  }
  return corner_then_ortho();
}

function type2(){
  //left flank
  if (count(friendlies, 5) == 0 && count(friendlies, 1) == 0  && count(friendlies,2) == 1) {
    return steal_then_road();
  }
  if (count(friendlies, 5) == 1 && count(friendlies, 1) == 0  && count(friendlies,2) == 1) {
    if (friendlies.indexOf(5) % 2 == 0) {
      return ortho_then_corner();
    }
    rotation = target_ant(5, 1);
    if (ants[0] == null) {
      return {cell: rot_n_pos(2, rotation)};
    }
    return corner_then_ortho();
    }
    if (count(friendlies, 5) == 1 && count(friendlies, 2) == 1) {
      return {cell: 4,color:2};
    }
    }
  return corner_then_ortho();
}

function type3(){}
function type4(){}

response = get_response();

return make_valid_move(response);

Strategi.

Fase 1: Perebutan Ratu.

Sang ratu berebut makanan menggunakan teknik yang mirip dengan jalan Romanesco (menggunakan road() fungsi). Setiap kali dia melihat makanan dia mengambilnya dan melahirkan pekerja tipe 1, mengaktifkan fase 2.

Fase 2: Perebutan pasangan ratu.

Ratu dan pasangannya saling menggunakan untuk mengarahkan, sehingga mereka bergerak dengan kecepatan cahaya. Mereka mengabaikan makanan di sekitar mereka dan hanya mendapatkan apa yang hits mereka. Ketika sang ratu mendapat makanan, ia melahirkan seorang pekerja tipe 2, mengaktifkan fase 3.

Fase 3: Menembus.

Tiga semut menggunakan satu sama lain untuk menemukan jalan mereka, bepergian dengan kecepatan cahaya. Setiap kali ratu melihat makanan yang tidak bisa didapatkan oleh pekerja, ia berhenti, menyebabkan pekerja berputar di sekelilingnya dan memutar formasi 90 derajat.

Kekuatan:

Formasi tiga-semut tidak menciptakan jejak, tidak membungkus, dan bergerak dengan kecepatan cahaya, mendapatkan rata-rata 0,1% * 3 = 0,003 makanan per gerakan. Ini setara dengan 0,003 * 30000 = 90 makanan per game rata-rata, yang biasanya diperoleh.

Kelemahan:

Bot memiliki dua kelemahan. Yang utama adalah seekor semut yang berjalan di depan formasi. Penanganan untuk itu bukan yang terbaik dan kadang-kadang menyebabkan ratu menciptakan jumlah pekerja yang berlebihan. Untungnya, para pekerja diprogram untuk mencuri dari ratu lain ( steal_then_road()). Tetapi karena formasi tidak menciptakan jejak, praktis tidak mungkin ditemukan.

Kelemahan lain adalah road()algoritma yang tampaknya memiliki masalah yang saya kerjakan untuk memperbaiki. Jalan yang menjadi gerakan Brown tidak baik. Ini berarti awal yang sangat lambat dan pelarian yang buruk dari situasi yang tidak sehat.


Semut-semut ini praktis sama dengan semut giling saya , tetapi tampaknya lebih buruk dalam pengujian saya. Satu-satunya perbedaan yang saya perhatikan adalah bahwa semut Anda berubah ketika ratu melihat makanan yang tidak bisa dijangkau oleh pekerja lain, sedangkan semut saya berubah ketika pekerja tipe 2 akan memakan makanan. Saya pikir semut Anda juga harus berkinerja baik, tetapi saya tidak mengerti mengapa kadang-kadang di bawah berkinerja.
K Zhang

@KZhang Menarik. Pada pengujian saya, Pierce secara umum lebih baik, kecuali ketika itu mengenai gangguan besar semut (Wildfire? Black Hole?) Ketika menciptakan puluhan semut baru yang tidak perlu. Hanya waktu dan pengujian yang akan membuktikan.
fireflame241

Disertakan kembali dalam turnamen saat ini yang siap untuk papan peringkat berikutnya setelah diedit.
trichoplax

2
Saya perhatikan bahwa dalam situasi yang jarang, pola ini bisa macet dalam putaran tanpa akhir. Secara khusus, jika ada dua potong makanan yang dipisahkan oleh 1 ruang, dan kemudian ratu memasuki posisi yang berdekatan dengan keduanya; ketika ini terjadi, polanya berubah secara konstan, mungkin karena itu hanya bereaksi terhadap "makanan yang akan terlewatkan jika kita tidak berbalik" dan bukan "makanan yang akan terlewatkan jika kita berbalik"
Kamil Drakari

3

Queen Single

var C = 5;

for(var i = 0; i<9; i++)
{
  if(view[i].food === 1)
    return {cell:i};
}


if(view[4].color != 5 && !view[0].ant && !view[1].ant && !view[2].ant && !view[3].ant && !view[5].ant && !view[6].ant && !view[7].ant && !view[8].ant)
  return {cell:4, color:C};

if(!view[0].ant && 
   view[0].color != C && view[8].color === C && view[1].color != C && view[3].color != C && view[2].color != C && view[6].color != C)

      return {cell:0};

if(!view[2].ant && 
   view[2].color != C && view[6].color === C && view[1].color != C && view[5].color != C  && view[0].color != C && view[8].color != C)

     return {cell:2};

if(!view[6].ant && 
   view[6].color != C && view[2].color ===  C && view[3].color != C && view[7].color != C  && view[0].color != C && view[8].color != C)

     return {cell:6};

if(!view[8].ant && 
   view[8].color != C && view[0].color === C && view[5].color != C && view[7].color != C  && view[2].color != C && view[6].color != C)

     return {cell:8};


if(!view[0].ant)
  return {cell:0};

if(!view[2].ant)
  return {cell:2};

if(!view[6].ant)
  return {cell:6};

if(!view[8].ant)
  return {cell:8};


return {cell:4};

Kode sederhana mencari makanan secara diagonal. Berusaha menghindari mencari daerah yang lama, tetapi akan mencari daerah lain untuk mencoba dan melewati daerah mereka dengan harapan menemukan ruang terbuka.

Tampaknya strategi yang mirip dengan Lone Wolf (tidak disengaja).


3

Penjelajah

Sebarkan Geng!

Explorer adalah tim 5 orang yang bertujuan untuk menyebar sejauh mungkin, sementara masih membawa makanan kembali ke sang ratu.

Ratu

Sang ratu sendiri menggunakan pengaturan 3 tahap.

Tahap 1.

Pada awal pertandingan, itu adalah makanan liar, seperti ratu yang baik. Itu hanya jalur dalam garis lurus secara diagonal sampai menemukan makanan, kemudian secara acak mengubah arah. Ketika memiliki 4 makanan, ia pindah ke tahap 2.

Tahap 2.

Ini mungkin akan mengambil tidak lebih dari 8 gerakan. Ini mengatur empat ubin di sekitarnya menjadi empat warna unik, dan menumbuhkan semut pada mereka, dengan jenisnya masing-masing. Setelah mereka semua bertelur, tahap 3 dipindahkan ke.

Tahap 3.

Semua tahap 3 tidak duduk diam selama sisa pertandingan, memastikan empat ubin di sekitarnya diatur dengan benar.

Para pekerja

Para pekerja itu sendiri adalah makhluk dua tahap yang sangat sederhana. Pertama, mereka menunggu ratu untuk memberi tanda bahwa dia sudah selesai Tahap 2. Tahap kedua jauh lebih kompleks (Tapi masih cukup sederhana). Ia berjalan searah jarum jam di sekitar jalannya sendiri sampai menemukan ujungnya, lalu terus mengembangkannya. Ketika menemukan makanan, ia menjangkau ke sana, lalu mengorbit searah jarum jam di sekitar jalan lagi, yang membawanya kembali ke ratu.

var me = view[4].ant
var turf = view[4].color

var queenHolder = 2 // "Queen Holder", the turf colour for the queen in stage 3.
var queenBuild = 7 // "Queen Build", the turf colour for the queen in stage 2.
var antTrail = [3, 4, 5, 6] // Various colours of the ant's trails.
var orth = [1, 3, 5, 7] // Orthogonal Directions.
var rotates = [[1,3,5,7],[3,7,1,5],[5,1,7,3],[7,5,3,1]] // These are the orthogonal directions rotated so 0 is the first position, used in the queen build stage.
var outside = [1,2,3,5,6,7,8] // Every tile but the center one.
var diag = [0,2,6,8] // Diagonal Directions.

// Define a move function to avoid throwing an error.
function move(dir){
    if(view[dir].ant)   // If we're going to move onto an ant.
        dir = 4 // Don't move anywhere.
    if(view[dir].food && me.type < 5 && me.food > 0)    // If we're going to over-eat.
        dir = 4 // Don't move anywhere.
    return {cell: dir}  // Build the move output.
}

if(me.type == 5){ // If we're the queen.
    var invDiag = [8,6,2,0] // Inverse of diagonals, using the indexing of diag. So 0 becomes 8, and such.
    if(turf == 1 || turf == 8){
        // Stage 1.
        // Find enough food to start a hive.
        for(var i=0; i < view.length; i++){ // Check every tile in view
            if(view[i].food){   // Is it food?
                return move(i)  // Move to it.
            }
        }
        if(me.food > 3) // Do we have 4 food?
            return {cell:4, color:queenBuild}   // Move to stage 2.
        if(turf == 1)   // Are we on a white tile?
            return {cell:4, color:8}    // Set the tile to black.
        for(var i=0; i < diag.length; i++)  // Check all diagonals.
            if(view[diag[i]].color == 8)    // Is it black?
                return move(invDiag[i]) // Move in the opposite direction. This creates a straight diagonal line.
        return move(2)  // When in doubt, move randomly diagonally.
    }else if(turf == queenBuild){
        // Stage 2.
        // Spawn ants around, and set up their movement paths.
        if(me.food < 1) // Have we used all our food?
            return {cell:4, color:queenHolder}  // Move to stage 3.

        var firstHolder = -1; // Stores which way we're facing.
        for(var i=0; i < orth.length; i++){ // Check orthogonals.
            if(view[orth[i]].color == antTrail[0]){ // Is it the first trail colour?
                firstHolder = i // THIS WAY UP
                break;
            }
        }
        if(firstHolder==-1) // No way is up?
            return {cell:1, color:antTrail[0]} // Set a random direction to up.

        var orthRot = rotates[firstHolder]  // Get the rotated orthogonal set for the current up direction.
        for(var i=0; i < orthRot.length; i++){  // For each of them.
            if(!view[orthRot[i]].ant)   // Is there an ant on this tile yet?
                return {cell:orthRot[i], type:(i+1)}    // If not, place one down with the correct type.
            if(view[orthRot[i]].color!=antTrail[i]) // Otherwise, is the turf set correctly yet?
                return {cell:orthRot[i], color:antTrail[i]} // If not, set the turf.
        }
        return {cell:4, color:queenHolder}; // After all's said and done, move to stage 3. Probably won't happen this way.
    }else if(turf == queenHolder){
        // Stage 3.
        // Sit still, ensure rails exist around.

        var firstHolder = -1;   // Same behavoir of which way is up from stage 2.
        for(var i=0; i < orth.length; i++){
            if(view[orth[i]].color == antTrail[0]){
                firstHolder = i
                break;
            }
        }
        if(firstHolder==-1)
            return {cell:1, color:antTrail[0]}

        var orthRot = rotates[firstHolder]
        for(var i=0; i < orthRot.length; i++)   // Basically stage 2 without the spawning of ants.
            if(view[orthRot[i]].color!=antTrail[i])
                return {cell:orthRot[i], color:antTrail[i]}

        return {cell:4, color:queenHolder}  // And if there's nothing better to do, waste your time.
    }else{
        return {cell:4, color:1}    // We're lost, go back to stage 1, and try again.
        // I could probably add logic to check if we're stage 3 or something, but meh.
    }

}else{  // If we're a worker!

    for(var i=0; i < orth.length; i++)  // Check around.
        if(view[orth[i]].ant && view[orth[i]].ant.type == 5 && view[orth[i]].ant.friend && view[orth[i]].color == queenBuild)   // Is there a queen, in build mode, around us?
            return move(4)  // Wait politely for her to finish.

    var col = antTrail[me.type-1] // Which colour I use.

    if(me.food < 1){    // If we have no food.
        for(i=0; i < orth.length; i++){ // Check Orthogonals
            if(view[orth[i]].food){ // Is there food there?
                if(turf != col) // If we're not standing on our trail.
                    return {cell: 4, color: col}    // Place our trail here, so we can still find our way back.
                return move(orth[i])    // Otherwise, move to the food!
            }
        }
    }

    if(turf == col) // If we're sitting on our trail.
        return move(2) // Move off it randomly

    var corq = (t)=>t.color == col || (t.ant && t.ant.type == 5 && t.ant.friend)    // Helper function, does this tile contain our trail or the queen?
    var corqorf = (t)=>corq(t) || t.food    // Helper function, odes this tile contain our trail, the queen, or a piece of food?
    var queenInView = false;
    for(var i=0; i < view.length; i++)  // Check the entire view.
        if(view[i].ant && view[i].ant.type == 5 && view[i].ant.friend) // Can we see the queen?
            queenInView = true; // Remember this.

    // Using food > 0 behavoir if we see a queen, makes it so that we don't accidentally build our path over the queen or something silly.

    if(me.food > 0 || queenInView){ // If we have food, or we can see the queen.
        // DON'T build paths, just orbit our path clockwise.
        var orthmov = [3,7,1,5] // Directions to move if we see a path on an orthogonal.
        var diagC = [3,1,7,5]   // Directions to move if we see a path on a diagonal.
        for(var i=0; i < orth.length; i++)  // For each Orthogonal, which takes preference.
            if(corqorf(view[orth[i]]))  // Is there the queen, a trail, or food here?
                return move(orthmov[i]) // move CW to it.
        for(var i=0; i < diag.length; i++)  // Ditto for Diagonals.
            if(corqorf(view[diag[i]]))
                return move(diagC[i])

    }else{
        // EXTEND paths, or continue orbiting clockwise.
        var orthM = [0,6,2,8]   // Directions a path should be when we check an orthogonal.
        var orthMo = [3,7,1,5]  // Directions to move if we see an orthogonal, and the diagonal is there.
        var diagC = [3,1,7,5]   // Directions to place a path if we only see an orthogonal.
        for(var i=0; i < orth.length; i++){ // In each Orthogonal.
            var v = view[orth[i]]
            if(corq(v)){    // Is there a trial?
                if(corq(view[orthM[i]]))    // Is there a trail in the after it position?
                    return move(orthMo[i])  // Move in the correct direction.
                return {cell:orthM[i], color:col}   // Place the trail in the after it position.
            }
        }
        for(var i=0; i < diag.length; i++)  // Check diagonals as a last resort.
            if(corq(view[diag[i]])) // Is there a path /HERE/?
                return {cell:diagC[i], color:col}   // Place the respective diagonal's orthogonal.

    }
    return move(2)  // When we're lost, scamper around. Just like Trail-eraser wants us to.
}

Pemain ini didiskualifikasi dalam pertandingan turnamen dan akan dikeluarkan dari papan peringkat sampai diedit untuk memperbaikinya. Rincian lebih lanjut dalam komentar selanjutnya.
trichoplax

Alasan: Tidak dapat membuat pekerja baru di atas makanan. Input: [{"warna": 7, "makanan": 0, "semut": null}, {"warna": 1, "makanan": 0, "semut": null}, {"warna": 8, "makanan": 0, "semut": nol}, {"warna": 3, "makanan": 1, "semut": null}, {"warna": 7, "makanan": 0, "semut": {"food": 1, "type": 5, "friend": true}}, {"color": 1, "food": 0, "ant": null}, {"color": 1, "food ": 0," ant "" null}, {"color": 1, "food": 0, "ant": null}, {"color": 7, "food": 0, "ant" "null" ] Tanggapan: {"sel": 3, "ketik":

3

Jalan Romanesco

Pemain ini tidak menghasilkan pekerja, dan sang ratu bergerak dalam garis lurus, menandai setiap sel yang dia kunjungi. Gerakan garis lurus dimungkinkan meskipun orientasi acak dari sel yang terlihat input, karena ratu dapat melihat sel yang ditandai dia baru saja pergi, dan bergerak dalam arah yang berlawanan dengan ini untuk memastikan garis lurus.

Blok kode pertama dalam jawaban adalah yang secara otomatis termasuk dalam permainan:

// Full version that won't be disqualified for moving onto another ant

var i

// Color own cell if white
if (view[4].color === 1) {
    return {cell:4, color:3}
}

// Otherwise move to food if visible
for (i=0; i<9; i++) {
    if (view[i].food) {
        return {cell:i}
    }
}

// Otherwise move to a white cell opposite a colored cell
for (i=0; i<9; i++) {
    if (view[i].color === 1 && view[8-i].color > 1 && !view[i].ant) {
        return {cell:i}
    }
}

// Otherwise move to an unoccupied cell
for (i=0; i<9; i++) {
    if (!view[i].ant) {
        return {cell:i}
    }
}

// Otherwise don't move at all
return {cell:4}

Berikut ini adalah versi yang lebih sederhana yang tidak memeriksa semut lain, tetapi memiliki perilaku yang identik sampai titik itu didiskualifikasi karena mencoba melangkah ke semut lain:

// Basic version for an intuitive understanding

var i

// Color own cell if white
if (view[4].color === 1) {
    return {cell:4, color:3}
}

// Otherwise move to food if visible
for (i=0; i<9; i++) {
    if (view[i].food) {
        return {cell:i}
    }
}

// Otherwise move to a white cell opposite a colored cell
for (i=0; i<9; i++) {
    if (view[i].color === 1 && view[8-i].color > 1) {
        return {cell:i}
    }
}

// Otherwise move "left and up", which will be a random direction
return {cell:0}

Blok kode kedua ini tidak akan diambil oleh game - ini berarti Anda dapat memasukkan blok kode tambahan sebagai bagian dari penjelasan jawaban Anda. Pastikan bahwa blok kode yang ingin Anda lawan dalam gim adalah yang pertama dalam jawabannya.

Untuk contoh menghasilkan gerakan acak daripada garis lurus, lihat Brownian Jig .


3

VinceAnt

Apakah Anda merasa bahwa setelah sembilan bulan, arena masih terlihat agak hambar?

Apakah Anda berpikir bahwa Antdom Walking Artist dan yatim piatu Formasi pekerja bisa lakukan dengan sedikit bantuan dalam menghiasnya secara menarik?

Maka Anda akan menyukai yang ini!

A Highway, dibuat ulang

Dari waktu ke waktu, jika sarana kami memungkinkan, ratu akan mensponsori dan menelurkan satu atau dua pelukis. Ini akan terus mengacaukan warna di sekitar mereka, dalam berbagai gaya pribadi, sering kali memperhitungkan warna yang ada tetapi mengatur ulang dan memelintirnya sesuai keinginan mereka. Mereka biasanya bekerja sendiri, atau kadang berpasangan. (Bahwa dua semut dapat mengikuti garis horizontal atau vertikal lurus sudah jelas sejak hari pertama dan Semut Forensik Dave , tetapi waspadalah sekarang untuk sebuah tandem yang menggeser pola yang mereka hadapi dengan nilai satu sel.)

Claude dan Jean

Untuk membuat ini terjangkau, ada tulang punggung yang diangkat dari Windmill saya - bayangkan seorang ratu Windmill dan sekretaris / navigator yang tidak pernah tumbuh dan menetap. (Gagasan itu sebelumnya telah disempurnakan oleh Lightspeed , yang telah dipelopori oleh Vampir fasilitas penjaga . Namun penerapannya berbeda dari Lightspeed.) Sejumlah modifikasi diperlukan untuk mencegah hal-hal berantakan ketika kita bertemu dengan keturunan kita sendiri. .

Secara alami, entri ini tidak bisa mengalahkan Lightspeed, tetapi diharapkan cukup baik untuk semua ekstravaganza-nya - sekitar kelima di antara pesaing saat ini (pada April 2018).

Kode sumber un-golfed yang dikomentari ada di GitHub , dan saya berencana untuk menambahkan rincian lebih lanjut tentang setiap jenis dan gaya pelukis di sana begitu saya berkesempatan mengumpulkan beberapa tangkapan layar yang bagus.

var ANV=1;var AMK=2;var AGS=3;var AWM=4;var AQ=5;var THC=1;var THP=[0,0,0,0,0,0];THP[AMK]=19;THP[AGS]=17;THP[AWM]=15;var RM=15;var SPDAT =[0,AMK,AGS,0,AWM,0,AGS,AWM,0,AMK,0,AWM,AMK,0,AGS];var PW=1;var PY=2;var PP=3;var PC=4;var PR=5;var PG=6;var PB=7;var PK=8;var LCLR=PW;var LT=PY;var LLSF=PG;var TN=8;var POSC=4;var NOP={cell:POSC};var CCW=[6,7,8,5,2,1,0,3,6,7,8,5,2,1,0,3,6,7,8,5,2,1];
var xn=-1;var here=view[POSC];var mC=here.color;var myself=here.ant;var mT=myself.type;var mF=myself.food;var mS=(mT!=AQ&&mF>0);var dOK=[true,true,true,true,true,true,true,true,true];
var uo=true;var sL=[0,0,0,0,0,0,0,0,0];var sD=[0,0,0,0,0,0,0,0,0];var sN=[0,0,0,0,0,0,0,0,0];var sT=[0,0,0,0,0,0,0,0,0];var fdL=0;var fdD=0;var fdT=0;sT[mC]++;for (var i=0; i<TN; i+=2){var cell=view[CCW[i]];sD[cell.color]++;sN[cell.color]++;sT[cell.color]++;if (cell.food>0){fdD++;fdT++;if (mS){dOK[CCW[i]]=false;uo=false;}}}for (var i=1; i<TN; i+=2){var cell=view[CCW[i]];sL[cell.color]++;sN[cell.color]++;sT[cell.color]++;if (cell.food>0){fdL++;fdT++;if (mS){dOK[CCW[i]]=false;uo=false;}}}var aF=[0,0,0,0,0,0];var aLF=[0,0,0,0,0,0];var aUF=[0,0,0,0,0,0];var fT=0;var mQ=0;var aE=[0,0,0,0,0,0];var aLE=[0,0,0,0,0,0];var aUE=[0,0,0,0,0,0];var eT=0;for (var i=0; i<TN; i++){var cell=view[CCW[i]];if (cell.ant){if (cell.ant.friend){aF[cell.ant.type]++;fT++;if (cell.ant.type==AQ){xn=i&6;mQ=i&1;}if (cell.ant.food>0){aLF[cell.ant.type]++;} else {aUF[cell.ant.type]++;}} else {aE[cell.ant.type]++;eT++;if (cell.ant.food>0){aLE[cell.ant.type]++;} else {aUE[cell.ant.type]++;}}dOK[CCW[i]]=false;uo=false;}}switch (mT){case AQ:return (rQSs());case ANV:return (rNSs());case AMK:return (rMSs());case AGS:return (rGSs());case AWM:return (rWSs());default:return NOP;}function rQSs(){switch (aF[ANV]){case 0:return (rQScrSy());case 1:for (var i=0; i<TN; i++){var cell=view[CCW[i]];if (cell.ant&&cell.ant.friend&&cell.ant.type==ANV){xn=i&6;if (i&1){return (rQLsSy());} else {return (rQCSy());}}}break;default:return (rQCNSy());}return NOP;}function rNSs(){if (aF[AQ]>0){if (mQ==1){return (rSLSy());} else {return (rNRSy());}} else if ((mF==0)&&(fdT>0)){return (rPEgSy());} else {return (rPPgSy());}}function rMSs(){if ((aF[AQ]>0)&&(mF==0)){if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else {return NOP;}} else if ((mF>0)&&(aF[AQ]+aF[ANV]>0)){return NOP;} else if ((mF==0)&&(fdT>0)){return (rPEgSy());} else if (aF[AGS]+aF[AWM]>1){return (rPMgSy());} else if (aF[AGS]==1){return (rCPgSy());} else {return (rMPgSy());}}function rGSs(){if ((aF[AQ]>0)&&(mF==0)){if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else {return NOP;}} else if ((mF>0)&&(aF[AQ]+aF[ANV]>0)){return NOP;} else if ((mF==0)&&(fdT>0)){return (rPEgSy());} else if (aF[AMK]+aF[AWM]>1){return (rPMgSy());} else if (aF[AMK]==1){return (rJPgSy());} else {return (rGPgSy());}}function rWSs(){if ((aF[AQ]>0)&&(mF==0)){if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else {return NOP;}} else if ((mF>0)&&(aF[AQ]+aF[ANV]>0)){return NOP;} else if ((mF==0)&&(fdT>0)){return (rPEgSy());} else if (aF[AMK]+aF[AGS]>1){return (rPMgSy());} else {return (rWPgSy());}}function rQScrSy(){if (uo){if (fdT>0){return (rQSETc());} else if (mF>=THC){for (var i=0; i<TN; i+=2){if ((view[CCW[i]].color==LT)||(view[CCW[i+1]].color==LT)){return {cell:CCW[i+1],type:ANV};}}return {cell:1,type:ANV};} else if (mC!=LT){if ((mC==LCLR)||(sN[LCLR]>=TN-1)){return {cell:POSC,color:LT};} else {return (rQSTCTc());}} else if ((sN[LCLR]>=4)&&(sN[LT]==1)){for (var i=0; i<TN; i+=2){if ((view[CCW[i]].color==LT)||(view[CCW[i+1]].color==LT)){return {cell:CCW[i+4]};}}} else if (sN[LCLR]==TN){return {cell:0};} else {return (rQSATc());}} else {if ((fdT>0)&&(eT>0)&&(eT==aE[AQ])){return (rQSSTc());} else {return (rQSEvTc());}}return NOP;}function rQLsSy(){if ((sT[LCLR]<=2)&&(mF>1)&&(eT==0)){var artist=SPDAT[mF % RM];if ((artist!=0)&&(mF>=THP[artist])&&(aF[artist]<=1)){var tc=[6,2,4,5,3];for (var i=0; i<tc.length; i++){var c=CCW[xn+tc[i]];if (dOK[c]&&(view[c].food==0)){return {cell:c,type:artist};}}}}if ((eT==0)&&(fT==1)){if (view[CCW[xn+2]].food>0){return {cell:CCW[xn+2]};} else if ((view[CCW[xn+3]].food +view[CCW[xn+4]].food>0)&&(view[CCW[xn+1]].color!=LLSF)){return NOP;} else {return {cell:CCW[xn+2]};}} else if (dOK[CCW[xn+2]]&&dOK[CCW[xn+3]]){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn]]&&dOK[CCW[xn+7]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else {return NOP;}}function rQCNSy(){for (var i=0; i<TN; i++){var cell=view[CCW[i]];if (cell.ant&&cell.ant.friend&&cell.ant.type==ANV){if (i&1){if (dOK[CCW[i-1]]){return {cell:CCW[i-1]};} else if (dOK[CCW[i+1]]){return {cell:CCW[i+1]};} else if (dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else {return NOP;}} else {if (dOK[CCW[i+7]]){return {cell:CCW[i+7]};} else if (dOK[CCW[i+1]]){return {cell:CCW[i+1]};} else if (dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else {return NOP;}}}}return (rQCSy());}function rQCSy(){return NOP;}function rSLSy(){if ((eT==0)&&(fT==1)){if (view[CCW[xn]].food>0){return {cell:CCW[xn]};} else if (view[CCW[xn+7]].food +view[CCW[xn+6]].food>0){return {cell:POSC,color:LLSF};} else {return {cell:CCW[xn]};}} else if ((eT>0)&&view[CCW[xn+2]].ant&&!view[CCW[xn+2]].ant.friend){return {cell:POSC,color:LLSF};} else if ((fT>1)&&((view[CCW[xn+6]].ant&&view[CCW[xn+6]].ant.friend&&
(view[CCW[xn+6]].ant.food>0))||(view[CCW[xn+5]].ant&&view[CCW[xn+5]].ant.friend&&
(view[CCW[xn+5]].ant.food>0)))){return {cell:POSC,color:LLSF};} else {if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};}}return NOP;}function rNRSy(){if (view[CCW[xn+1]].ant&&view[CCW[xn+1]].ant.friend&&
(view[CCW[xn+1]].ant.type==mT)){if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else {return NOP;}} else if (view[CCW[xn+7]].ant&&view[CCW[xn+7]].ant.friend&&(view[CCW[xn+7]].ant.type==mT)){if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else {return NOP;}} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+2]]){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}}function rPPgSy(){if (aLF[AMK]+aLF[AGS] +aLF[AWM]>0){for (var i=0; i<TN; i++){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.food>0)){if (dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else if (dOK[CCW[i+3]]){return {cell:CCW[i+3]};} else if (dOK[CCW[i+5]]){return {cell:CCW[i+5]};}}}} else if (aF[mT]>0){return (rSPTc());}return (rPPgTc());}function rMPgSy(){if (aF[mT]>0){return (rSPTc());}return (rMPgTc());}function rGPgSy(){if (aF[mT]>0){return (rSPTc());}return (rGPgTc());}function rWPgSy(){if (aF[mT]>0){return (rSPTc());}return (rWPgTc());}function rCPgSy(){var phase=0;for (var i=0; i<TN; i++){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==AGS)){xn=i&6;phase=i&1;break;}}if ((phase==1)&&(mC==view[CCW[xn+7]].color)&&(view[CCW[xn]].color==view[CCW[xn+1]].color)){if (dOK[CCW[xn+3]]){return {cell:CCW[xn+3]};} else if (dOK[CCW[xn]]){return {cell:CCW[xn]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}} else {return {cell:CCW[xn+7],color:mC};}return NOP;}function rJPgSy(){var phase=0;for (var i=0; i<TN; i++){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==AMK)){xn=i&6;phase=i&1;break;}}if (phase==0){if (dOK[CCW[xn+7]]){return {cell:CCW[xn+7]};} else if (dOK[CCW[xn+1]]){return {cell:CCW[xn+1]};} else if (dOK[CCW[xn+4]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else if (dOK[CCW[xn+6]]){return {cell:CCW[xn+6]};} else {return NOP;}} else {return {cell:CCW[xn+3],color:mC};}return NOP;}function rPEgSy(){for (var i=0; i<TN; i++){if ((view[CCW[i]].food>0)&&dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;}function rPMgSy(){for (var i=0; i<TN; i+=2){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type!=mT)){if (dOK[CCW[i+1]]&&!view[CCW[i+7]].ant&&!view[CCW[i+2]].ant&&!view[CCW[i+3]].ant){return {cell:CCW[i+1]};} else if (dOK[CCW[i+7]]&&!view[CCW[i+1]].ant&&
!view[CCW[i+6]].ant&&!view[CCW[i+5]].ant){return {cell:CCW[i+7]};}}}for (var i=1; i<TN; i+=2){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type!=mT)){if (dOK[CCW[i-1]]&&!view[CCW[i+6]].ant){return {cell:CCW[i-1]};} else if (dOK[CCW[i+1]]&&!view[CCW[i+2]].ant){return {cell:CCW[i+1]};}}}for (var i=0; i<TN; i++){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return {cell:POSC,color:view[CCW[0]].color};}function rQSETc(){if (mC!=LT){return {cell:POSC,color:LT};}for (var i=0; i<TN; i++){if (view[CCW[i]].food>0){return {cell:CCW[i]};}}return NOP;}function rQSSTc(){for (var i=0; i<TN; i++){if ((view[CCW[i]].food>0)&&(dOK[CCW[i]])){return {cell:CCW[i]};}}return NOP;}function rQSTCTc(){if ((mC!=LCLR)&&(sN[mC]>=4)){if (sN[LT]==0){return {cell:POSC,color:LT};} else if (sN[LT]>=3){return {cell:POSC,color:LT};} else {for (var i=0; i<TN; i++){if ((view[CCW[i]].color==LT)&&(view[CCW[i+2]].color!=LT)){return {cell:CCW[i+2],color:LT};}}return NOP;}} else if (sN[LT]==1){for (var i=0; i<TN; i++){if ((view[CCW[i]].color==LT)&&(view[CCW[i+4]].color!=LCLR)){if (view[CCW[i+1]].color==LCLR){return { cell:CCW[i+1]};} else if (view[CCW[i+7]].color==LCLR){return { cell:CCW[i+7]};} else {return {cell:POSC,color:LT};}}}return {cell:POSC,color:LT};} else {return {cell:POSC,color:LT};}return NOP;}function rQSATc(){for (var i=0; i<TN; i++){if ((view[CCW[i]].color==LCLR)&&(view[CCW[i+1]].color==LCLR)&&(view[CCW[i+2]].color==LCLR)){if ((view[CCW[i+3]].color==LCLR)&&(view[CCW[i+4]].color==LCLR)){return {cell:CCW[i+2]};}return {cell:CCW[i+1]};}}for (var i=TN-1; i>=0; i--){if (view[CCW[i]].color!=LT){return {cell:CCW[i]};}}for (var i=0; i<TN; i++){if (view[CCW[i]].color!=LT){return {cell:CCW[i],color:LCLR};}}return {cell:0,color:LCLR};}function rQSEvTc(){if (sN[LT]>0){for (var i=0; i<TN; i++){if (view[CCW[i]].color==LT){xn=i&6;}}if ( dOK[CCW[xn+7]]&&dOK[CCW[xn]]&&dOK[CCW[xn+1]]&&dOK[CCW[xn+2]]&&dOK[CCW[xn+3]] ){return {cell:CCW[xn+1]};} else if (dOK[CCW[xn+5]]&&dOK[CCW[xn+6]]&&dOK[CCW[xn+7]]&&dOK[CCW[xn]]&&dOK[CCW[xn+1]]){return {cell:CCW[xn+7]};} else if (dOK[CCW[xn+3]]&&dOK[CCW[xn+4]]&&dOK[CCW[xn+5]]){return {cell:CCW[xn+4]};} else if (dOK[CCW[xn+5]]&&dOK[CCW[xn+6]]&&dOK[CCW[xn+7]]){return {cell:CCW[xn+6]};} else if (dOK[CCW[xn+1]]&&dOK[CCW[xn+2]]&&dOK[CCW[xn+3]]){return {cell:CCW[xn+2]};} else if (dOK[CCW[xn+7]]&&dOK[CCW[xn]]&&dOK[CCW[xn+1]]){return {cell:CCW[xn]};} else {for (var i=0; i<TN; i++){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;}} else {for (var i=0; i<TN; i++){if (dOK[CCW[i]]&&dOK[CCW[i+1]]&&dOK[CCW[i+2]]&&dOK[CCW[i+3]]&&dOK[CCW[i+4]]){return {cell:CCW[i+2]};}}for (var i=0; i<TN; i++){if (dOK[CCW[i]]&&dOK[CCW[i+1]]&&dOK[CCW[i+2]]){return {cell:CCW[i+1]};}}for (var i=0; i<TN; i++){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;}return NOP;}function rPPgTc(){if (sL[mC]==0){return {cell:1,color:mC};}for (var i=1; i<TN; i+=2){if (view[CCW[i]].color==mC){xn=i&6;break;}}var col1=(mC+1) % 8+1;if ((view[CCW[xn+5]].color==mC)&&(view[CCW[xn+3]].color==col1)&&(view[CCW[xn+7]].color!=col1)){xn=(xn+4) % 8;}if (view[CCW[xn+7]].color!=col1){return {cell:CCW[xn+7],color:col1};} else if (view[CCW[xn]].color!=col1){return {cell:CCW[xn],color:col1};}var col2=(mC+5) % 8+1;if (view[CCW[xn+3]].color!=col2){return {cell:CCW[xn+3],color:col2};} else if (view[CCW[xn+2]].color!=col2){return {cell:CCW[xn+2],color:col2};} else if (view[CCW[xn+5]].color!=mC){return {cell:CCW[xn+5],color:mC};} else if (view[CCW[xn+4]].color!=col2){return {cell:CCW[xn+4],color:col2};} else if (dOK[CCW[xn+5]]){return {cell:CCW[xn+5]};} else {return (rWgPTc());}}function rMPgTc(){switch (sT[mC]){case 9:var col=((mC+2) % 8)+1;return {cell:CCW[0],color:col};case 8:for (var i=0; i<TN; i++){var col=view[CCW[i]].color;if (col!=mC){if (i==0){return {cell:POSC,color:col};} else if ((i==1)&&dOK[CCW[i+3]]){return {cell:CCW[i+3]};} else if ((i==2)&&dOK[CCW[i+5]]){return {cell:CCW[i+5]};} else {return {cell:CCW[i-1],color:col};}}}break;case 7:return rWgPTc();case 6:for (var i=0; i<TN; i++){if (view[CCW[i]].color!=mC){if ((i==0)&&dOK[CCW[i+5]]){return {cell:CCW[i+5]};} else if ((i==1)&&dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else {return {cell:CCW[i],color:mC};}}}break;case 5:case 4:case 3:for (var i=0; i<TN; i++){if (view[CCW[i]].color!=mC){return {cell:CCW[i],color:mC};}}break;case 2:case 1:default:for (var i=TN-1; i>=0; i--){var col=view[CCW[i]].color;if ((col==mC)&&(sT[view[CCW[i+4]].color]==7)&&dOK[CCW[i+4]]){return {cell:CCW[i+4]};}if (sT[col]>=3){return {cell:POSC,color:col};}}var col=view[CCW[1]].color;if (view[CCW[0]].color!=col){return {cell:CCW[0],color:col};} else if (view[CCW[2]].color!=col){return {cell:CCW[2],color:col};}break;}return (rWgPTc());}function rGPgTc(){var col=0;for (var c0=view[CCW[0]].color; c0<view[CCW[0]].color+8; c0++){var c=(c0 % 8)+1;if (sN[c]==0){col=c;}}if (col==0){return (rWgPTc());}for (var i=0; i<TN; i++){if (sN[view[CCW[i]].color]>1){return {cell:CCW[i],color:col};}}return (rWgPTc());}function rWPgTc(){var col=((mC+6) % 8)+1;if (sT[mC]==9){return {cell:CCW[0],color:col};}var myRand=(view[CCW[0]].color+sT[view[CCW[2]].color]) % 3;
switch (myRand){case 0:for (var i=0; i<TN; i+=2){if (dOK[CCW[i]]){return {cell:CCW[i]};}}break;case 1:if (view[CCW[1]].color!=view[CCW[7]].color){return {cell:CCW[1],color:view[CCW[7]].color};} else if (view[CCW[5]].color!=view[CCW[3]].color){return {cell:CCW[5],color:view[CCW[3]].color};}break;case 2:if (view[CCW[5]].color!=view[CCW[3]].color){return {cell:CCW[5],color:view[CCW[3]].color};} else if (view[CCW[1]].color!=view[CCW[7]].color){return {cell:CCW[1],color:view[CCW[7]].color};}break;default:break;}for (var i=1; i<TN; i+=2){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return (rWgPTc());}function rSPTc(){for (var i=0; i<TN; i++){if (view[CCW[i]].ant&&view[CCW[i]].ant.friend&&
(view[CCW[i]].ant.type==mT)){if (dOK[CCW[i+4]]){return {cell:CCW[i+4]};} else if (dOK[CCW[i+3]]){return {cell:CCW[i+3]};} else if (dOK[CCW[i+5]]){return {cell:CCW[i+5]};} else if (dOK[CCW[i+2]]){return {cell:CCW[i+2]};} else if (dOK[CCW[i+6]]){return {cell:CCW[i+6]};} else if (dOK[CCW[i+1]]){return {cell:CCW[i+1]};}}}return NOP;}function rWgPTc(){for (var i=0; i<TN; i++){if (dOK[CCW[i]]){return {cell:CCW[i]};}}return NOP;}

Nikmati!

** v1.0.1 Sekarang kompatibel dengan pengontrol mode ketat.

** v1.0.2 Memperbaiki kesalahan ketik yang didiskualifikasi.


Perhatikan bahwa jika semut "cukup kreatif" dan berhasil membingungkan semut yang lebih maju, maka entri ini mungkin benar-benar memiliki peluang untuk melampaui Lightspeed - pesaing yang kurang kuat = skor lebih tinggi. Namun, itu mungkin tidak akan menjadi masalah di sini, karena bahkan Highway biasanya berhasil selamat dari perlakuan para seniman ...
Alion

@Lion VinceAnt pelukis kadang-kadang berhasil membingungkan semut Windmill antara lain, dan kadang-kadang memblokir pekerja musuh, atau menyebabkan Penambang Geser atau penambang Kincir Angin untuk memulai rel baru di tempat yang salah - tetapi jika Lightspeed bermain di permainan yang sama juga, maka Lightspeed juga akan mendapat manfaat ...
GNiklasch

Versi formasi yang tidak resmi mencoba untuk berkumpul di sekitar pekerja musuh dan menghitamkan mereka dengan sampah, tetapi bahkan ini tidak selalu mengarah pada railbending. Dan bahkan ketika hal itu terjadi, tidak lazim bahwa hal ini mengurangi kemampuan pekerja lain untuk bernavigasi ke dan dari ratu. Ambillah dari saya, cukup sulit untuk menyesatkan sarang rel.
eaglgenes101

@trichoplax Fixed - maaf dan terima kasih!
GNiklasch

2

HalfThere

if(view[4].ant.type==5&&view[4].food>1)
{
    for(var i = 0; i<9; i++)
    {
        if(!view[i].ant)
        {
            return{cell:i,type: 1};
        }
    }
}
if(view[4].color != 3){
    return {cell: 4, color: 3};
}
for(var i = 0; i<9; i++)
{
    if(view[i].food==1)
    {
        return({cell:i})
    }

}


var i, j
var orthogonals = [1, 3, 7, 5]  // These are the non-diagonal cells



// Otherwise move to a white cell opposite a colored cell
for (i=0; i<4; i++) {
    j = (i+2) % 4
    if (view[orthogonals[i]].color === 1 &&
        view[orthogonals[j]].color > 1 && !view[orthogonals[i]].ant) {
        return {cell:orthogonals[i]}
    }
}

// Otherwise move to one of the vertical or horizontal cells if not occupied
for (i=1; i<9; i+=2) {
    if (!view[i].ant) {
        return {cell:i}
    }
}

// Otherwise move to one of the diagonal cells if not occupied
for (i=0; i<9; i+=2) {
    if (!view[i].ant) {
        return {cell:i}
    }
}


for(var i = 0; i<9; i++)
{
    if(view[i].color==1)
    {
        return {cell:i};
    }
}
for(var i = 0; i<9; i++)
{
    if(view[i].ant==null)
    {
        return {cell:i};
    }
}

Bot ini setengahnya ... Pada dasarnya hanya membuat garis dan berjalan dalam garis lurus dan agak derp di sekitar jika melihat garis lain.

INI BEKERJA DI PROGRESS BOT.


Ini bisa didiskualifikasi untuk menciptakan pekerja pada makanan dalam keadaan eksotis
mungkin

@pperry sebenarnya tidak bisa. Tidak pernah membuat pekerja: /
Christopher

Sebenarnya, itu tidak bisa, karena "buat logika pekerja hanyalah kode mati. Masih layak untuk ditunjukkan
pppery

Saya bingung dengan view[4].food > 1cek mati , yang merupakan kesalahan yang pernah saya buat sendiri
pppery

1
Ubah view[4].food > 1ke view[4].ant.food > 1pada baris pertama
pppery
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.