Hunger Gaming - Eat or Die

60

Hunger Gaming - Eat or Die

Jika kamu tidak makan, kamu mati. Jika Anda makan, Anda hidup (sampai Anda mati). Kamu akan mati, jadi cobalah untuk mati terakhir.

Gambaran

Ada sebuah pulau yang dihuni kawanan binatang buas. Anda mengontrol sekelompok lima predator. Objek Anda adalah untuk menjaga paket Anda tetap hidup. Lakukan ini dengan memakan mangsa. Sebaliknya, mangsa cenderung lari dari pemangsa, dan mencoba bertahan dalam kawanan. Tentu saja, bungkusan Anda akan berada di bidang yang sama dengan bungkusan lainnya , sehingga pesaing akan mencoba memakannya sebelum Anda bisa. Jangan biarkan ini membuat Anda kecil hati, atau Anda akan kelaparan.

Cara bermain

Buat dan kirimkan program baris perintah untuk mengarahkan paket Anda. Ini akan menerima informasi status dari program kontrol pada STDIN, dan output perintah pada STDOUT. Formatnya diuraikan secara terperinci di bawah ini. Setiap program hanya akan dijalankan sekali dan harus tetap berjalan sampai tidak ada lagi anggota paket yang hidup. Anda perlu membaca input saat masuk, dan merespons dengan cepat. Ada batas waktu 200ms untuk setiap respons. Jika Anda belum menjawab, paket Anda tidak akan menerima instruksi baru untuk giliran saat ini.

Jika program Anda tidak dapat dijalankan oleh controller, itu tidak akan dianggap valid. Harap sertakan string baris perintah yang perlu saya gunakan untuk menjalankan kiriman Anda. Jika ada instruksi khusus (untuk mengatur kompiler, dll), harap sertakan. Jika saya tidak bisa membuatnya berfungsi, saya akan meminta bantuan Anda dalam komentar. Jika Anda tidak merespons, saya tidak akan dapat menerima kiriman Anda.

Turnamen akan diadakan pada sistem Linux 64bit. Ingatlah hal ini ketika memberikan arahan yang diperlukan.

Detail

  • Posisi dan arah setiap makhluk adalah dalam bentuk sepasang angka floating point presisi ganda (misalnya double) yang mewakili masing-masing xdan ykoordinatnya.

  • Setiap makhluk dianggap titik. Ini berarti mereka dapat tumpang tindih dan menempati ruang yang sama. Anda tidak akan terbentur ke samping, dan tidak ada konsep tabrakan dengan makhluk lain.

  • Pulau ini berbentuk bujur sangkar, 500 unit di satu sisi. Jika Anda mencoba menjelajah melampaui batas itu, Anda akan dijepit ke tepian. Asalnya {0,0}di kiri atas, dengan xmeningkatnya ke kanan dan ymeningkat ke bawah. Sekali lagi, peta tidak terbungkus .

  • Permainan dimulai dengan 1500 + (packCount * 50) hewan mangsa. Mereka akan dikumpulkan di tengah pulau, tetapi dengan cepat memutuskan untuk mulai bergerak.

  • Paket akan diatur dalam lingkaran dengan jarak yang sama di sekeliling perimeter. Pesanan paket dikocok, jadi jangan mengandalkan mulai di lokasi tertentu.

  • Hewan mangsa dapat melihat semua hewan lain dalam radius 30 unit. Mereka dapat bergerak maksimal 6,0 unit per putaran.

  • Predator dapat melihat semua hewan lain dalam radius 50 unit. Mereka dapat bergerak maksimal 6,1 unit per putaran. Ini berarti mereka dapat melihat mangsa sebelum terlihat dan (nyaris) berlari lebih cepat dari mereka.

  • Predator hidup dan mati sesuai dengan tingkat kelaparan mereka . Itu dimulai pada 1000 , dan berkurang satu setiap belokan. Jika, setelah bergerak, predator berada dalam 1 unit mangsa, ia akan memakannya secara otomatis. Ini menghilangkan mangsa dan mengatur rasa lapar predator ke 1000. Setiap predator hanya makan satu mangsa per giliran. Jika ada lebih dari satu dalam jangkauan, ia akan memakan yang mana satu loop sampai yang pertama (belum tentu yang terdekat). Seorang predator mati jika rasa laparnya mencapai nol.

  • Paket mulai dengan masing-masing lima anggota . Setiap 5.000 putaran, semua paket yang masih dalam game akan menelurkan satu anggota baru. Ini akan ditempatkan dalam jangkauan yang terlihat dari sesama anggota paket. Pastikan entri Anda dapat menangani lebih dari lima anggota.

  • Setiap 1000 putaran, lebih banyak mangsa yang akan muncul. Jumlah mangsa baru akan menjadi jumlah predator hidup dikurangi satu.

  • Predator tidak dapat menyerang predator lainnya. Mereka memakan mangsa saat mereka menangkapnya. Itu dia.

  • Urutan dalam belokan adalah:

    • Semua mangsa membuat keputusan
    • Semua predator membuat keputusan
    • Semua mangsa bergerak
    • Semua pemangsa bergerak / makan
  • Urutan setiap paket untuk membuat keputusan / perpindahan mereka akan diacak pada setiap belokan.

Protokol (Umum)

Semua komunikasi dilakukan dalam format string US-ASCII. Angka dikonversi ke string menggunakan Java Double.toString()atau Integer.toString(). Output Anda harus diformat sehingga dapat dibaca oleh Java Double.valueOf(String)(Anda tidak akan menghasilkan bilangan bulat). Untuk detail tentang format yang dapat diuraikan, lihat dokumentasi untukDouble . Semua bidang pada baris dipisahkan oleh \tkarakter standar , dan baris baru \n. Seluruh string akan diakhiri dengan null byte \0.

Dalam contoh di bawah ini, saya menggunakan <>untuk menandai bidang demi keterbacaan. Ini tidak hadir dalam string yang sebenarnya.

Protokol (Input)

Panjang string input bervariasi, tergantung pada berapa banyak makhluk yang terlihat oleh paket Anda. Itu bisa melebihi 100 ribu karakter, jadi bersiaplah untuk itu. Pengaturan dasar adalah:

  • Baris 0: Informasi dasar tentang permainan. turnadalah nomor belokan saat ini, dan jumlah adalah jumlah total mangsa dan predator yang tersisa di lapangan. Ini integerdalam bentuk string.

    <turn>\t<preyCount>\t<predatorCount>\n
    
  • Baris 1: Id unik dan tingkat kelaparan anggota pack Anda. Ini tidak diberikan dalam urutan yang sama untuk setiap input. Gunakan id unik untuk melacak masing-masing anggota, bukan urutan kemunculannya di input. Sekali lagi, ini adalah integerstring. Untuk satu pak dua, ini akan menjadi:

    <id[0]>\t<hunger[0]>\t<id[1]>\t<hunger[1]>\n
    
  • Baris 2: Posisi anggota pack Anda, dalam urutan yang sama seperti yang diberikan pada baris 1 . Ini doublesebagai string:

    <x[0]>\t<y[0]>\t<x[1]>\t<y[1]>\n
    

Baris berikut adalah visibilitas setiap anggota paket, dalam urutan yang sama seperti yang diberikan pada baris 1 . Ini akan diberikan sebagai dua baris per anggota.

Yang pertama untuk masing-masing terdiri dari lokasi untuk mangsa yang bisa dia lihat. Yang kedua adalah lokasi untuk predator yang bisa dia lihat. Lokasi-lokasi ini tidak unik secara keseluruhan. Misalnya, jika dua anggota paket dapat melihat hewan yang sama, itu akan berada di string kedua anggota. Juga, anggota paket Anda sendiri akan disertakan. Jika Anda ingin mengecualikan mereka, Anda mungkin ingin membandingkan lokasi dengan anggota Anda sendiri. Semua lokasi dalam doubleformat string.

Untuk setiap anggota yang masih hidup:

<prey[0].x>\t<prey[0].y>\t<prey[1].x>\t<prey[1].y>\n
<predator[0].x>\t<predator[0].y>\t<predator[1].x>\t<predator[1].y>\n

Akhirnya, karakter terakhir adalah \0, di awal baris berikutnya.

Pengecualian: Jika Anda menerima input dead\0, paket Anda sudah mati. Akhiri program Anda dengan anggun. Pengontrol harus mematikan semua proses hidup ketika ditutup, tetapi saya lebih suka tidak memiliki proses zombie di semua tempat. Sebagai rasa hormat, Anda dapat memasukkan batas waktu input. Sebagai contoh, kelas contoh saya berakhir jika tidak menerima input selama 15 detik.

Protokol (Keluaran)

Outputnya sederhana. Anda akan memberikan sepasang doublenilai untuk setiap anggota paket langsung. Ini mewakili gerakan yang Anda ingin mereka lakukan pada giliran ini. Misalnya, jika makhluk Anda saat ini di {100.0, 100.0}dan Anda memberi mereka perintah {-1.0, 1.0}, mereka akan pindah ke {99.0, 101.0}. Semua angka akan berada pada satu baris, dipisahkan oleh tab.

Misalnya, jika Anda memiliki 3 anggota pack hidup, ini akan menjadi respons yang valid:

1.0\t-1.0\t2.0\t-2.0\t3.0\t-3.0\0

Hal ini akan memindahkan makhluk Anda dengan {1.0,-1.0}, {2.0,-2.0}, dan {3.0,-3.0}. Urutannya sama dengan yang diterima dalam input. Jangan lupa bagian akhirnya \0!

Jika Anda memberikan input yang tidak valid, hasil yang buruk akan mengikuti. Jika salah satu nomor tidak dapat diuraikan menjadi double, itu akan menjadi nol. Jika string secara keseluruhan tidak dapat diuraikan, tidak ada instruksi baru yang akan diberikan, dan seluruh paket Anda akan menggunakan petunjuk dari giliran sebelumnya.

Semua arah akan dijepit ke jarak maksimum 6,1 unit. Anda bisa bergerak lebih lambat dari ini jika mau. Misalnya, {1, 0}akan memindahkan Anda satu unit. {6,8}(jarak 10) hanya akan menggerakkan Anda 6,1 unit, dan akan berkurang menjadi sekitar {3.66, 4.88}. Arahnya tetap konstan.

Penting: Program kontrol membaca STDOUT dan STDERR Anda. Jika Anda melempar pengecualian dan mencetak ke STDERR, sangat kecil kemungkinannya bahwa pesan akan berbentuk respons yang valid. Cobalah untuk menghindari melakukan ini.

Program Kontrol / Pengujian

Sumber untuk controller dapat ditemukan di sini di bitbucket.org . Anda harus mengompilasinya sebelum dijalankan. Kelas utamanya adalah Game, dan semua kelas ada dalam paket default. Untuk menjalankan, masukkan perintah setiap paket sebagai argumen terpisah. Misalnya, jika Anda ingin menjalankan Java ChaserPack dan Python LazyPack.py, Anda bisa menggunakan:

java Game "java ChaserPack" "python LazyPack.py"

Pada peta, mangsa muncul berwarna hijau, dan predator berwarna merah. Namun, paket mana pun yang merupakan paket pertama yang diberikan sebagai argumen akan berwarna biru. Ini dimaksudkan untuk membedakan mereka lebih mudah untuk tujuan pengujian. Predator juga akan berkedip putih selama lima frame saat mereka makan.

Permainan akan berlanjut hingga predator terakhir kelaparan, menulis ke konsol saat kelaparan atau peristiwa kepunahan terjadi. Setelah permainan selesai, skor akan diberikan untuk setiap paket. Jika Anda ingin tidak ingin melihat peristiwa kelaparan / kepunahan, Anda dapat menggunakan -silentargumen. Maka itu hanya akan menghasilkan skor akhir. Anda harus melewati ini sebagai argumen pertama :

java Game -silent "java ChaserCat" "./someOtherPack"

Termasuk adalah paket kerangka Java bernama GenericPack. Ini mencakup operasi input / output dasar yang diperlukan. Itu ada untuk memberikan contoh yang jelas tentang bagaimana mengurai dan membalas. Jika Anda ingin menambahkan templat dalam bahasa lain, beri tahu saya.

Juga termasuk predator berdasarkan pada templat ChaserPack,. Itu tidak akan dimasukkan dalam turnamen, dan hanya disertakan untuk tujuan pengujian. Performanya cukup buruk, karena cacat penargetan yang disengaja. Jika Anda tidak bisa mengalahkannya, teruslah berusaha.

Di bawah ini adalah contoh menjalankan program kontrol (klik untuk video). Kualitas videonya tidak bagus (maaf), tetapi Anda bisa merasakan bagaimana mangsanya bergerak. ( hati-hati: audio )

tangkapan layar

Mencetak gol

Pemenang akan ditentukan oleh turnamen, mendapatkan poin di setiap putaran.

Setiap putaran berlangsung sampai semua predator mati. Setiap paket akan dinilai berdasarkan kapan anggota terakhirnya meninggal karena kelaparan. Mereka kemudian akan diberi poin berdasarkan pesanan. Poin akan terakumulasi selama sepuluh putaran, dan pemenangnya adalah paket dengan poin total tertinggi.

Tempat pertama untuk setiap putaran akan menerima 100 poin. Untuk setiap tempat setelah itu, hadiah akan dikurangi sebesar 20% (dibulatkan ke bawah). Ini akan berlanjut sampai poin mencapai nol (setelah tempat ke-17). Places 18+ tidak akan menerima poin untuk putaran ini. Paket yang mengikat akan menerima poin yang sama. Sebagai contoh:

1st : 100
2nd : 80
3rd : 64 (T)
3rd : 64 (T)
4th : 51
...
17th: 1
18th: 0
19th: 0

Poin maksimum yang mungkin selama turnamen adalah 1000, dari tempat pertama sebanyak sepuluh kali.

Jika beberapa program mengakhiri turnamen terikat untuk tempat pertama, turnamen sepuluh putaran lain akan diadakan dengan hanya entri tempat pertama yang diajukan. Ini akan berlanjut sampai satu pemenang muncul.

Saya akan mencoba menjalankan turnamen kira-kira setiap minggu, atau ketika kiriman baru masuk.

Aturan Tambahan (bermain adil!)

  • Anda tidak boleh membaca atau menulis ke sumber daya eksternal apa pun. Karena Anda tidak akan menjalankan program Anda beberapa kali, informasi status apa pun dapat disimpan secara internal.

  • Jangan mengganggu proses / pengajuan lainnya. Ini tidak berarti jangan mencoba mencuri mangsa mereka, berlari lebih cepat dari mereka, dll. Itu berarti jangan mengganggu jalannya proses. Ini adalah kebijaksanaan saya.

  • Kontestan dibatasi hingga maksimal tiga entri. Jika Anda mengirim lebih banyak, saya hanya akan mencetak tiga yang pertama dikirimkan. Jika Anda ingin mencabut satu, hapus itu.

  • Entri mungkin tidak ada hanya untuk menopang entri lain. Masing-masing harus bermain untuk menang karena kemampuannya sendiri.

  • Program Anda dapat menghasilkan maksimum satu proses anak pada satu waktu ( total keturunan, tidak langsung). Either way, pastikan Anda tidak melewati batas waktu. Anda tidak boleh memanggil Gamekelas itu sendiri dengan cara apa pun.

Hasil - 29 April 2014

Inilah hasil turnamen sepuluh ronde terbaru:

Clairvoyant         : 1000
EcoCamels           : 752
Netcats             : 688
RubySpiders         : 436
RubyVultures        : 431
CivilizedBeasts     : 382
LazyPack            : 257

Paket yang dikirimkan sebelum 09:00 EDT 2014/04/29 termasuk dalam paket ini.

Anda juga dapat melihat detail untuk setiap putaran . Untuk beberapa alasan saya memutuskan untuk menghitung putaran ke belakang, jadi itu dimulai dengan "putaran 10".

Pembaruan

2014/04/23: FGreg melaporkan bug terkait timeout (terima kasih!). Perbaikan telah dilaksanakan, sehingga penguji ingin memperbarui kode program kontrol mereka.

Geobit
sumber
28
Saya menyukai pertanyaan-pertanyaan raja bukit ini!
Cruncher
2
@ Manu Saya menulis contoh bot pada Windows 7, dan diuji pada win dan linux. Apa masalah Anda dengan mereka?
Geobits
2
Pertanyaan-pertanyaan raja bukit ini sangat mengagumkan, dan yang ini pasti menarik. Saya punya dua paket berbeda dalam karya sekarang!
mackthehobbit
2
@githubphagocyte Saya tidak benar-benar ingin membunuh paket pada timeout pertama, hanya karena saya telah melihat bahkan program sederhana habis sekali setiap 40k + ternyata atau serupa. Saya memang melakukan perubahan penamaan di controller. Belokan sekarang dikenal sebagai belokan di seluruh program, kecuali saya melewatkannya di suatu tempat.
Geobits
2
@ Geobits eh, itu tidak masalah bagi saya. Anda tahu, ini terlihat sangat mirip dengan proyek penelitian yang dilakukan salah seorang profesor fisika saya, yang mungkin saya bantu selama musim panas. Saya akan menjelaskan sedikit tentang itu nanti jika saya bisa.
krs013

Jawaban:

10

Peramal

Kode diperbarui untuk menghadapi AbleDogs

Woo hoo! Akhirnya mengalahkan Netcats itu! Saya memperluas kode yang ada (kredit ke Geobits!) Dengan beberapa modifikasi kecil untuk membuat paket prediksi masa depan ini. Tidak ada yang mengalahkan pemangsa yang tahu ke mana mangsanya akan bergerak!

Dari dua tes yang telah saya lakukan, paket saya selalu menang melawan Netcats. Tapi ini tidak akan berfungsi dengan baik jika tidak ada paket lain, karena prediksi masih gagal jika ada terlalu banyak mangsa di sekitarnya.

Mungkin saya bisa memasukkan trik CivilizedBeasts untuk mengurangi jumlah mangsa secara substansial selama beberapa ribu putaran pertama.

Dilakukan dalam 5,21 menit
Peramal (1): Belok 9270: Skor 100
EcoCamel.pl (3): Putar 8118: Skor 80
Netcats (0): Putar 6111: Skor 64
RubyVultures.rb (5): Turn 4249: Score 51
RubySpiders.rb (4): Belok 3495: Skor 40
CivilizedBeasts (2): Putar 3176: Skor 32
ChaserPack (6): Belok 2492: Skor 25

Dari nama paket saya, Anda harus tahu strategi apa yang saya gunakan = D

Edit :

  • Sistem manajemen paket yang diperbarui untuk tidak mengejar mangsa yang sama (dan juga mencoba menemukan yang paling cocok!)
  • Tingkatkan proses berkeliaran ketika jumlah mangsa kecil (ini sangat penting untuk menang!)
  • Memperbaiki kasus khusus ketika versi sebelumnya hanya macet di sudut.
  • Memperbaiki bug dalam algoritme pendeteksi predator (sekarang cukup akurat!)
  • Termasuk flock[ALIGN]faktor mangsa
  • Simpan satu mangsa sebagai hewan peliharaan jika makanannya langka
  • Buat sarang di mana paket akan menggiring mangsanya
  • Lure pemangsa terdekat untuk mengejar mangsa kita, yang mereka tidak akan menang

Saya menghitung berapa banyak mangsa yang dimakan setiap bungkus, dan inilah hasilnya:

Peramal (1) mengkonsumsi 916 mangsa di 9270 putaran (0,099 mangsa / belokan)
EcoCamel.pl (3) mengonsumsi 73 mangsa di 8118 putaran (0,009 mangsa / belokan)
Netcats (0) mengkonsumsi 563 preys dalam 6111 turn (0,092 preys / turn)
RubyVultures.rb (5) mengonsumsi 77 mangsa pada 4249 putaran (0,018 mangsa / putaran)
RubySpiders.rb (4) mengonsumsi 293 mangsa di 3495 putaran (0,084 mangsa / belokan)
CivilizedBeasts (2) mengonsumsi 10 mangsa dalam 3176 putaran (0,003 mangsa / belokan)
ChaserPack (6) mengonsumsi 43 mangsa dalam 2492 putaran (0,017 mangsa / belokan)

Paket saya sangat agresif, dan sebagian besar dari 916 jumlah yang saya pikir didapat dari mencuri mangsa dari Netcats, sama seperti RubySpiders.

CivilizedBeasts sayangnya kalah karena unta pusat dari EcoCamel.

Dan EcoCamel (dengan kelaparan kritis 500) cukup efisien, hanya makan untuk bertahan hidup sampai akhir.

Juga dengan peramal yang diperbarui ini, permainan hampir mencapai 10.000 putaran.

Kode:

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.TreeSet;

public class Clairvoyant extends GenericPack {
    private static final double MAX_SPEED = 6.1;

    private TreeSet<Animal> foods = new TreeSet<Animal>(new AnimalComparator());
    private TreeSet<Animal> predators = new TreeSet<Animal>(new AnimalComparator());

    private XY abattoirCorner;
    private double abattoirRadius = 100;

    private MyMember[] myMembers = new MyMember[100];

    public class AnimalComparator implements Comparator<Animal>{

        @Override
        public int compare(Animal arg0, Animal arg1) {
            if(arg0.x < arg1.x){
                return -1;
            } else if (arg0.x > arg1.x){
                return 1;
            } else {
                if(arg0.y < arg1.y){
                    return -1;
                } else if(arg0.y > arg1.y){
                    return 1;
                } else {
                    return 0;
                }
            }
        }
    }

    public class MyMember extends Member{
        public XY target;
        public XY herdPos;
        public double herdRadius; 
        public boolean mayEat;
        public XY pos;
        public ArrayList<MyAnimal> closestPreys;
        public boolean outdated;

        public MyMember(int id) {
            super(id);
            this.pos = new XY(x, y);
            closestPreys = new ArrayList<MyAnimal>();
            mayEat = true;
            outdated = true;
        }

        public MyMember(Member member){
            super(member.id);
            this.pos = new XY(x, y);
            closestPreys = new ArrayList<MyAnimal>();
            mayEat = true;
            outdated = true;
        }

        public MyMember(Member member, Animal target){
            super(member.id);
            this.target = new XY(target.x, target.y);
            this.pos = new XY(x, y);
            closestPreys = new ArrayList<MyAnimal>();
            mayEat = true;
            outdated = true;
        }

        public void reset(Member me){
            x = me.x;
            y = me.y;
            pos = new XY(x, y);
            closestPreys.clear();
            mayEat = true;
            outdated = true;
        }
    }

    public class MyAnimal extends Animal{
        public ArrayList<MyMember> chasers;
        public XY pos;
        public boolean resolved;

        public MyAnimal(double x, double y){
            super(x, y);
            pos = new XY(x, y);
            chasers = new ArrayList<MyMember>();
            resolved = false;
        }

        public MyAnimal(Animal ani){
            super(ani.x, ani.y);
            pos = new XY(x, y);
            chasers = new ArrayList<MyMember>();
            resolved = false;
        }
    }

    public static void main(String[] args){
        new Clairvoyant().run();
    }

    public Clairvoyant(){
        for(int i=0; i<100; i++){
            nextIdx[i] = 0;
        }
        int cornerIdx = (int)Math.floor(Math.random()*4);
        switch (cornerIdx){
        case 0: abattoirCorner = new XY(0,0); break;
        case 1: abattoirCorner = new XY(500,0); break;
        case 2: abattoirCorner = new XY(500,500); break;
        case 3: abattoirCorner = new XY(0,500); break;
        }
    }

    @Override
    public void respond(){
        updateData();
        goToTarget();
    }

    private void updateData(){
        for(int i=0; i<100; i++){
            if(myMembers[i]!=null){
                myMembers[i].pos = null;
            }
        }
        foods.clear();
        predators.clear();
        for(Member me: members){
            foods.addAll(me.foods);
            predators.addAll(me.others);
            predators.add(new Animal(me.x, me.y));
            if(myMembers[me.id] != null){
                myMembers[me.id].reset(me);
            } else {
                myMembers[me.id] = new MyMember(me);
            }
        }
        for(int i=0; i<100; i++){
            if(myMembers[i]!=null && myMembers[i].pos == null){
                myMembers[i] = null;
            }
        }

        TreeSet<MyAnimal> closestPreys = new TreeSet<MyAnimal>(new AnimalComparator());
        for(int i=0; i<100; i++){
            if (myMembers[i]==null) continue;
            MyMember me = myMembers[i];
            ArrayList<Animal> animals = findClosest(foods, me.pos, members.size());
            boolean first = true;
            for(Animal ani: animals){
                MyAnimal myAni = new MyAnimal(ani);
                if(closestPreys.contains(ani)){
                    myAni = closestPreys.ceiling(myAni);
                } else {
                    closestPreys.add(myAni);
                }
                if(first){
                    myAni.chasers.add(me);
                    first = false;
                }
                me.closestPreys.add(myAni);
            }
        }
        performMatching();
        for(int i=0; i<100; i++){
            if (myMembers[i] == null) continue;
            MyMember me = myMembers[i];
            if(!me.outdated) continue;
            if(me.closestPreys.size() == 0) continue;
            MyAnimal closestPrey = me.closestPreys.get(0);
            if(closestPrey.resolved) continue;
            if(closestPrey.chasers.size() > 1){
                MyMember hungriest = me;
                MyMember closest = me;
                for(MyMember otherMe: closestPrey.chasers){
                    if(sqDist(closestPrey.pos, otherMe) < sqDist(closestPrey.pos, closest)){
                        closest = otherMe;
                    }
                    if(otherMe.hunger < hungriest.hunger){
                        hungriest = otherMe;
                    }
                }
                if(hungriest.hunger > 200){ // Nobody's critically hungry, the closest takes the prey
                    closest.target = closestPrey.pos;
                    closest.mayEat = true;
                    closest.herdPos = abattoirCorner;
                    closest.herdRadius = abattoirRadius;
                    closest.outdated = false;
                } else {
                    if(hungriest == closest){
                        closest.target = closestPrey.pos;
                        closest.mayEat = true;
                        closest.herdPos = abattoirCorner;
                        closest.herdRadius = abattoirRadius;
                        closest.outdated = false;
                    } else {
                        closest.target = closestPrey.pos;
                        closest.mayEat = false;
                        closest.herdPos = hungriest.pos;
                        closest.herdRadius = 0;
                        closest.outdated = false;
                        hungriest.target = closestPrey.pos;
                        hungriest.mayEat = true;
                        hungriest.herdPos = abattoirCorner;
                        hungriest.herdRadius = 10;
                        hungriest.outdated = false;
                    }
                }
                closestPrey.resolved = true;
            } else {
                me.target = closestPrey.pos;
                me.herdPos = abattoirCorner;
                me.herdRadius = abattoirRadius;
                me.mayEat = true;
                me.outdated = false;
            }
        }
        for(int i=0; i<100; i++){
            if (myMembers[i] == null) continue;
            MyMember me = myMembers[i];
            if(me.outdated){
                me.target = null;
                me.outdated = false;
            }
        }
    }

    private void goToTarget(){
        for(Member me: members){
            MyMember mem = myMembers[me.id];
            if(mem.target == null){
                wander(me, 2*(me.id%2)-1);
                continue;
            } else {
                nextIdx[me.id] = 0;
                XY[] nearestHostile = new XY[100];
                for(Animal other: me.others){
                    XY otherPos = new XY(other.x, other.y);
                    boolean isMember = false;
                    for(Member otherMember: members){
                        if(other.x==otherMember.x && other.y==otherMember.y){
                            isMember = true;
                            break;
                        }
                    }
                    if(!isMember){
                        if(nearestHostile[me.id] == null || XY.sqDistance(mem.pos, otherPos) < XY.sqDistance(mem.pos,  nearestHostile[me.id])){
                            nearestHostile[me.id] = otherPos;
                        }
                    }
                }

                // Go towards the target by predicting its next position
                XY target = predictNextPos(mem.target, me);

                me.dx = (target.x - me.x);
                me.dy = (target.y - me.y); 

                // Try to herd the target to our abattoir if this member is not too hungry
                // and if there is no other hostile predator who is closer to the target than us
                // This will make the other hostile predator to keep targeting this target, while
                // it is certain that we will get the target.
                // This is a win situation for us, since it will make the other predator wasting his turn.
                if((me.hunger <= 200 && XY.sqDistance(mem.target, mem.pos) > 400) || me.hunger <= 50 ||
                        (nearestHostile[me.id] != null && Math.sqrt(XY.sqDistance(mem.target, nearestHostile[me.id])) < Math.sqrt(XY.sqDistance(mem.target, mem.pos)))){
                    continue;
                }

                // Don't eat if not threatened nor hungry
                if(me.hunger > 50 && (nearestHostile[me.id] == null ||
                        Math.sqrt(XY.sqDistance(mem.target, nearestHostile[me.id])) > Math.sqrt(XY.sqDistance(mem.target, mem.pos)) + 6)){
                    mem.mayEat = false;
                }

                // Herd to abattoir corner
                double distFromHerd = Math.sqrt(XY.sqDistance(target, mem.herdPos));
                XY oppositeAbattoirCorner = new XY(500-abattoirCorner.x, 500-abattoirCorner.y);
                double distFromOpposite = Math.sqrt(XY.sqDistance(target, oppositeAbattoirCorner));
                if((me.dx*me.dx+me.dy*me.dy > 64 && distFromHerd > mem.herdRadius && distFromOpposite > abattoirRadius)
                        || (preyCount < 5*predCount)){
                    double herdDistance = 4*(distFromHerd-mem.herdRadius)/(Island.SIZE-mem.herdRadius);
                    if(!mem.mayEat) herdDistance = 4;
                    XY gradient = target.minus(abattoirCorner);
                    me.dx += gradient.x*herdDistance/distFromHerd;
                    me.dy += gradient.y*herdDistance/distFromHerd;
                }
            }
        }
    }

    private void performMatching(){
        for(int i=0; i<100; i++){
            if (myMembers[i] == null) continue;
            MyMember me = myMembers[i];
            if(me.closestPreys.size()==0) continue;
            MyAnimal closestPrey = me.closestPreys.get(0);
            if(closestPrey.chasers.size() > 1){
                resolveConflict(closestPrey);
            }
        }
    }

    private void resolveConflict(MyAnimal prey){
        ArrayList<MyMember> chasers = prey.chasers;
        MyMember winner = null;
        double closestDist = Double.MAX_VALUE;
        for(MyMember me: chasers){
            if(sqDist(prey.pos, me) < closestDist){
                closestDist = sqDist(prey.pos, me);
                winner = me;
            }
        }
        for(int i=chasers.size()-1; i>=0; i--){
            MyMember me = chasers.get(i);
            if(me!=winner){
                me.closestPreys.get(0).chasers.remove(me);
                me.closestPreys.add(me.closestPreys.remove(0));
                me.closestPreys.get(0).chasers.add(me);
            }
        }
    }

    private Animal findClosest(Collection<Animal> preys, XY me){
        Animal target = null;
        double cDist = Double.MAX_VALUE;
        double x, y, sqDist;
        for (Animal food : preys) {
            x = food.x - me.x;
            y = food.y - me.y;
            sqDist = x * x + y * y + Double.MIN_NORMAL;
            if (sqDist < cDist) {
                cDist = sqDist;
                target = food;
            }
        }
        return target;
    }

    private ArrayList<Animal> findClosest(Collection<Animal> preys, XY me, int num){
        ArrayList<Animal> result = new ArrayList<Animal>();
        for(Animal food: preys){
            int addIdx = -1;
            for(int i=0; i<num && i<result.size(); i++){
                Animal regFood = result.get(i);
                if(sqDist(me, food) < sqDist(me, regFood)){
                    addIdx = i;
                    break;
                }
            }
            if(addIdx == -1){
                result.add(food);
            } else {
                result.add(addIdx, food);
            }
            if(result.size() > num){
                result.remove(num);
            }
        }
        return result;
    }

    private Member findClosestToTarget(Collection<Member> members, Animal target){
        Member member = null;
        double cDist = Double.MAX_VALUE;
        double x, y, sqDist;
        for (Member me : members) {
            x = me.x - target.x;
            y = me.y - target.y;
            sqDist = x * x + y * y + Double.MIN_NORMAL;
            if (sqDist < cDist) {
                cDist = sqDist;
                member = me;
            }
        }
        return member;
    }

    private static final XY[] CHECKPOINTS = new XY[]{
        new XY(49.5,49.5),
        new XY(450.5,49.5),
        new XY(450.5,100),
        new XY(49.5,100),
        new XY(49.5,150),
        new XY(450.5,150),
        new XY(450.5,200),
        new XY(49.5,200),
        new XY(49.5,250),
        new XY(450.5,250),
        new XY(450.5,300),
        new XY(49.5,300),
        new XY(49.5,350),
        new XY(450.5,350),
        new XY(450.5,400),
        new XY(49.5,400),
        new XY(49.5,450.5),
        new XY(450.5,450.5)};
    private int[] nextIdx = new int[100];

    private int advanceIdx(int idx, int sign, int amount){
        return sign*(((Math.abs(idx)+CHECKPOINTS.length-1+sign*amount) % CHECKPOINTS.length) + 1);
    }

    private void wander(Member me, int sign) {
        if(preyCount > 20*predCount){
            if (me.dx == 0 && me.dy == 0) {
                me.dx = 250 - me.x;
                me.dy = 250 - me.y;
                return;
            }

            double lx, ly, px, py;
            lx = me.dx / 4;
            ly = me.dy / 4;
            boolean dir = Math.random() < 0.5 ? true : false;
            px = dir ? ly : -ly;
            py = dir ? -lx : lx;

            me.dx += px;
            me.dy += py;
        } else {
            if(nextIdx[me.id]==0){
                XY farthest = new XY(2000,2000);
                int farthestIdx = -1;
                for(int i=0; i<CHECKPOINTS.length; i++){
                    if(sign*sqDist(CHECKPOINTS[i], me) > sign*sqDist(farthest, me)){
                        farthest = CHECKPOINTS[i];
                        farthestIdx = i+1;
                    }
                }
                nextIdx[me.id] = farthestIdx*sign;
                for(Member mem: members){
                    if(mem.id == me.id) continue;
                    if(nextIdx[mem.id]==nextIdx[me.id]){
                        nextIdx[me.id] = advanceIdx(nextIdx[me.id], sign, 5); 
                    }
                }
            }
            if(sqDist(CHECKPOINTS[Math.abs(nextIdx[me.id])-1],me) < 1){
                nextIdx[me.id] = advanceIdx(nextIdx[me.id], sign, 1);
            }
            me.setDirection(CHECKPOINTS[Math.abs(nextIdx[me.id])-1].x-me.x,
                    CHECKPOINTS[Math.abs(nextIdx[me.id])-1].y-me.y);
        }
    }

    private double sqDist(XY me, Animal target){
        double dx = me.x-target.x;
        double dy = me.y-target.y;
        return dx*dx + dy*dy + Double.MIN_NORMAL;
    }

    private double sqDist(XY me, Member target){
        double dx = me.x-target.x;
        double dy = me.y-target.y;
        return dx*dx + dy*dy + Double.MIN_NORMAL;
    }

    private double sqDist(Animal target, Member me){
        double dx = me.x-target.x;
        double dy = me.y-target.y;
        return dx*dx + dy*dy + Double.MIN_NORMAL;
    }

    private List<Animal> getNeighbors(double radius, XY pos, Collection<Animal> candidates) {
        List<Animal> neighbors = new ArrayList<Animal>();
        for(Animal neighbor: candidates){
            if(sqDist(pos, neighbor) < radius * radius){
                neighbors.add(neighbor);
            }
        }
        return neighbors;
    }

    final double[] weights = { 1, 1, 0.96, 2, 4 };
    double weightSum;

    static final int ALIGN = 0;
    static final int SEPARATE = 1;
    static final int COHESION = 2;
    static final int FLEE = 3;
    static final int WALL = 4;
    static final int VISIBLE = 30;
    static final int VISIBLE_PRED = 50;

    private HashMap<Member, List<Animal>> prevPreys = new HashMap<Member, List<Animal>>();

    private XY matchPreys(List<Animal> prevs, List<Animal> curs, XY prey){
        XY result = new XY();
        double sqDist = 0;
        Animal candidate;
        XY otherPos;
        for(Animal otherPrey: curs){
            otherPos = new XY(otherPrey.x, otherPrey.y);
            sqDist = XY.sqDistance(prey, otherPos);
            if(sqDist > VISIBLE * VISIBLE)
                continue;
            candidate = findClosest(getNeighbors(6, otherPos, prevs), prey);
            if(candidate == null){
                return null;
            }
            result.add(otherPos.x-candidate.x, otherPos.y-candidate.y);
        }
        return result;
    }

    private XY predictNextPos(XY prey, Member me) {
        List<Animal> preys = getNeighbors(VISIBLE_PRED, prey, foods);
        List<Animal> preds = getNeighbors(VISIBLE, prey, predators);

        XY flock[] = new XY[weights.length];
        for (int i = 0; i < weights.length; i++)
            flock[i] = new XY();

        double dx, dy, dist, sqDist;
        for (Animal otherPrey : preys) {
            sqDist = XY.sqDistance(prey, new XY(otherPrey.x, otherPrey.y));
            if(sqDist > VISIBLE * VISIBLE)
                continue;
            dx = otherPrey.x - prey.x;
            dy = otherPrey.y - prey.y;
            flock[COHESION].add(dx*sqDist, dy*sqDist);
            flock[SEPARATE].add(-dx*(1d/sqDist), -dy*(1d/sqDist));
            flock[ALIGN].add(new XY(prey.x-me.x,prey.y-me.y));
        }

        if(sqDist(prey, me) < 400){
            if(prevPreys.get(me) == null){
                prevPreys.put(me, preys);
            } else {
                XY flockAlign = matchPreys(prevPreys.get(me), preys, prey);
                if(flockAlign == null){
                    prevPreys.put(me , null);
                } else {
                    flock[ALIGN] = flockAlign;
                    prevPreys.put(me, preys);
                }
            }
        }

        flock[ALIGN].unitize().multiply(5);
        flock[COHESION].unitize().multiply(5);
        flock[SEPARATE].unitize().multiply(5);

        for (Animal predator : preds){
            flock[FLEE].add(prey.x-predator.x, prey.y-predator.y);
        }

        dx = Island.CENTER.x - prey.x;
        dy = Island.CENTER.y - prey.y;
        dist = Math.max(Math.abs(dx), Math.abs(dy));
        if(dist > 240){
            flock[WALL].x = dx * dist;
            flock[WALL].y = dy * dist;
            flock[WALL].unitize().multiply(5);
        }

        XY vec = new XY();
        vec.x = 0;
        vec.y = 0;
        for (int i = 0; i < flock.length; i++) {
            flock[i].multiply(weights[i]);
            vec.add(flock[i]);
        }
        limitSpeed(vec);
        return vec.add(prey);
    }

    private XY limitSpeed(XY move) {
        if (move.x*move.x+move.y*move.y > MAX_SPEED*MAX_SPEED)
            move.unitize().multiply(MAX_SPEED);
        return move;
    }
}
justhalf
sumber
1
Terlihat sangat bagus, milikmu memang lebih baik daripada netcats di gim saya. Tapi saya benci kalau saya tidak bisa menjalankan predator lain karena binatang buas saya melakukan pekerjaan yang sangat buruk dalam statistik Anda (sementara evilcamel terlalu bagus). Mungkin saya harus mencoba menginstal perl compiler atau lebih.
Herjan
Ya, saya pikir metode Anda tidak berfungsi jika ada pemangsa di tengah, seperti yang dibahas dalam jawaban Anda. Saya telah mencoba menerapkan versi lain yang berperilaku sama seperti milik Anda. Itu dapat mengubah formasi tergantung pada jumlah predator yang tersedia, jadi cukup menyenangkan untuk ditonton, meskipun tidak jauh lebih baik daripada Anda.
justhalf
Ya, strategi saya dapat ditingkatkan dengan banyak cara seperti formasi lain dengan jumlah lain dengan anggota karena binatang buas saya ditakdirkan dengan <4 predator. Atau tempat acak untuk berkumpul (bukan hanya tengah) misalnya. Tapi saya terlalu malas untuk mengimplementasikannya (sekarang). Dan itu tidak akan sebagus yang ini karena jika mangsa menjadi rendah, taktik saya tidak akan berhasil. Saat itulah Anda membutuhkan binatang seperti milik Anda (Anda sudah disebutkan untuk memulai dengan taktik saya dan ketika mangsa semakin rendah untuk menggunakan taktik ini). Jadi saya kira Anda sudah memikirkan ini.
Herjan
Saya sedang menghadapi tantangan lain sekarang, dan GeoBits tampaknya telah kehilangan minat pada yang ini, jadi saya akan membiarkannya untuk sementara waktu kecuali hasilnya diperbarui. Saya punya ide untuk beberapa kiriman lainnya, jadi saya harap tantangan ini akan tetap hidup. Saya akan melihat pembaruan Anda, tentu saja.
15

Netcats

Ini paket untuk memulai. Itu memperluas GenericPackkelas yang disertakan dengan program kontrol. Ini telah ditingkatkan sejak posting asli, dan tidak lagi kelaparan sendiri dengan kawanan yang jarang.

Netcats menggunakan formasi jaring berbentuk vee untuk menjebak mangsa di sudut, di mana mereka bisa memakannya di waktu luang. Jaring ini dibentuk dengan satu anggota "kepala" di tengah. Begitu kepala makan, ia bertukar tempat dengan anggota kelompok yang paling lapar, karena kepala biasanya yang pertama mendapatkan kesempatan untuk makan.

Jaring mulai agak kecil, tetapi melebar ketika kawanan menjadi lebih kecil untuk menjaring lapangan lebih efisien.

Jika tidak ada mangsa yang terlihat, formasi melebar menjadi pola pencarian naif yang mencakup sebagian besar pulau.

Setelah paket turun menjadi dua anggota, internet tidak berfungsi. Pada titik itu masing-masing berjalan dengan caranya sendiri, dengan rakus memakan hal terdekat yang dapat ditemukannya dan berjalan secara semi-acak sebaliknya.

Versi ini bertahan jauh lebih baik daripada Netcat naif yang terlihat di video yang ditautkan dalam pertanyaan.

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

public class Netcats extends GenericPack {

    boolean seeking;
    Member head = null;
    Set<Animal> foods;

    public static void main(String[] args) {
        new Netcats().run();
    }

    @Override
    public void respond() {
        if (foods == null)
            foods = new HashSet<Animal>();
        else
            foods.clear();
        for (Member member : members)
            foods.addAll(member.foods);

        if (members.size() < 3) {
            soloRun();
        } else {
            head = setHead();
            setHeadVec();
            for (int i = 1; i < members.size(); i++) {
                setMemberVec(i);
            }
        }
    }

    Member setHead() {
        if (!members.contains(head))
            return members.get(0);

        Member hungry = head;
        int idx = 0;
        for (int i = 0; i < members.size(); i++) {
            Member me = members.get(i);
            if (me.hunger < hungry.hunger) {
                hungry = me;
                idx = i;
            }
        }

        if (hungry != head) {
            members.remove(hungry);
            members.remove(head);
            members.add(0, hungry);
            members.add(idx, head);
            return hungry;
        }
        return head;
    }

    void setHeadVec() {
        double x = 0, y = 0;

        Collection<Animal> yummy = getFoods(head);

        seeking = false;
        if (yummy.size() == 0) {
            scoutHead();
            return;
        }

        if (members.size() == 1)
            if (findFood(head))
                return;

        for (Animal food : yummy) {
            x += food.x - head.x;
            y += food.y - head.y;
        }
        x *= 10000000;
        y *= 10000000;

        head.dx = x;
        head.dy = y;
        if (members.size() > 1)
            limitSpeed(head, MAX_SPEED * HEAD_MULT);
    }

    void scoutHead() {
        seeking = true;
        head.dy = 250 - head.y;
        head.dx = round % 80 < 40 ? -head.x : 500 - head.x;
    }

    void setMemberVec(int idx) {
        Member me = members.get(idx);
        Member leader;
        leader = idx < 3 ? members.get(0) : members.get(idx - 2);
        if (findFood(me))
            return;

        double lx, ly, px, py, tx, ty, dist;
        lx = -leader.dx;
        ly = -leader.dy;
        dist = Math.sqrt(lx * lx + ly * ly) + Double.MIN_NORMAL;
        lx /= dist;
        ly /= dist;
        px = idx % 2 == 0 ? ly : -ly;
        py = idx % 2 == 0 ? -lx : lx;

        tx = leader.x + leader.dx;
        ty = leader.y + leader.dy;
        int xtrack = seeking ? COMB : preyCount > 400 ? ASIDE : MID_SIDE;
        tx += lx * BEHIND + px * xtrack;
        ty += ly * BEHIND + py * xtrack;

        me.dx = tx - me.x;
        me.dy = ty - me.y;
        limitSpeed(me, MAX_SPEED * (idx < 3 ? MID_MULT : 1));
    }

    Collection<Animal> getFoods(Member me) {
        return me.foods.size() == 0 ? foods : me.foods;
    }

    boolean findFood(Member me) {
        if (me.hunger > 500)
            return false;

        Collection<Animal> yummy = getFoods(me);
        if (yummy.size() == 0)
            return false;

        double x, y, sqDist, cDist = 10 * 10;
        Animal target = null;
        for (Animal food : me.foods) {
            x = food.x - me.x;
            y = food.y - me.y;
            sqDist = x * x + y * y + Double.MIN_NORMAL;
            if (sqDist < cDist) {
                cDist = sqDist;
                target = food;
            }
        }

        if (target == null)
            return false;

        if (cDist < 5 * 5 || me.hunger < 200) {
            me.dx = (target.x - me.x) * 10000000d;
            me.dy = (target.y - me.y) * 10000000d;
            return true;
        }
        return false;
    }

    void soloRun() {
        double x, y, sqDist, cDist;
        for (Member me : members) {
            Collection<Animal> yummy = getFoods(me);
            if (yummy.size() == 0) {
                wander(me);
                continue;
            }

            Animal target = null;
            cDist = Double.MAX_VALUE;
            for (Animal food : yummy) {
                x = food.x - me.x;
                y = food.y - me.y;
                sqDist = x * x + y * y + Double.MIN_NORMAL;
                if (sqDist < cDist) {
                    cDist = sqDist;
                    target = food;
                }
            }

            me.dx = (target.x - me.x) * 100000d;
            me.dy = (target.y - me.y) * 100000d;
        }
    }

    void wander(Member me) {
        if (me.dx == 0 && me.dy == 0) {
            me.dx = 250 - me.x;
            me.dy = 250 - me.y;
            return;
        }

        double lx, ly, px, py;
        lx = me.dx / 4;
        ly = me.dy / 4;
        boolean dir = Math.random() < 0.5 ? true : false;
        px = dir ? ly : -ly;
        py = dir ? -lx : lx;

        me.dx += px;
        me.dy += py;
    }

    void limitSpeed(Member me, double max) {
        double x = me.dx, y = me.dy;
        double dist = Math.sqrt(x * x + y * y) + Double.MIN_NORMAL;
        if (dist > max) {
            x = (x / dist) * max;
            y = (y / dist) * max;
        }
        me.dx = x;
        me.dy = y;
    }

    final static double MAX_SPEED = 6.1;
    final static double HEAD_MULT = 0.85;
    final static double MID_MULT = 0.92;
    final static int BEHIND = -25;
    final static int ASIDE = 15;
    final static int MID_SIDE = 30;
    final static int COMB = 150;
}
Geobits
sumber
11

Laba-laba Ruby

Karena terkadang lebih sedikit lebih banyak dan banyak solusi mungkin akan mencoba untuk menyudutkan mangsanya ...

Saya pikir paket saya hanya bisa berpisah dan menunggu orang lain melakukan pekerjaan.

gets
print "3.0\t3.0\t3.0\t-3.0\t-3.0\t-3.0\t-3.0\t3.0\t0.0\t0.0\0"
STDOUT.flush

Peringatan: Ini tidak benar-benar tetap berjalan, juga tidak membaca input saat masuk atau merespons dengan cepat. Namun, karena berfungsi dengan baik dengan pengontrol, saya harap ini memenuhi syarat tanpa penyesuaian lebih lanjut.

Legat
sumber
4
+1 larutan parasit pertama. Saya pikir jenis jawaban ini akan meningkatkan kualitas jawaban lain dengan secara bertahap menghilangkan celah ...
trichoplax
@githubphagocyte Saya memiliki parasit yang lebih cerdas, tetapi ini lebih efisien dalam hal waktu hidup / baris kode. Saya harap saya akan menemukan waktu untuk mengimplementasikannya.
Legat
Mungkin @ Sintynthica sedang mengkode ide saya sekarang. Atau jika idenya adalah satu lagi, kita mungkin akan memiliki lebih banyak parasit daripada pemburu;)
Legat
1
@githubphagocte kami diizinkan membuat tiga entri, jadi saya akan memposting paket lain setelah siap. Namun, saya merasa menarik bahwa yang ini diberi kode sementara dan mungkin terbukti lebih efektif. Ini mengambil keuntungan dari Netcats dengan sangat baik dan itu benar-benar hidup lebih lama dari paket pemburu pertama saya.
Legat
3
Ini bisa masuk apa adanya, bahkan jika saya butuh beberapa saat untuk mencari tahu mengapa. Tampaknya lebih baik semakin banyak Netcats yang Anda tambahkan (yang masuk akal). +1 dari saya, mari kita lihat pemburu macam apa yang keluar untuk menghindari tikungan :)
Geobits
11

Beast Beradab

Akhirnya, saatnya memamerkan binatang buasku!

Trah saya menganggap berburu agak primitif sehingga mereka bekerja bersama dalam tim 4 dan jadi mereka meninggalkan sekutu 5 mereka, karena: lebih sedikit pemangsa = lebih banyak mangsa untuk diri mereka sendiri. Apa yang pada dasarnya mereka lakukan adalah apa yang dilakukan manusia, mereka menangkap mangsa, dan merawat ternak mereka dengan baik;)

public class CivilizedBeasts extends GenericPack{

    private static int TL = 0, TR = 0, BL = 0, BR = 0; // TopLeft/BotRight
    private static int teamSize = 0, turnsWaiting = 0, turnsToWait = 20;

    private boolean out = true;
    private double maxSpeed = 6.1, mapSize = 500;

    public CivilizedBeasts(){
    }

    @Override
    public void respond(){
        if(teamSize > members.size()){

            Member check = getMemberById(TL);
            totalLoop:
            if(check == null){
                for (Member member : members) {
                    if(member.id != TR && member.id != BL && member.id != BR){
                        TL = member.id;
                        break totalLoop;
                    }
                }

                TL = 0;
            }

            check = getMemberById(TR);
            totalLoop:
            if(check == null){
                for (Member member : members) {
                    if(member.id != TL && member.id != BL && member.id != BR){
                        TR = member.id;
                        break totalLoop;
                    }
                }

                TR = 0;
            }

            check = getMemberById(BL);
            totalLoop:
            if(check == null){
                for (Member member : members) {
                    if(member.id != TL && member.id != TR && member.id != BR){
                        BL = member.id;
                        break totalLoop;
                    }
                }

                BL = 0;
            }

            check = getMemberById(BR);
            totalLoop:
            if(check == null){
                for(Member member : members) {
                    if(member.id != TL && member.id != TR && member.id != BL){
                        BR = member.id;
                        break totalLoop;
                    }
                }

                BR = 0;
            }
        }else if(teamSize < members.size()){
            for(Member member : members) {
                if(member.id != TL && member.id != TR && member.id != BL && member.id != BR){
                    if(TL == 0)
                        TL = member.id;
                    else if(TR == 0)
                        TR = member.id;
                    else if(BL == 0)
                        BL = member.id;
                    else if(BR == 0)
                        BR = member.id;
                }
            }
        }

        teamSize = members.size();

        double border = 1;
        double x, y;
        boolean reached = true;

        double distance = 16.3;

        for (Member member : members) {
            boolean doesNotCount = false;
            x = 0; y = 0;
            if(member.id == TL){
                if(out){
                    x = -(member.x - border);
                    y = -(member.y - border);
                }else{
                    x = ((mapSize/2 - distance) - member.x);
                    y = ((mapSize/2 - distance) - member.y);
                }
            }else if(member.id == TR){
                if(out){
                    x = (mapSize - member.x - border);
                    y = -(member.y - border);
                }else{
                    x = ((mapSize/2 + distance) - member.x);
                    y = ((mapSize/2 - distance) - member.y);
                }
            }else if(member.id == BL){
                if(out){
                    x = -(member.x - border);
                    y = (mapSize - member.y - border);
                }else{
                    x = ((mapSize/2 - distance) - member.x);
                    y = ((mapSize/2 + distance) - member.y);
                }
            }else if(member.id == BR){
                if(out){
                    x = (mapSize - member.x - border);
                    y = (mapSize - member.y - border);
                }else{
                    x = ((mapSize/2 + distance) - member.x);
                    y = ((mapSize/2 + distance) - member.y);
                }
            }else{
                double dist = 50, temp = 0;
                int index = -1;
                for(int i = 0; i < member.foods.size(); i++){
                    temp = (Math.abs(member.foods.get(i).x - member.x)+Math.abs(member.foods.get(i).y - member.y));
                    if(temp < dist){
                        dist = temp;
                        index = i;
                    }
                }
                if(index != -1){
                    x = (member.foods.get(index).x - member.x);
                    y = (member.foods.get(index).y - member.y);
                }
                doesNotCount = true;
            }

            if(!doesNotCount && Math.abs(x)+Math.abs(y) > maxSpeed)
                reached = false;
            member.setDirection(x,y);
        }

        if(reached){
            if(!out){ // in the middle.
                if(teamSize < 4){
                    int temp = TL;
                    TL = BR;
                    BR = temp;
                    temp = TR;
                    TR = BL;
                    BL = temp;
                    out = true;
                }else{
                    turnsWaiting++;
                }
            }else // no need to wait in the corners
                out = false;

            if(turnsWaiting >= turnsToWait){
                turnsToWait = 15;
                out = true;
                turnsWaiting = 0;
            }

        }

    }

    public static void main(String[] args){
        new CivilizedBeasts().run();
    }
}

Menjadi sangat sulit bagi payudaraku untuk bertahan hidup dengan kurang dari 200 mangsa pada gilirannya + -12.000 dengan hanya Netcats musuh dalam permainan. Anda akan senang dengan trah ini karena benar-benar melahap mangsa dalam jumlah besar dengan kecepatan seperti yang tidak bisa trah lainnya (bukan pembantaian yang cepat dan besar memberi kemenangan, tapi itu memengaruhi waktu (lama) seluruh putaran mengambil banyak).

Herjan
sumber
3
Jika dengan " merawat mereka " yang Anda maksudkan " berulang kali menggiring mereka ke tengah dan membantai / memakannya ", maka ya, mereka melakukannya dengan baik. +1
Geobits
Ini lucu, dengan versi Evil Camel yang tidak termutasi, taktik beradab benar-benar tidak efisien karena 'unta pusat'.
user2846289
1
@VadimR Sialan, terima kasih telah memperbarui unta Anda: PI tidak dapat mengujinya karena ini bukan Jawa, tetapi saya tahu strategi saya agak tidak berguna dengan predator di tengah-tengah wilayah saya: P
Herjan
5
Ini Herjan lagi! Juga "Menjadi sangat sulit bagi payudara saya untuk bertahan hidup dengan kurang dari 200 mangsa" (penekanan ditambahkan). Saya tidak menyadari vitalitas payudara Anda tergantung pada jumlah mangsa dalam simulasi komputer ....
Justin
5

Ruby Vultures

Di sini datang paket parasit lebih aktif . Mereka berusaha mengelilingi predator terdekat yang bergerak , sehingga mereka dapat mencuri mangsanya . Mereka sedikit bergantung pada keberuntungan karena mereka tidak memiliki cara cerdas untuk memilih siapa yang akan diikuti tetapi mereka biasanya mengalahkan pemburu dan kadang-kadang laba-laba .

Mereka belum selesai, karena saya memposting ini untuk mendorong tempo :)

Saya berharap untuk:

  • membuat mereka mencari predator di luar bidang tampilan
  • memperhitungkan mangsa - sering salah satunya adalah antara paket lain dan mangsa!
  • mulai memutar mereka untuk menghindari satu kelaparan ketika semua yang lain cukup makan

22 April 2014: Menambahkan kebosanan , yang membuat mereka kurang lengket dan memungkinkan mereka untuk berburu mangsa sendiri dan mencari predator

class Animal
  attr_accessor :x, :y
end

class Hunter < Animal
  attr_accessor :id, :bored

  def initialize diff
   @diff = diff
   @lastGoal = nil
   @bored = false
  end

  def move goal
    if not goal.nil? 
      if @bored or goal != @lastGoal
        @lastGoal = goal
        return [goal.first - x + @diff.first, goal.last - y + @diff.last]
      end
    end
    [250 - x + 3*@diff.first, 250.0 - y + 3*@diff.last]
  end
end

class Pack
  def initialize
    @file = File.open "pack_log", "w"
    @count = 0
    @pack = []
    @order = []
    @hunters = []
    @closest = nil
    @random_goal = [250.0, 250.0]
    @locations = []
    @timer = 0
    d = 25.0
    diffs = [[d, d], [d, -d], [-d, -d], [-d, d], [0.0, 0.0]]
    5.times do |i|
      @pack << (Hunter.new diffs[i])
    end
    line = 0
    s = gets
    loop do
      s = gets
      if not (s =~ /dead\0/).nil?
        break
      end
      if line == 0
        get_structure s
      elsif line == 1
        get_positions s
      end
      @pack.length.times do |i|
        if line == i*2 + 3
          look_for_hunters s
          if @count <= i+1
            @closest = closest_hunter
            move
          end
        end
      end
      if not (s =~ /\0/).nil?
        line = 0
        @hunters = []
      else
        line += 1
      end
    end
  end

  def member_by_id id
    member = nil
    @pack.each do |v|
      if v.id == id
        member = v
        break
      end
    end
    member
  end

  def member_by_order index
    member_by_id @order[index]
  end

  def distance a, b
    Math.sqrt((a.first - b.first)**2 + (a.last - b.last)**2)
  end

  def bored?
    bored = true
    l1 = @locations.first
    @locations.each do |l2|
      if distance(l1, l2) > 20
        bored = false
      end
    end 
    bored
  end

  def bored_move v
    if @timer <= 0
      @random_goal = [rand(1000).to_f - 250, rand(1000).to_f - 250]
      @pack.each do |m|
        m.bored = true
      end
      @timer = 250 
    else
      @timer -= 1
    end
    v.move @random_goal
  end

  def move
    first_one = true
    answer = ""
    @order.each do |id|
      v = member_by_id id
      x, y = 0, 0
      if bored?
        x, y = (bored_move v)
      elsif @timer > 0
        @location = []
        x, y = (bored_move v)
      else
        @pack.each do |m|
          m.bored = false
        end
        @timer = 0
        x, y = v.move @closest
      end
      if not first_one
        answer << "\t"
      end
      answer << "#{x.to_i}.0\t#{y.to_i}.0"
      first_one = false
    end
    answer << "\0"
    print answer
    STDOUT.flush
  end

  def get_structure line
    @order = []
    if @pack.first.id.nil? 
      @count = 0
      line.split.each_with_index do |v, i|
        if i % 2 == 0
          @order << v.to_i
          @pack[i/2].id = v.to_i
          @count += 1
        end
      end
    else
      @count = 0
      line.split.each_with_index do |v, i|
        if i % 2 == 0
          @order << v.to_i
          @count += 1
        end
      end
    end
  end

  def get_positions line
    if not @order.empty?
      line.split.each_with_index do |v, i|
        if i % 2 == 0
          member_by_order(i/2).x = v.to_f
        else
          member_by_order(i/2).y = v.to_f
        end
      end
    end
  end

  def look_for_hunters line
    line.split.each_with_index do |v, i|
      if i % 2 == 0
        @hunters << [v.to_f]
      else
        @hunters.last << v.to_f
      end
    end
  end

  def closest_hunter
    mass_center
    closest = nil
    bestDist = 500*500
    if not @hunters.nil? and not @hunters == []
      @hunters.each do |h|
        our = false
        @pack.each do |v|
          if h.first == v.x and h.last == v.y
            our = true
          end
        end
        if our
          next
        end
        sqDist = (@mass_center.first - h.first)**2 + (@mass_center.last - h.last)**2
        if sqDist < bestDist
          closest = []
          closest << h.first
          closest << h.last
        end
      end
    end
    closest
  end

  def mass_center
    center_x = 0
    center_y = 0
    @pack.each do |v|
      center_x += v.x
      center_y += v.y
    end
    @mass_center = [center_x.to_f / @count, center_y.to_f / @count]
    if @locations.length > 30
      @locations.shift
      @locations << @mass_center
    else
      @locations << @mass_center
    end
  end
end

Pack.new
Legat
sumber
Anda pasti membutuhkan lebih banyak "pemburu" dalam campuran. Karena itu, ini cenderung melekat pada parasit lain (karena itulah mayoritas di lapangan). Saya suka menonton mereka, dan dapat melihat bagaimana mereka akan efektif dengan beragam pesaing.
Geobits
Oh ya, di lingkungan pengujian saya, saya memiliki dua paket pemburu lainnya. Tanpa mereka, burung nasar mungkin tidak tahu apa-apa. Terutama netcats yang dapat dengan cepat bekerja di sudut tanpa terlihat dari tengah.
Legat
Saya pikir, saya tahu apa yang mungkin mengganggu mereka secara khusus. Tarian perang unta jahat. @Geobits bagaimana kalau berkelahi di Youtube? 10 putaran tidak terlalu banyak untuk tetap ditonton. Tentu saja, markas besar akan dibutuhkan. Tidak mengharapkan jutaan penonton, tetapi akan menghibur untuk melihat bagaimana paket Anda tampil dan mungkin menghibur mereka sedikit :)
Legat
1
Pertandingan penuh mungkin agak lama (~ 8 menit per putaran sekarang) untuk mempertahankan perhatian, tetapi merekam satu putaran "penonton" bisa bekerja. Saya akan memikirkannya untuk masa depan.
Geobits
@ Geobits. Apakah kecepatannya sangat bervariasi selama putaran 8 menit? Saya bertanya-tanya apakah layak merekam frame per putaran sehingga mereka dapat diputar ulang pada tingkat yang konstan, daripada memperlambat selama bagian intensif komputasi. Untuk keperluan YouTube, maksud saya.
trichoplax
5

Unta Eco Jahat

Edit: Mutasi # 2. Oh, tidak, saya terlambat dengan penerapan prediksi pergerakan mangsa saya, untuk menjadi yang pertama mengalahkan Netcats. OK, jadi itu.

Mutasi ini memiliki $hunger_criticalvariabel (konstan). Mengubahnya menjadi nilai di atas 1000 membuat Unta selalu berburu, seperti Peramal. Kemudian:

Done in 11.93 minutes
camels1.pl(0)                   : Turn 23112    : Score 100
Netcats(1)                      : Turn 22508    : Score 80

Jika $hunger_criticaldiatur ke mis 500 (seperti di bawah), maka Unta saya (setelah melihat kengerian peradaban ) mencoba untuk berperilaku ramah lingkungan (maka mereka telah mengubah nama ras mereka), yaitu mereka membunuh hanya ketika lapar. Jika tidak lapar, mereka berpatroli di daerah Pulau kritis - pusat dan sudut, untuk mencegah pembantaian yang tidak ada gunanya oleh beberapa pemburu lainnya. Nah, dengan pusat, itu kurang lebih berfungsi. Gagasan berputar-putar di sudut adalah mengusir mangsanya dan membuat hidup lebih sulit bagi Kucing dan parasit. Yah, itu tidak berhasil. Lagipula mangsa bodoh masuk ke sudut-sudut.

Menarik juga, bahwa flock[ALIGN]komponen hanya dapat ditebak oleh predator, dan implementasi saya berbeda dari justhalf. Saya khawatir ada beberapa bug kecil dalam implementasi rip-off saya dari kode Geobits, menonton / membandingkan perburuan individu Unta vs Peramal.

Dan program agak lama sekarang, maaf.


Edit: Mutasi # 1. Pulau itu ternyata sangat radioaktif (yang menjelaskan kurangnya vegetasi dan sifat makhluk 'mangsa' yang tidak bisa dijelaskan), jadi inilah mutasi pertama unta saya. Setiap dari mereka dapat menjadi pemburu solo, jika lapar atau jika tidak ada sudut gratis untuk semua orang. Hunter mencoba untuk secara aktif mengejar mangsa terdekat. Jika tidak ada, ia berpatroli dalam lingkaran lebar di sekitar pusat pulau, kemudian mengejar makhluk terdekat ketika menemukannya. Sayangnya, arahan mangsa menjadi tidak dapat diprediksi ketika mendekati kawanannya (layak diselidiki ...), jadi pengejaran solo tidak terlalu efisien. Tetapi jika berhasil, Unta pergi untuk mencerna sudut bebas terdekat (jika ada). Ketika tingkat kelaparan di bawah tingkat tertentu, setiap Unta meninggalkan sudutnya (mungkin mengutuk Netcats ('di mana makanan?' )) dan bebas roaming sendiri. Dan seterusnya.


Lelucon yang sama yang dikatakan dua kali tidak lucu, tetapi (1) Saya harus memulai dari suatu tempat dan saya baru dalam hal ini, (2) Jujur, saya memikirkan taktik sudut (dan siapa yang tidak?), Menonton Netcats, sebelum Ruby Laba-laba muncul di Pulau.

Jadi, pernah dengar tentang unta karnivora? Suatu hari hewan-hewan malang terbangun di pulau yang ditinggalkan Tuhan ini untuk tidak menemukan rumput atau pepohonan sama sekali, tetapi banyak hal-hal hijau aneh yang dapat dimakan, tetapi cepat bergerak (sangat mengganggu). Saya harap tidak memiliki kebiasaan berburu (tetapi mereka akan segera bermutasi), unta saya mengembangkan skema yang sangat jahat untuk bertahan hidup: mereka berpisah dan pergi masing-masing menjadi 1 dari 4 sudut, dan yang kelima pergi ke pusat (untuk mati di sana dulu, seperti ternyata). Di tempat tujuan mereka dengan sabar menunggu, melakukan tarian unta perang, atau mungkin mereka hanya mencoba untuk tidak menginjak binatang lain yang sudah ada di sana, laba-laba dan semua ...

#!/usr/bin/env perl
use strict;
use warnings;

binmode STDOUT;
binmode STDIN;
$| = 1;
$, = "\t";

my $hunger_critical = 500;
my %pack;
my ($turn, $prey_count, $predators_count);
my $patrol_radius_hunt = 150;
my $patrol_radius_corner = 16;
my $patrol_radius_center = 1;
my @roles = qw/C LL LR UL UR/; # or P (patrol if > 5), H (hunt)
my %places = (
    UL => {x =>   1 + $patrol_radius_corner, y =>   1 + $patrol_radius_corner},
    UR => {x => 499 - $patrol_radius_corner, y =>   1 + $patrol_radius_corner},
    LR => {x => 499 - $patrol_radius_corner, y => 499 - $patrol_radius_corner},
    LL => {x =>   1 + $patrol_radius_corner, y => 499 - $patrol_radius_corner},
    C  => {x => 250, y => 250},
);

sub sq_dist {
    my ($x1, $y1, $x2, $y2) = @_;
    return ($x1 - $x2)**2 + ($y1 - $y2)**2
}

sub distance {
    return sqrt(&sq_dist)
}

sub assign_role {
    my $camel = shift;
    if (@roles) {
        my %choice = (d => 1000, i => 0);
        for my $i (0..$#roles) {
            my $r = $roles[$i];
            if ($r eq 'C') {
                if ($prey_count > 700) {
                    $choice{i} = $i;
                    last
                }
                else {
                    next
                }
            }
            my $d = distance($camel->{x}, $camel->{y}, $places{$r}{x}, $places{$r}{y});
            if ($d < $choice{d}) {
                @choice{qw/d i/} = ($d, $i)
            }
        }
        return splice @roles, $choice{i}, 1
    }
    else {
        return 'P'
    }
}

sub xy_average {
    my $xy = shift;
    my $x = my $y = 0;
    if ($xy && @$xy) {
        for my $item (@$xy) {
            $x += $item ->{x};
            $y += $item->{y}
        }
        $x /= @$xy;
        $y /= @$xy
    }
    return $x, $y
}

sub patrol {
    my ($xc, $yc, $radius, $camel) = @_;
    my ($x, $y) = ($camel->{x} - $xc, $camel->{y} - $yc);
    my $d = distance(0, 0, $x, $y);
    my $a = atan2($y, $x);
    if (abs($d - $radius) < 3) {
        $a += 6 / $radius
    }
    return $radius * cos($a) - $x, $radius * sin($a) - $y
}

while (1) {

    # Get input

    my @in;
    # Line 0 - turn, counts
    $_ = <>;
    die if /dead/;
    ($turn, $prey_count, $predators_count) = /\0?(\S+)\t(\S+)\t(\S+)/;
    # Line 1 - pack's ids and hunger
    $_ = <>;
    while (/(\S+)\t(\S+)/g) {
        push @in, {id => $1, hunger => $2}
    };
    # Line 2 - positions
    $_ = <>;
    for my $animal (@in) {
        /(\S+)\t(\S+)/g;
        ($animal->{x}, $animal->{y}) = ($1, $2);
    }
    # 2 lines per member, visible prey and predators
    for my $animal (@in) {
        $_ = <>;
        my @prey;
        while (/(\S+)\t(\S+)/g) {
            push @prey, {x => $1, y => $2}
        };
        $animal->{prey} = \@prey;
        $_ = <>;
        my @beasts;
        while (/(\S+)\t(\S+)/g) {
            push @beasts, {x => $1, y => $2}
        };
        $animal->{beasts} = \@beasts
    }
    # trailing \0 zero will be prepended to next turn input

    # Update my pack

    for my $n (0..$#in) {
        my $animal = $in[$n];
        my $id = $animal->{id};
        # old average prey position
        my @opp = xy_average($pack{$id}{prey});
        # new average prey position
        my @npp = xy_average($animal->{prey});
        # average prey displacement
        my %apd = (x => $npp[0] - $opp[0], y => $npp[1] - $opp[1]);
        $pack{$id}{apd}    = \%apd;
        $pack{$id}{hunger} = $animal->{hunger};
        $pack{$id}{x}      = $animal->{x};
        $pack{$id}{y}      = $animal->{y};
        $pack{$id}{prey}   = $animal->{prey};
        $pack{$id}{beasts} = $animal->{beasts};
        $pack{$id}{num}    = $n;
        $pack{$id}{dead}   = 0
    }

    # Bury dead animals, retrieve their roles

    while (my ($id, $camel) = each %pack) {
        if ($camel->{dead}) {
            my $role = $camel->{role};
            push @roles, $role if $role ne 'P' and $role ne 'H';
            delete $pack{$id};
        }
        else {
            $camel->{dead} = 1
        }
    }

    # See that everyone has a role and lives accordingly

    my @out;
    for my $camel (values %pack) {
        my $role = $camel->{role} ||= assign_role($camel);
        if ($camel->{hunger} < $hunger_critical and $role ne 'H') {
            push @roles, $role if $role ne 'P';
            $role = $camel->{role} = 'H'
        }
        if ($camel->{hunger} > $hunger_critical and ($role eq 'H' or $role eq 'P') and $prey_count > 400) {
            $role = $camel->{role} = assign_role($camel)
        }
        my @vector = (0, 0);
        if ($role eq 'H') {
            my @prey = @{$camel->{prey}};
            if (@prey) {
                my %nearest = (p => undef, dd => 2500);
                for my $prey (@prey) {
                    my $dd = sq_dist($camel->{x}, $camel->{y}, $prey->{x}, $prey->{y});
                    if ($dd <= $nearest{dd}) {
                        @nearest{qw/p dd/} = ($prey, $dd)
                    }
                }
                my $target = $nearest{p};
                if ($nearest{dd} > 900) {
                    @vector = ($target->{x} - $camel->{x}, $target->{y} - $camel->{y})
                }
                else {
                    my @vect = map{{x => 0, y => 0}}1..5;
                    my $n = 0;
                    for my $prey (@prey) {
                        next if $prey eq $target;
                        my $dd = sq_dist($target->{x}, $target->{y}, $prey->{x}, $prey->{y}) + 1/(~0);
                        next if $dd > 900;
                        $n ++;
                        my $dx = $prey->{x} - $target->{x};
                        my $dy = $prey->{y} - $target->{y};
                        $vect[1]{x} -= $dx / $dd;
                        $vect[1]{y} -= $dy / $dd;
                        $vect[2]{x} += $dx * $dd;
                        $vect[2]{y} += $dy * $dd
                    }
                    $vect[0] = {x => $n * $camel->{apd}{x}, y => $n * $camel->{apd}{y}};
                    my $dx = abs(250 - $target->{x});
                    my $dy = abs(250 - $target->{y});
                    my $d = $dx > $dy ? $dx : $dy;
                    if ($d > 240) {
                        $vect[4]{x} = $dx * $d;
                        $vect[4]{y} = $dy * $d;
                    }
                    for my $v (@vect) {
                        my $d = sqrt($v->{x}**2 + $v->{y}**2) + 1/(~0);
                        $v->{x} /= $d;
                        $v->{y} /= $d;
                    }
                    for my $beast (@{$camel->{beasts}}, $camel) {
                        my $dd = sq_dist($target->{x}, $target->{y}, $beast->{x}, $beast->{y});
                        next if $dd > 900;
                        $vect[3]{x} += $target->{x} - $beast->{x};
                        $vect[3]{y} += $target->{y} - $beast->{y};
                    }
                    $vector[0] = 5 * 1   * $vect[0]{x}
                               + 5 * 1   * $vect[1]{x}
                               + 5 * .96 * $vect[2]{x}
                               + 1 * 2   * $vect[3]{x}
                               + 5 * 4   * $vect[4]{x};
                    $vector[1] = 5 * 1   * $vect[0]{y}
                               + 5 * 1   * $vect[1]{y}
                               + 5 * .96 * $vect[2]{y}
                               + 1 * 2   * $vect[3]{y}
                               + 5 * 4   * $vect[4]{y};
                    my $dd = $vector[0]**2 + $vector[1]**2;
                    if ($dd > 36) {
                        my $d = sqrt($dd);
                        @vector = map {$_ * 6.1 /$d} @vector
                    }
                }
            }
            else {
                @vector = patrol(250, 250, $patrol_radius_hunt, $camel)
            }
        }
        elsif ($role eq 'P') {
            @vector = patrol(250, 250, $patrol_radius_hunt, $camel)
        }
        else {
            my $r = $role eq 'C' 
                ? $patrol_radius_center 
                : $patrol_radius_corner;
            @vector = patrol($places{$role}{x}, $places{$role}{y}, $r, $camel)
        }
        my $id_x = $camel->{num} << 1;
        my $id_y = $id_x + 1;
        @out[$id_x, $id_y] = @vector
    }

    # And let the cruel world know about it

    print @out;
    print "\0"
}

__END__
pengguna2846289
sumber
5
Ini harus menjadi skrip perl yang paling mudah dibaca yang pernah saya lihat di situs ini.
Geobits
Anda harus pergi ke sudut yang tepat untuk mengusir mereka secara efektif, jika tidak, Anda hanya akan benar-benar berpartisipasi dalam penjagalan Netcats, haha
justhalf
@justhalf, Seperti yang saya katakan: rencana tidak berhasil. Parasit yang duduk di sudut juga tidak mengusir mangsanya. Hm-m, mungkin 2 atau lebih binatang buas yang berpatroli di satu sudut akan membantu.
user2846289
Unta Anda sebenarnya cukup bagus! Untungnya (bagi saya) saya telah meningkatkan Peramal saya, sehingga sebagian besar waktu (tidak selalu), paket saya menang melawan Anda selama pertempuran terakhir. Menarik!
justhalf
1
Jika Anda lebih dekat dari 8 (20-2 * 6) unit dari mangsa, kita dapat melihat pergerakan semua mangsa lain yang berada dalam 30 unit mangsa kita pada giliran saat ini. Dan vecproperti pada dasarnya hanya perpindahan dari belokan sebelumnya ke belokan saat ini. Dan seperti yang saya katakan, kami melakukan pencocokan dari giliran sebelumnya untuk mencari tahu mangsa mana yang pergi ke mana, kita tidak bisa bergantung pada urutan mangsa. Ini dimungkinkan karena mangsa biasanya (dalam skenario tipikal) menjaga jarak yang cukup dari satu sama lain (> 12 unit), dan sebagian besar waktu kita dapat mencocokkan mangsa di giliran sebelumnya dengan giliran saat ini.
justhalf
4

AbleDogs - PHP

Anjing-anjing yang baik ini telah belajar bagaimana menggigit betis mangsa untuk membawanya ke dinding. Mereka juga suka berkeliaran di padang rumput untuk mencari mangsa baru. Terakhir, mereka diajarkan untuk tidak makan kecuali mereka benar-benar membutuhkan kalori.

Masukkan kode ke dalam AbleDogsfile dan jalankan denganphp AbleDogs

<?php
// simulation parameters

define ("ARENA_SIZE", 500);

define ("HUNGER_MAX", 1000);

define ("PREY_SPEED", 6);
define ("PRED_SPEED", 6.1);

define ("PREY_VISION", 30);
define ("PRED_VISION", 50);

define ("WALL_BOUNCE", 10); // distance from a wall from which a prey starts bouncing

// derived constants

define ("PRED_SPEED2" , PRED_SPEED  * PRED_SPEED );
define ("PRED_VISION2", PRED_VISION * PRED_VISION);
define ("PREY_VISION2", PREY_VISION * PREY_VISION);

// grid to speedup preys lookup

define ("GRID_SIZE", ceil (ARENA_SIZE/PRED_VISION));
define ("GRID_STEP", ARENA_SIZE/GRID_SIZE);

// search patterns

define ("SEARCH_OFFSET", WALL_BOUNCE+PRED_VISION/sqrt(2));
define ("SEARCH_WIDTH" , 2*sqrt(PRED_VISION2-PRED_SPEED2/4));
define ("SEARCH_HEIGHT", ARENA_SIZE-2*SEARCH_OFFSET);
define ("SEARCH_SIZE"  , ceil(SEARCH_HEIGHT/SEARCH_WIDTH));
define ("SEARCH_STEP"  , SEARCH_HEIGHT/SEARCH_SIZE);
define ("SEARCH_LEGS"  , 2*SEARCH_SIZE+1);

// tracking

define ("MAX_TRACK_ERROR", 10); // max abs distance for prey tracking correlation
define ("TRACKING_HUNGER_START", HUNGER_MAX*.9); // hunger limit to try and eat the tracked prey (start of game)
define ("TRACKING_HUNGER_END", 4);     // idem, for endgame
define ("TRACKING_DISTANCE", PREY_SPEED*2.5);
define ("TRACKING_DISTANCE2", TRACKING_DISTANCE * TRACKING_DISTANCE);

class Point {
    public $x = 0;
    public $y = 0;

    function __construct ($x=0, $y=0)
    {
        $this->x = (float)$x;
        $this->y = (float)$y;
    }

    function __toString() // for comparisons
    {
        return "$this->x,$this->y";
    }

    function multiply ($scalar)
    {
        return new Point ($this->x * $scalar, $this->y * $scalar);
    }

    function add ($v)
    {
        return new Point ($this->x + $v->x, $this->y + $v->y);
    }

    function dot($v)
    {
        return $this->x * $v->x + $this->y * $v->y;
    }

    function rotate90()
    {
        return new Point (-$this->y, $this->x);
    }

    function vector_to ($goal)
    {
        return new Point ($goal->x - $this->x, $goal->y - $this->y);
    }

    function norm2 ()
    {
        return $this->dot ($this);
    }

    function norm ()
    {
        return sqrt ($this->norm2());
    }

    function normalize ($norm = 1)
    {
        $n = $this->norm();
        if ($n != 0) $n = $norm/$n;
        return $this->multiply ($n);
    }

    function limit ($norm)
    {
        return $this->norm() > $norm
             ? $this->normalize($norm)
             : clone $this;
    }
}

class Search {

    function __construct ($direction)
    {
        switch ($direction % 4)
        {
            case 0: $this->pos = new Point (          0,           0); break;
            case 1: $this->pos = new Point (SEARCH_SIZE,           0); break;
            case 2: $this->pos = new Point (SEARCH_SIZE, SEARCH_SIZE); break;
            case 3: $this->pos = new Point (          0, SEARCH_SIZE); break;
        }
        $this->start();
    }

    private function start ()
    {
        $this->dir = $this->pos->x == $this->pos->y;
        $this->adj = $this->pos->x ? -1 : 1;
        $this->target = new Point ($this->pos->x * SEARCH_STEP + SEARCH_OFFSET,
                                   $this->pos->y * SEARCH_STEP + SEARCH_OFFSET);
        $this->leg = 0;
    }

    function point ($pos)
    {
        if ($pos == $this->target)
        {
            if ($this->leg % 2)
            {
                if ($this->dir) $this->pos->y+= $this->adj;
                else            $this->pos->x+= $this->adj;
            }
            else
            {
                if ($this->dir) $this->pos->x = $this->pos->x ? 0 : SEARCH_SIZE;
                else            $this->pos->y = $this->pos->y ? 0 : SEARCH_SIZE;
            }
            $this->leg++;
            if ($this->leg == SEARCH_LEGS) $this->start();
            $this->target = new Point ($this->pos->x * SEARCH_STEP + SEARCH_OFFSET,
                                       $this->pos->y * SEARCH_STEP + SEARCH_OFFSET);
        }
        return $this->target;
    }
}

class Pack {

    public static $turn;   // turn number
    public static $size;   // number of live members
    public static $member; // array of members

    public static $prev_preys;     // previous coordinates of all preys
    public static $prev_preds;     // previous coordinates of foreign predators

    public static $n_preys; // total number of preys     (including those not currently seen)
    public static $n_preds; // total number of predators (including those not currently seen)

    public static $preys;     // coordinates of all preys
    public static $preds;     // coordinates of all predators
    public static $own_preds; // coordinates of all predators in our pack
    public static $foe_preds; // coordinates of all foreign predators

    public static $arena_center; // arena center

    private static $output_order; // to send output according to input order

    function init ()
    {
        Pack::$member = array();
        Pack::$arena_center = new Point (ARENA_SIZE/2, ARENA_SIZE/2);
    }

    function read_line ($line)
    {
        $values = array();
        if ($line == "") return $values;
        $input = explode ("\t", $line);
        $num = count($input);
        if ($num % 2) panic ("read_line: invalid input $line num $num");
        $num /= 2;
        for ($i = 0 ; $i != $num ; $i++)
        {
            $values[] = new Point ($input[$i*2  ], $input[$i*2+1]);
        }
        return $values;
    }

    function read_input ()
    {
        // read controller input (blocking)
        $input = "";
        while (($in = fread(STDIN, 1)) !== false)
        {
            if ($in == "\0") break;
            $input .= $in;
        }

        // check extinction
        if ($input == "dead") return false;
        $lines = explode ("\n", $input);

        // save previous predators and preys positions
        Pack::$prev_preys = Pack::$preys;
        Pack::$prev_preds = Pack::$foe_preds;

        // line 0: turn, preys, predators
        list (self::$turn, Pack::$n_preys, Pack::$n_preds) = explode ("\t", $lines[0]);

        // line 1: list of ids and hunger levels
        $id = array();
        Pack::$size = 0;
        Pack::$output_order = array();
        foreach (Pack::read_line($lines[1]) as $i=>$v)
        {
            $id[$i] = $v->x;
            Pack::$output_order[] = $id[$i];

            if (!isset (Pack::$member[$id[$i]])) Pack::$member[$id[$i]] = static::new_member();
            Pack::$size++;
            Pack::$member[$id[$i]]->hunger = $v->y;
            Pack::$member[$id[$i]]->ttl = self::$turn;
        }

        // line 2: member positions
        Pack::$own_preds = array();
        foreach (Pack::read_line($lines[2]) as $i=>$pos)
        {
            Pack::$member[$id[$i]]->pos = $pos;
            Pack::$own_preds[] = $pos;
        }

        // lines 3 to 2*#members+3: coordinates of all visible preys and predators
        $preys = array();
        $preds = array();
        $y_seen = array();
        $d_seen = array();
        for ($i = 0 ; $i != Pack::$size ; $i++)
        {
            // visible preys
            foreach (Pack::read_line($lines[2*$i+3]) as $coords)
            {
                if (!in_array ($coords, $preys) || !isset($y_seen[(string)$coords]))
                {
                    $preys[] = $coords;
                }
            }
            foreach ($preys as $p) $y_seen[(string)$p] = true;

            // visible predators
            foreach (Pack::read_line($lines[2*$i+4]) as $coords)
            {
                if (!in_array ($coords, $preds) || !isset($d_seen[(string)$coords]))
                {
                    $preds[] = $coords;
                }
            }
            foreach ($preds as $p) $d_seen[(string)$p] = true;
        }

        // remove dead members
        foreach (Pack::$member as $k => $m)
        {
            if ($m->ttl != self::$turn)
            {
                unset (Pack::$member[$k]);
            }
        }

        // filter out own positions from predators list
        Pack::$foe_preds = array_diff ($preds, Pack::$own_preds);
        Pack::$preds = Pack::$foe_preds;
        foreach (Pack::$own_preds as $p) Pack::$preds[] = $p;
        Pack::$preys = $preys;

        // done
        return true;
    }

    function output_moves ()
    {
        $output = array();
        foreach (Pack::$output_order as $i)
        {
            $output[] = Pack::$member[$i]->move->x;
            $output[] = Pack::$member[$i]->move->y;
        }
        echo implode ("\t", $output) . "\0";
    }

    static function point_closest_to_walls ($pos)
    {
        $delta = $pos->vector_to (Pack::$arena_center);
        if (abs ($delta->x) > abs ($delta->y))
        {
            $y = $pos->y;
            $x = $delta->x > 0 ? -1 : ARENA_SIZE+1;
        }
        else
        {
            $x = $pos->x;
            $y = $delta->y > 0 ? -1 : ARENA_SIZE+1;
        }
        return new Point ($x, $y);
    }

    static function in_arena ($pos)
    {
        $delta = $pos->vector_to (Pack::$arena_center);
        return abs ($delta->x) <= ARENA_SIZE/2 && abs ($delta->y) <= ARENA_SIZE/2;
    }

    static function clamp_to_arena (&$pos)
    {
        // mimics the slightly strange behaviour of the Java engine setInZeroBounds function
        if ($pos->x >= ARENA_SIZE) $pos->x = ARENA_SIZE-1; // should rather be ARENA_SIZE
        if ($pos->x <           0) $pos->x = 0;
        if ($pos->y >= ARENA_SIZE) $pos->y = ARENA_SIZE-1;
        if ($pos->y <           0) $pos->y = 0;
    }

    function get_closest ($pos, $set, $max_dist)
    {
        // check for empty set
        if (count ($set) == 0) return null;

        // construct an array of distances with the same indexes as the points
        $dist = array();
        $max_dist *= $max_dist;
        foreach ($set as $k=>$pt)
        {
            $d = $pos->vector_to($pt)->norm2();
            if ($d <= $max_dist) $dist[$k] = $d;
        }
        if (count($dist) == 0) return false;

        // get the key of the smallest distance and use it to retrieve the closest point
        $keys = array_keys ($dist, min($dist));
        return $set[$keys[0]];
    }

    function get_visible ($pos, $set)
    {
        $res = array();
        $skipped = false;
        $pts = 0;
        foreach ($set as $point)
        {
            $d = $pos->vector_to($point)->norm2();
            if ($d == 0 && !$skipped)
            {
                $skipped = true;
                continue; // skip ourself
            }
            if ($d > PREY_VISION2) continue; // skip far points
            $res[] = $point;
            if ($pts++ > 10) break; // too many points are useless since prediction will go haywire anyway
        }
        return $res;
    }
}
Pack::init();

class PackMember {
    public $pos; // current position
    public $ttl; // last turn reported alive

    function move_to ($goal)
    {
        $this->move = $this->pos->vector_to ($goal);
    }

    function intercept ($target_pos, $target_speed)
    {
        // change reference to position difference
        $delta = $this->pos->vector_to($target_pos);
        $i = $delta->normalize();
        $j = $i->rotate90();

        // match tangential speeds
        $vj = $target_speed->dot ($j);

        // deduce axial speed
        $vi = PRED_SPEED2 - $vj*$vj; // this should always be positive since predators are faster than preys
        $vi = sqrt ($vi);

        // return intercept speed in original reference coordinates
        return $i->multiply($vi)->add($j->multiply($vj));
    }
}

class Target {
    public $pos;      // current position
    public $pos_next; // predicted position
    public $speed;    // estimated speed

    function __construct ($pos)
    {
        $this->pos    = $pos;
        $this->speed  = new Point(0,0);
        $this->predict();
    }

    private function predict()
    {
        // predators contribution
        $preds = Pack::get_visible ($this->pos, Pack::$preds);
        $this->preds = count ($preds);
        $res = new Point();
        foreach ($preds as $predator)
        {
            $res = $res->add ($predator->vector_to ($this->pos));
        }
        $res = $res->multiply (2);

        // preys contribution
        $preys = Pack::get_visible ($this->pos, Pack::$preys);
        $this->preys = count ($preys);

        $f_cohesion  = new Point;
        $f_separate  = new Point();
        foreach ($preys as $prey)
        {
            $delta = $this->pos->vector_to ($prey);
            $d2 = $delta->norm2();
            if ($d2 != 0)
            {
                $f_cohesion  = $f_cohesion ->add ($delta->multiply ($d2));
                $f_separate  = $f_separate ->add ($delta->multiply (-1/$d2));
            }
        }

        $res = $res
        ->add ($this->speed->normalize(5*.96)) // assume all preys have same speed as target
        ->add ($f_cohesion ->normalize(5*1))
        ->add ($f_separate ->normalize(5*1));
        $delta = $this->pos->vector_to(Pack::$arena_center);
        $dist = max (abs($delta->x), abs($delta->y));
        if ($dist > (ARENA_SIZE/2-WALL_BOUNCE))
        {
            $res = $res->add ($delta->normalize(5*4));
        }

        $this->raw_speed = $res;
        $this->speed = $res->limit(PREY_SPEED);
        $this->pos_next = $this->pos->add ($this->speed);
        Pack::clamp_to_arena ($this->pos_next);
    }

    function track ()
    {
        // see if we can find our prey at the start of a new turn
        $min = 1e10;
        foreach (Raptors::$free_preys as $k=>$prey)
        {
            $dist = abs ($this->pos_next->x - $prey->x) + abs ($this->pos_next->y - $prey->y);
            if ($dist < $min)
            {
                $min = $dist;
                $new_pos = $prey;
                $new_k = $k;
                if ($min < .001) break;
            }
        }
        if ($min > MAX_TRACK_ERROR) return false;

        // remove this prey from free preys
        unset(Raptors::$free_preys[$new_k]);

        $delta = $new_pos->vector_to($this->pos_next);

        // update postion and speed
        if ($this->speed->norm2() == 0)
        {
            // this can be either an endgame prey not yet moving
            // OR initial speed for a new target
            $this->speed = $this->pos->vector_to ($new_pos);
        }
        $this->pos = $new_pos;

        // predict speed and position
        $this->predict();
        return true;
    }
}

class Raptor extends PackMember {

    // possible states
    const IDLE     = 1;
    const TRACKING = 2;
    const HUNTING  = 3;
    const RUSHING  = 4;
    public $state;

    public  $target;  // current prey
    public  $patrol;  // patrol governor

    private static $id_gen;

    function __construct ()
    {
        $this->patrol = new Search (++self::$id_gen);
        $this->target  = null;
        $this->state = Raptor::IDLE;
        $this->pos = null;
        $this->hunger = HUNGER_MAX;
    }

    function __destruct ()
    {
        $this->tracking_lost();
    }

    function tracking_lost()
    {
        $this->target  = null;
        $this->state = Raptor::IDLE;
    }

    function track_prey()
    {
        // stop tracking if hunger went back to max
        if ($this->hunger == HUNGER_MAX)
        {
            $this->tracking_lost();
        }

        // try to acquire a new target
        if (!$this->target)
        {
            $victim = Pack::get_closest ($this->pos, Raptors::$free_preys, PRED_VISION);
            if (!$victim) return;
            $this->target = new Target ($victim);
            $this->state = Raptor::TRACKING;
        }

        // track prey
        if (!$this->target->track (Pack::$preys))
        {
            // prey was eaten or move prediction failed
            $this->tracking_lost();
        }
    }

    function beat_competition ()
    {
        if ($this->target === null) return;
        $pm = $this->target->pos_next->vector_to ($this->pos);
        $dm = $pm->norm2();
        foreach (Pack::$foe_preds as $f)
        {
            $pf = $this->target->pos_next->vector_to($f);
            $df = $pf->norm2();
            if ($df > PRED_VISION2) continue;
//          if ($df < ($dm*2))
            {
                $this->state = Raptor::RUSHING;
                return;
            }
        }
        if ($this->state == Raptor::RUSHING) $this->state = Raptor::TRACKING;
        return;
    }
}

class Raptors extends Pack {
    public static $free_preys; // coordinates of all preys that are not targeted

    // allows generic Pack to create a proper pack member instance
    static function new_member()
    {
        return new Raptor();
    }

    // main AI loop
    static function think ()
    {
        $hunger_limit = Pack::$n_preys > 2 * Pack::$n_preds ? TRACKING_HUNGER_START : TRACKING_HUNGER_END;
        self::$free_preys = static::$preys;

        // update targets and members states
        foreach (Pack::$member as $m)
        {
            // track current targets
            $m->track_prey();

            // rush to target if a competitor draws near
            $m->beat_competition();

            // hunt if hungry enough
            if ($m->state == Raptor::TRACKING && $m->hunger < $hunger_limit)
            {
                $m->state = Raptor::HUNTING;
            }
        }

        // move members
        foreach (Pack::$member as $m)
        {
            switch ($m->state)
            {
            case Raptor::IDLE:
                $destination = $m->patrol->point($m->pos);
                break;
            case Raptor::TRACKING:
                $wall_point = Pack::point_closest_to_walls ($m->target->pos_next);
                $destination = $wall_point->vector_to ($m->target->pos_next)->normalize (TRACKING_DISTANCE)->add($m->target->pos_next);
                break;
            case Raptor::HUNTING:
                $wall_point = Pack::point_closest_to_walls ($m->target->pos_next);
                $to_hunter = $m->target->pos_next->vector_to ($m->pos);
                $dist_to_target = $to_hunter->norm();

                if ($dist_to_target > (PREY_VISION-PREY_SPEED)) // intercept the prey
                {
                    // use actual speed (i.e. true position delta, including wall stops)
                    $target_true_speed = $m->target->pos->vector_to ($m->target->pos_next);
                    $intercept_speed = $m->intercept ($m->target->pos, $target_true_speed);
                    $destination = $m->pos->add ($intercept_speed);
                }
                else if ($dist_to_target < PRED_SPEED) // pounce on the prey!
                {
                    $destination = $m->target->pos_next;
                }
                else if ($to_hunter->dot($m->target->speed) > 0)
                {
                    $destination = $m->target->pos_next;
                }
                else // goad the prey
                {
                    $to_wall = $m->target->pos->vector_to ($wall_point);
                    $wall_point = $wall_point;
                    $raw_speed = $m->target->raw_speed->add($m->target->pos->vector_to($m->pos)->multiply(2))->multiply (-0.5);
                    $wpd_t = $m->target->pos->vector_to ($wall_point)->normalize()->rotate90(); // wpd = Wanted Prey Direction
                    $delta = $wpd_t->multiply ($raw_speed->dot ($wpd_t));
                    $destination = $delta->vector_to ($m->target->pos_next);
                    if (!Pack::in_arena ($destination)) $destination = $m->target->pos_next;
                }
                break;
            case Raptor::RUSHING:
                $destination = $m->target->pos_next;
                break;
            }
            $m->move_to ($destination);
        }
    }
}

while (Raptors::read_input())
{
    Raptors::think();
    Raptors::output_moves();
}
?>

Pertimbangan umum

  • Ini adalah akhir pertandingan yang diperhitungkan. Anda dapat memiliki algoritme perburuan paling cerdas yang pernah ada, jika Anda tidak melihat dan menangkap beberapa mangsa terakhir lebih cepat dari lawan, Anda kalah.

  • Jika predator Anda tidak dapat menangkap mangsa sendirian (atau setidaknya berpasangan), Anda bersulang begitu kepadatan mangsa turun cukup rendah untuk bergantung pada keberuntungan buta atau memblokir mangsa ke sudut.

  • Prediktor pergerakan mangsa pada dasarnya wajib. Saya tidak bisa membayangkan mengalahkan program berbasis prediksi tanpa memiliki prediksi sendiri.

Kejaran ekor

Cara yang paling tidak efisien untuk menangkap mangsa adalah dengan mengejarnya. Dengan asumsi satu predator mengejar mangsa tunggal dan tidak ada pengaruh eksternal (dinding, mangsa lain, dll), pengejaran ekor bisa bertahan selamanya. Segera setelah Anda memasuki radius penglihatan 30 unit, mangsa melarikan diri dengan kecepatan 6 untuk 6.1 Anda, sehingga Anda memperoleh .1 jarak per belokan: dalam garis lurus, Anda akan membutuhkan sekitar 300 putaran untuk mendapatkannya.

Dengan mempertimbangkan ukuran arena, mangsa akan melakukan perjalanan paling banyak diagonal 500 unit persegi sebelum menabrak dinding atau sudut, yang akan mengambil paling banyak 117 putaran.

Strategi yang menang jelas untuk menemukan cara untuk memperlambat mangsa, yaitu dengan memiliki predator lain atau dinding / sudut di depannya.

Prediktor

Dengan kecepatan mangsa 6, mangsa dapat pindah ke area 36 * pi unit kuadrat. Dengan radius penangkapan 1, tebakan buta tentang di mana mangsa selanjutnya adalah peluang 1/36 * pi (sekitar 1%) untuk berhasil. Jelas ada sesuatu yang harus dilakukan untuk meningkatkan itu!

Melihat kode mesin simulasi, Anda dapat melihat bahwa inputnya adalah:

  • posisi mangsa dan predator yang terlihat
  • kecepatan mangsa sebelumnya

Sementara semua posisi tersedia, kecepatan mangsa sebelumnya tidak. Satu-satunya cara untuk menghitung kecepatan ini adalah dengan melacak setiap mangsa dari satu belokan ke belokan berikutnya, yang hampir mustahil dilakukan (kecuali jika Anda menerapkan algoritma pelacak gerak yang sangat cerdas). Jadi seorang prediktor dapat dengan mudah mereproduksi semua ketentuan perhitungan, kecuali untuk kontribusi kecepatan yang harus ditebak.

Dalam kasus mangsa tunggal, kecepatan dapat dilacak tanpa terlalu banyak masalah, yang memungkinkan untuk membangun prediktor "sempurna" untuk menangkap mangsa yang diisolasi dari kawanan. Yang pada dasarnya adalah semua yang Anda butuhkan untuk permainan akhir, ketika mangsa terlalu sedikit untuk saling berinteraksi. Ketika mangsa banyak dan efek kawanan cukup kuat untuk mengelabui sang prediktor, kepadatan mangsa akan mengkompensasi kesalahan (jika Anda tidak menangkap yang Anda tuju, kemungkinan Anda akan mendapatkan salah satu teman terdekatnya. ).

Goading mangsa

Dengan pengetahuan yang tepat tentang perhitungan kecepatan mangsa, dimungkinkan untuk "mengarahkan" mangsa yang diberikan ke arah yang diinginkan, dengan menyesuaikan posisi predator.

Ini memungkinkan untuk menempelkan mangsa ke dinding, atau mengarahkannya ke anggota paket lainnya. Saya mencoba beberapa strategi yang disempurnakan, seperti menjepit mangsa di antara dua anggota kelompok. Sayangnya, ini terbukti kurang efisien daripada rutin "pin and scan" saat ini, karena menjaga dua predator sibuk mengejar satu mangsa meninggalkan oposisi dengan terlalu banyak predator bebas untuk mencari padang rumput.

Mencuri mangsa

Salah satu karakteristik perilaku mangsa adalah bahwa pengaruh pemangsa meningkat secara proporsional dengan jaraknya dari mangsa (asalkan tetap dalam radius penglihatan mangsa). Semakin dekat predator dengan mangsa, paling tidak mangsa akan menjauh darinya.

Ini berarti bahwa ketika dua pemangsa bersaing untuk menangkap mangsa, yang terdekat pasti akan mendapatkannya terlebih dahulu. Bahkan pesaing super pintar yang akan mengatur posisi dirinya tepat di depan sumbu chaser / mangsa pada dasarnya akan menakuti mangsa ke dalam rahang pesaing.

Untuk berhasil mencuri mangsa, setidaknya sepasang predator diperlukan. Satu akan pergi untuk membunuh, dan yang lain akan tetap berada dalam radius penglihatan mangsa, sejauh mungkin membentuk mangsa untuk memaksimalkan pengaruh dan mengarahkan mangsa ke arah pemburu.

Selain itu, setiap perubahan arah akan memungkinkan kompetisi memotong sudut ke arah mangsa, dan menjaga di belakang lawan hanya mungkin jika "goader" cukup dekat dengan mangsa di awal aksi.

Jadi mencuri mangsa hanya memiliki peluang untuk berhasil jika posisi awal dari "pencuri" menguntungkan dan Anda dapat menyimpan setidaknya predator kedua. Dalam pengalaman saya, ini tidak sebanding dengan kompleksitasnya.

Perubahan yang disarankan

Untuk memungkinkan strategi yang lebih kompleks, memindahkan predator di atas kecepatan tertinggi mangsa dapat memiliki biaya dalam poin kelaparan, sebanding dengan kecepatan berlebih. Misalnya, naik ke kecepatan 6 adalah gratis dan setiap titik kecepatan di atas 6 menghabiskan 100 poin kelaparan (pergi ke 6,3 biaya 30 poin kelaparan per putaran, membakar 1000 poin kelaparan akan memungkinkan untuk mencapai kecepatan 16 untuk satu putaran - dan mati jika Anda tidak bisa menangkap mangsa yang melakukannya!).

Daripada memberikan pembunuhan kepada predator acak ketika lebih dari satu cukup dekat untuk memakan mangsa, saya sarankan membagi keuntungan (misalnya 3 predator akan mendapatkan masing-masing 333,33 poin kelaparan). Ini akan memungkinkan lebih banyak strategi endgame intrersting (membayangi predator musuh akan menjadi berguna jika Anda rasa Anda memiliki lebih banyak poin kelaparan, misalnya).

Warna khusus untuk paket pertama agak sulit dilihat. Saya sarankan cyan atau oranye alih-alih biru.


sumber
Akhirnya pesaing lain! Saya sengaja menerapkan setiap poin yang Anda sebutkan kecuali untuk mencuri mangsa, yang saya puas dengan efek samping saat ini. Dari komentar Anda, tampaknya Anda menang melawan waskita saya? Itu menarik, saya akan periksa besok. = D. Anda juga dapat mencoba pembaruan GUI saya untuk melihat grafik yang lebih baik (setidaknya menurut saya).
justhalf
Saya tidak menang sepanjang waktu. Itu tergantung pada siapa yang terdekat dengan mangsa terakhir ketika mereka bertelur. Anjing-anjing saya yang cakap mungkin memiliki keunggulan statistik. Saya juga mengubah mesin simulasi untuk menampilkan masing-masing tim dalam warna yang berbeda, tetapi pada akhirnya terbukti terlalu berwarna untuk selera saya, jadi saya memilih oranye, bukan biru untuk tim 1, dan semua merah lainnya.
Wow, saya baru saja menjalankan kiriman Anda. Itu gila, saya tidak tahu Anda bisa membuat doa tetap seperti itu. Dan saya merasa sedikit tertipu bahwa ketika mangsa tepat berada di ujung, predator saya tidak akan dapat mendeteksi mereka, itu jelas merupakan kerugian besar bagi saya.
justhalf
Itu aritmatika vektor untuk Anda :). Itulah yang saya coba jelaskan dalam posting saya: untuk satu mangsa, Anda tahu semua parameter gerakan (termasuk kecepatan sebelumnya), sehingga Anda dapat menghitung posisi predator yang akan menghasilkan kecepatan mangsa yang sesuai untuk membuatnya berdiri diam di dekat dinding.
1
Nah solusi persamaannya adalah hard-coded (tidak diperlukan iterasi). Pada dasarnya Anda menghitung vektor mangsa harus digunakan untuk kecepatan belokan berikutnya jika predator Anda tidak ada di sana. Mengingat arah yang Anda inginkan mangsa untuk pergi, Anda menyimpulkan perbedaan vektor yang diperlukan untuk membuat titik kecepatan mangsa ke arah itu. Perbedaan vektor ini memberi Anda posisi predator relatif terhadap mangsa. Ini membuat Anda memiliki tingkat kebebasan yang memungkinkan Anda (dalam batas-batas tertentu) memilih jarak dari mangsa.
3

Lazy Pack Haskell

import Control.Monad
import Control.Concurrent

main :: IO ()
main=do
    t<-forkIO $ forever $ (putStrLn "Pack is paralyzed with indecision.\0")
    loop
    killThread t
        where
            loop=do
                line <- getLine
                case line of
                    "dead\0" -> return ()
                    _        -> loop

Anda membutuhkan platform haskell untuk menjalankan ini. Kemudian Anda menggunakan runhaskellperintah untuk menjalankannya. Bungkusan saya menunggu mangsa datang kepada mereka.

PyRulez
sumber
+1 untuk solusi kerangka dalam bahasa baru. Mungkin Anda senang orang-orang membangun strategi baru di atas ini?
trichoplax
Tentu, mengapa tidak. (Meskipun, itu tidak melakukan apa-apa selain menghasilkan output yang konstan dan berhenti pada "mati \ 0", jadi saya tidak yakin apakah itu akan sangat berguna.)
PyRulez
Saya masih akan menyarankan siapa pun yang menjalankan ini untuk menggunakan -silentopsi, meskipun ...
Geobits
3

Bukan entri, saya selalu tertarik untuk menambahkan warna yang disesuaikan untuk setiap entri yang berpartisipasi di ;)

Dan juga proses makan tidak divisualisasikan dengan mengubah warna, tetapi mengubah ukurannya, sehingga kita bisa melihat beberapa acara makan dalam waktu singkat.

Game.java

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.JFrame;

public class Game {

    static int preyStartCount = 0; // 0 means 1500 + (packs * 50)
    static int turn;
    static boolean silent = false;
    long startTime;

    JFrame frame;
    BufferedImage img;
    Color[] colors;

    Island map;
    List<Prey> preys;
    List<Predator> predators;
    List<Pack> packs;
    List<Pack> initPacks;

    public static void main(String[] args) throws InterruptedException {

        Game game = new Game();
        game.init(args);
        if (game.packs.size() > 0){
            game.run();
            game.score();
        }
        game.end();
    }

    void end() {
        frame.setVisible(false);
        frame.dispose();
        for (Pack pack : packs)
            pack.handler.end();
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        } finally {
            for (Pack pack : packs)
                pack.handler.shutdown();
        }

        System.exit(0);
    }

    void score() {
        Collections.sort(initPacks);
        int score = 100;
        initPacks.get(0).score = score;
        for (int i = 1; i < initPacks.size(); i++) {
            Pack pack = initPacks.get(i);
            if (pack.extinctionTurn < initPacks.get(i - 1).extinctionTurn)
                score = score < 1 ? score : score * 80 / 100;
            pack.score = score;
        }
        print("", true);
        print("Done in " + getElapsedTime(), true);
        for (Pack pack : initPacks)
            print(pack.toString() + "\t: Turn " + pack.extinctionTurn + "\t: Score " + pack.score, true);
    }

    String getElapsedTime(){
        double elapsed = (System.currentTimeMillis() - startTime) / 1000d;
        if(elapsed < 60)
            return String.format("%.2f", elapsed) + " seconds";
        elapsed /= 60;
        if(elapsed < 60)
            return String.format("%.2f", elapsed) + " minutes";
        elapsed /= 60;
        return String.format("%.2f", elapsed) + " hours";       
    }


    public Game() {
        initPacks = new ArrayList<Pack>();
        packs = new ArrayList<Pack>();
        preys = new ArrayList<Prey>();
        predators = new ArrayList<Predator>();
    }

    void run() throws InterruptedException {
        frame.setVisible(true);
        turn = 0;
        Graphics2D g = img.createGraphics();

        printStatus();
        while (true) {
            turn++;

            getAllMoves();
            moveAll();
            spawn();
            removeDead();
            shuffle();

            if (turn % 500 == 0)
                printStatus();
            paint(frame, g);
            Thread.sleep(5);
            if (packs.size() < 1)
                break;
        }
    }

    void getAllMoves(){
        for (Prey prey : preys)
            prey.setNextMove();
        for (Pack pack : packs){    
            pack.talk(preys.size(), predators.size(), turn);
        }
        while(true){
            int doneCount = 0;
            for(Pack pack : packs)
                if(pack.doneTalking)
                    doneCount++;
            if(doneCount >= packs.size())
                break;
            try {Thread.sleep(1);}catch(InterruptedException e){}
        }
    }

    void moveAll(){
        for (Creature prey : preys) 
            prey.move();
        for(Pack pack : packs){
            for (Predator predator : pack.members) {
                predator.move();
                predator.eatOrStarve();
            }
        }
    }

    void paint(JFrame frame, Graphics2D g){
        g.setPaint(Color.BLACK);
        g.fillRect(0, 0, img.getWidth(), img.getHeight());

        for(Prey prey : preys)
            prey.paint(g);
        for(Pack pack : packs)
            for(Predator predator : pack.members)
                predator.paint(g);

        frame.repaint();
    }

    List<Prey> deadPreys;
    List<Predator> deadPredators;
    List<Pack> deadPacks;

    void removeDead(){
        deadPreys.clear();
        for (Prey prey : preys)
            if (!prey.alive)
                deadPreys.add(prey);
        preys.removeAll(deadPreys);

        deadPredators.clear();
        for (Predator predator : predators)
            if (!predator.alive)
                deadPredators.add(predator);
        predators.removeAll(deadPredators);

        deadPacks.clear();
        for (Pack pack : packs)
            if (!pack.alive)
                deadPacks.add(pack);
        packs.removeAll(deadPacks);

        for (Pack pack : packs) {
            pack.members.removeAll(deadPredators);
        }

        map.rebuildLists(preys, predators);
    }

    void shuffle(){
        Collections.shuffle(packs);
        for(Pack pack : packs)
            Collections.shuffle(pack.members);
    }

    void spawn(){
        if(turn % 5000 == 0)
            addPredators(1);
        if(turn % 1000 == 0)
            populatePrey(predators.size()-1, false);
    }

    void addPredators(int count){
        for(Pack pack : packs){
            if(!pack.alive)
                continue;
            if(pack.aliveCount == 0)
                continue;
            Predator parent = null;
            for(Predator predator : pack.members)
                if(predator.alive)
                    parent = predator;
            if(parent != null){
                for(int i=0;i<count;i++){
                    XY pos = new XY(Math.random() * 30, Math.random()   * 30);
                    pos.add(parent.pos);
                    pos.setInZeroBounds(Island.SIZE, Island.SIZE);
                    Predator child = new Predator(pack);
                    child.color = colors[pack.id];
                    child.moveTo(pos, Island.getCellByPosition(pos));
                    predators.add(child);
                    pack.members.add(child);
                    pack.aliveCount++;
                }
            }
        }
    }

    Color[] generateColors(int n){
        Color[] result = new Color[n];
        double maxR = -1000;
        double minR = 1000;
        double maxG = -1000;
        double minG = 1000;
        double maxB = -1000;
        double minB = 1000;
        double[][] colors = new double[n][3];
        for(int i=0; i<n; i++){
            double cos = Math.cos(i * 2 * Math.PI / n);
            double sin = Math.sin(i * 2 * Math.PI / n);
            double bright = 1;
            colors[i][0] = bright + sin/0.88;
            colors[i][1] = bright - 0.38*cos - 0.58*sin;
            colors[i][2] = bright + cos/0.49;
            maxR = Math.max(maxR, colors[i][0]);
            minR = Math.min(minR, colors[i][0]);
            maxG = Math.max(maxG, colors[i][1]);
            minG = Math.min(minG, colors[i][1]);
            maxB = Math.max(maxB, colors[i][2]);
            minB = Math.min(minB, colors[i][2]);
        }
        double scaleR = 255/(maxR-minR);
        double scaleG = 255/(maxG-minG);
        double scaleB = 255/(maxB-minB);
        for(int i=0; i<n; i++){
            int R = (int)Math.round(scaleR*(colors[i][0]-minR));
            int G = (int)Math.round(scaleG*(colors[i][1]-minG));
            int B = (int)Math.round(scaleB*(colors[i][2]-minB));
            result[i] = new Color(R,G,B);
        }
        return result;
    }

    void populatePredators(String[] args) {
        int start = 0;
        if(args[0].equals("-silent")){
            silent = true;
            start = 1;
        }

        colors = generateColors(args.length-start);
        if(colors.length==1){
            colors[0] = Color.BLUE;
        }

        for (int i = start; i < args.length; i++) {
            Pack pack = new Pack(args[i]);
            if (pack.handler.init()) {
                packs.add(pack);
                initPacks.add(pack);
            }
        }
        Collections.shuffle(packs);
        XY[] positions = map.getPackStartLocations(packs.size());
        XY offset = new XY(-15, -15);
        for(int i=0;i<packs.size();i++){
            Pack pack = packs.get(i);
            for (Predator predator : pack.members) {
                predator.color = colors[pack.id];
                XY pos = new XY(Math.random() * 30, Math.random()   * 30);
                pos.add(positions[i]);
                pos.add(offset);
                pos.setInZeroBounds(Island.SIZE, Island.SIZE);
                predator.moveTo(pos, Island.getCellByPosition(pos));
                predators.add(predator);
            }
        }
        deadPredators = new ArrayList<Predator>(predators.size());
        deadPacks = new ArrayList<Pack>(packs.size());
    }

    void populatePrey(int count, boolean center) {
        XY pos = new XY();
        for (int i = 0; i < count; i++) {
            Prey prey = new Prey();
            if(center){
                pos.x = Math.random() * 100 + 200;
                pos.y = Math.random() * 100 + 200;
            } else {
                pos.x = Math.random() * 500;
                pos.y = Math.random() * 500;
            }

            prey.moveTo(pos, Island.getCellByPosition(pos));
            preys.add(prey);
        }
        deadPreys = new ArrayList<Prey>(preys.size());
    }

    static void print(String txt){
        print(txt, false);
    }

    static void print(String txt, boolean override){
        if(!silent || override)
            System.out.println(txt);
    }

    void printStatus(){
        print("Turn " + turn + " : Prey " + preys.size()
                + " : Predators " + predators.size() + " (" + getElapsedTime() + " elapsed)");
    }

    @SuppressWarnings("serial")
    void init(String[] args) {
        startTime = System.currentTimeMillis();
        map = new Island();
        populatePredators(args);
        if (preyStartCount == 0)
            preyStartCount = 1500 + (packs.size() * 50);

        populatePrey(preyStartCount, true);
        map.rebuildLists(preys, predators);
        img = new BufferedImage(Island.SIZE, Island.SIZE, 1);
        frame = new JFrame() {
            @Override
            public void paint(Graphics g) {
                g.drawImage(img, 32, 32, null);
            }
        };
        frame.setSize(Island.SIZE+64, Island.SIZE+64);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                for (Pack pack : packs)
                    pack.handler.end();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                } finally {
                    for (Pack pack : packs)
                        pack.handler.shutdown();
                }
            }
        });
    }
}

Predator.java

import java.awt.Graphics2D;


public class Predator extends Creature {

    static int count = 0;

    int id;
    int hunger;
    Pack pack;

    public Prey eatOrStarve() {
        for (Prey prey : preys) {
            if (prey.alive && pos.isCloserThan(prey.pos, eatDist)) {
                prey.die();
                hunger = MAX_HUNGER;
                return prey;
            }
        }
        if (hunger-- < 1)
            die();
        return null;
    }

    @Override
    public void die() {
        super.die();
        pack.aliveCount--;
        Game.print(pack.toString() + " starved! " + pack.aliveCount + " members remaining.");
    }

    @Override
    void paint(Graphics2D g){
        g.setPaint(color);
        int size = ((hunger + 10) > MAX_HUNGER && Game.turn > 10) ? 3+(int)Math.pow((hunger+10-MAX_HUNGER)/4,3) : 3;
        g.drawOval((int)pos.x - 1, (int)pos.y - 1, size, size);
        g.fillOval((int)pos.x - 1, (int)pos.y - 1, size, size);
    }

    Predator(Pack pack) {
        super();
        id = count++;
        this.pack = pack;
        MAX_SPEED = 6.1;
        VISIBLE = 50;
        hunger = MAX_HUNGER;
    }

    final double eatDist = 1;
    final static int MAX_HUNGER = 1000;
}
justhalf
sumber