Hunger Gaming: Eat or Die


60

Hunger Gaming: Eat or Die

Se non mangi, muori. Se mangi, vivi (fino alla morte). Si dovrà morire, in modo da provare a morire scorso.

Panoramica

C'è un'isola popolata da un branco di animali da preda. Controlli un pacchetto di cinque predatori. Il tuo obiettivo è mantenere vivo il tuo branco. Fallo mangiando una preda. Le prede tendono a scappare dai predatori, altrimenti cercano di rimanere in uno stormo. Ovviamente, il tuo pacco sarà sullo stesso campo di ogni altro pacco , quindi la competizione cercherà di mangiarli prima che tu possa farlo. Non lasciarti scoraggiare, altrimenti morirai di fame.

Come giocare

Crea e invia un programma da riga di comando per dirigere il tuo pacchetto. Riceverà informazioni sullo stato dal programma di controllo su STDIN e emetterà comandi su STDOUT. Il formato è delineato in dettaglio di seguito. Ogni programma verrà eseguito solo una volta e deve rimanere in esecuzione fino a quando non ha più membri del pacchetto in vita. Dovrai leggere l'input appena arriva e rispondere rapidamente. C'è un timeout rigoroso di 200 ms per ogni risposta. Se non hai ancora risposto, il tuo pacco non riceverà nuove istruzioni per il turno in corso.

Se il programma non può essere eseguito dal controller, non sarà considerato valido. Includere la stringa della riga di comando che dovrò utilizzare per eseguire l'invio. Se sono presenti istruzioni speciali (per impostare compilatori, ecc.), Includerle. Se non riesco a farlo funzionare, ti chiederò assistenza nei commenti. Se non rispondi, non sarò in grado di accettare la tua richiesta.

Il torneo si svolgerà su un sistema Linux a 64 bit. Tienilo a mente quando fornisci le indicazioni necessarie.

Dettagli

  • La posizione e la direzione di ogni creatura hanno la forma di una coppia di numeri in virgola mobile a precisione doppia (ad es. double) Che rappresentano rispettivamente le loro xe ycoordinate.

  • Ogni creatura è considerata un punto. Ciò significa che possono sovrapporsi e occupare lo stesso spazio. Non sarai urtato da parte, e non vi è alcun concetto di collisione con altre creature.

  • L'isola è una piazza, 500 unità su un lato. Se provi ad avventurarti oltre quei limiti, verrai bloccato sul bordo. L'origine {0,0}è in alto a sinistra, con l' xaumento verso destra e yverso il basso. Ancora una volta, la mappa non si avvolge .

  • Il gioco inizia con 1500 + (packCount * 50) animali da preda. Saranno riuniti al centro dell'isola, ma presto decideranno di iniziare a muoversi.

  • I pacchetti saranno disposti in un cerchio distribuito uniformemente attorno al perimetro. L'ordine del pacchetto viene mischiato, quindi non contare sull'avvio in una posizione particolare.

  • Gli animali da preda possono vedere tutti gli altri animali entro un raggio di 30 unità. Possono muoversi ad un massimo di 6,0 unità per turno.

  • I predatori possono vedere tutti gli altri animali entro un raggio di 50 unità. Possono muoversi ad un massimo di 6,1 unità per turno. Ciò significa che possono vedere la preda prima di essere visti e (a malapena) superarli.

  • I predatori vivono e muoiono in base al loro livello di fame . Inizia da 1000 e diminuisce di uno ogni turno. Se, dopo il movimento, un predatore si trova entro 1 unità di preda, lo mangerà automaticamente. Questo rimuove la preda e imposta la fame del predatore su 1000. Ogni predatore può mangiare solo una preda per turno. Se ce ne sono più di uno nel raggio d'azione, mangerà quello che il ciclo raggiunge per primo (non necessariamente il più vicino). Un predatore muore se la sua fame raggiunge lo zero.

  • I pacchetti iniziano con cinque membri ciascuno. Ogni 5000 turni, tutti i pacchetti ancora in gioco genereranno un nuovo membro. Verrà inserito nel raggio visibile di un membro del pacchetto. Assicurati che le tue voci possano gestire più di cinque membri.

  • Ogni 1000 turni, si generano più prede. Il numero di nuove prede sarà il numero di predatori viventi meno uno.

  • I predatori non possono attaccare altri predatori. Mangiano prede quando la prendono. Questo è tutto.

  • L'ordine in un turno è:

    • Tutte le prede prendono decisioni
    • Tutti i predatori prendono decisioni
    • Tutte le prede si muovono
    • Tutti i predatori si muovono / mangiano
  • L'ordine in cui ogni pacchetto prende le proprie decisioni / mosse sarà randomizzato ad ogni turno.

Protocollo (generale)

Tutte le comunicazioni vengono eseguite in formato stringa US-ASCII. I numeri vengono convertiti in stringhe utilizzando Java Double.toString()o Integer.toString(). L'output deve essere formattato in modo che possa essere letto da Java Double.valueOf(String)(non verranno emessi numeri interi). Per dettagli sui formati analizzabili, consultare la documentazione perDouble . Tutti i campi su una linea sono separati dal \tcarattere standard e le nuove linee lo sono \n. L'intera stringa verrà terminata con un byte nullo \0.

Negli esempi seguenti, sto usando <>per contrassegnare i campi per motivi di leggibilità. Questi non sono presenti nelle stringhe effettive.

Protocollo (input)

La stringa di input varia in lunghezza, a seconda di quante creature sono visibili al tuo branco. Può superare i 100k caratteri, quindi preparati. L'impostazione di base è:

  • Linea 0: informazioni di base sul gioco. turnè il numero di turno corrente e i conteggi sono il numero totale di prede e predatori rimasti sul campo. Questi sono integerin forma di stringa.

    <turn>\t<preyCount>\t<predatorCount>\n
    
  • Riga 1: ID univoci e livelli di fame dei membri del tuo pacchetto. Questi non sono indicati nello stesso ordine per ogni input. Utilizzare gli ID univoci per tenere traccia dei singoli membri, non nell'ordine in cui vengono visualizzati nell'input. Ancora una volta, questi sono integercome stringhe. Per un pacchetto di due, questo sarebbe:

    <id[0]>\t<hunger[0]>\t<id[1]>\t<hunger[1]>\n
    
  • Riga 2: posizioni dei membri del tuo pacchetto, nello stesso ordine indicato nella riga 1 . Questi sono doublecome stringa:

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

Le seguenti righe rappresentano la visibilità di ciascun membro del pacchetto, nello stesso ordine indicato nella riga 1 . Questi saranno indicati come due righe per membro.

Il primo per ciascuno è costituito da posizioni per la preda che può vedere. Il secondo è posizioni per i predatori che può vedere. Queste posizioni non sono uniche nel loro insieme. Ad esempio, se due membri del branco possono vedere lo stesso animale, sarà nella stringa di entrambi i membri. Inoltre, saranno inclusi i membri del tuo pacchetto personale . Se si desidera escluderli, è possibile che si desideri confrontare le posizioni con i propri membri. Tutte le posizioni sono in doubleformato stringa.

Per ogni membro vivente:

<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

Infine, l'ultimo personaggio sarà \0, all'inizio della riga successiva.

Eccezione: se ricevi l'input dead\0, il tuo pacchetto è morto. Termina il tuo programma con grazia, per favore. Il controller dovrebbe chiudere tutti i processi viventi quando è chiuso, ma preferirei non avere processi di zombi ovunque. A titolo di cortesia, è possibile includere un timeout di input. Ad esempio, la mia classe di esempio termina se non riceve input per 15 secondi.

Protocollo (uscita)

L'output è semplice. Darai una coppia di doublevalori per ciascun membro del live pack. Questi rappresentano il movimento che vorresti che intraprendessero in questo turno. Ad esempio, se la tua creatura è attualmente in posizione {100.0, 100.0}e tu dai loro un comando di {-1.0, 1.0}, si sposteranno in {99.0, 101.0}. Tutti i numeri saranno su una sola riga, separati da tab.

Ad esempio, se avessi 3 membri del pacchetto in vita, questa sarebbe una risposta valida:

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

Questo sarebbe spostare le tue creature dai {1.0,-1.0}, {2.0,-2.0}e {3.0,-3.0}. L'ordine è uguale a quello ricevuto nell'input. Non dimenticare il finale \0!

Se si fornisce un input non valido, seguiranno risultati negativi. Se un singolo numero non può essere analizzato in a double, diventerà zero. Se la stringa nel suo insieme non può essere analizzata, non verranno fornite nuove istruzioni e l'intero pacchetto utilizzerà le indicazioni della svolta precedente.

Tutte le direzioni saranno bloccate ad una distanza massima di 6,1 unità. Puoi muoverti più lentamente di questo, se lo desideri. Ad esempio, {1, 0}ti sposterà di un'unità. {6,8}(distanza 10) ti sposta solo di 6,1 unità e si riduce a circa {3.66, 4.88}. La direzione rimane costante.

Importante: il programma di controllo legge sia STDOUT che STDERR. Se si genera un'eccezione e si stampa su STDERR, è molto improbabile che il messaggio abbia la forma di una risposta valida. Cerca di evitare di farlo.

Programma di controllo / Test

La fonte per il controller può essere trovata qui su bitbucket.org . Dovrai compilarlo prima di eseguirlo. La classe principale è Gamee tutte le classi sono nel pacchetto predefinito. Per eseguire, includere il comando di ogni pacchetto come argomento separato. Ad esempio, se si desidera eseguire un Java ChaserPack e un Python LazyPack.py, è possibile utilizzare:

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

Sulla mappa, la preda appare in verde e i predatori in rosso. Tuttavia, qualunque pacchetto sia il primo pacchetto dato come argomento verrà invece colorato in blu. Questo ha lo scopo di distinguerli più facilmente a scopo di test. I predatori lampeggeranno anche in bianco per cinque fotogrammi quando mangiano.

Il gioco continuerà fino a quando l'ultimo predatore muore di fame, scrivendo alla console quando si verificano eventi di fame o estinzione. Una volta completato il gioco, il punteggio verrà assegnato per ogni pacchetto. Se vuoi non voler vedere gli eventi di fame / estinzione, puoi usare l' -silentargomento. Quindi emetterà solo il punteggio finale. Devi passare questo come primo argomento :

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

È incluso un pacchetto scheletro Java chiamato GenericPack. Include le operazioni di base input / output necessarie. È lì per dare un chiaro esempio di come analizzare e rispondere. Se desideri aggiungere un modello in un'altra lingua, fammi sapere.

Inoltre è incluso un predatore basato sul modello, ChaserPack. Non sarà incluso nel torneo ed è incluso solo a scopo di test. Si comporta abbastanza male, a causa di un difetto di targeting intenzionale. Se non puoi batterlo, continua a provare.

Di seguito è riportato un esempio del programma di controllo (fare clic per il video). La qualità del video non è eccezionale (mi dispiace), ma puoi avere un'idea di come si muovono le prede. ( attenzione: audio )

immagine dello schermo

punteggio

Il vincitore sarà determinato dal torneo, guadagnando punti in ogni round.

Ogni round procede fino alla morte di tutti i predatori. Ogni pacchetto verrà valutato in base alla morte del suo ultimo membro per fame. Verranno quindi assegnati punti in base all'ordine. I punti si accumulano per dieci round e il vincitore è il gruppo con il punteggio totale più alto.

Il primo posto per ogni round riceverà 100 punti. Per ogni luogo successivo, la ricompensa sarà ridotta del 20% (arrotondata per difetto). Questo continuerà fino a quando i punti raggiungono lo zero (dopo il 17 ° posto). I posti 18+ non riceveranno punti per il round. I pacchetti che legano riceveranno punti uguali. Per esempio:

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

Il punteggio massimo possibile nel corso del torneo è 1000, dal primo posto tutte e dieci le volte.

Se più programmi terminano il torneo in parità per il primo posto, si terrà un altro torneo da dieci round con solo le iscrizioni al primo posto presentate. Questo continuerà fino a quando non emergerà un vincitore.

Proverò a organizzare un torneo all'incirca settimanalmente o quando arrivano nuovi invii.

Regole aggiuntive (gioca bene!)

  • Non è possibile leggere o scrivere su risorse esterne. Dato che non invocherai più volte il tuo programma, tutte le informazioni sullo stato possono essere memorizzate internamente.

  • Non interferire con altri processi / invii. Ciò non significa che non provi a rubare le loro prede, a superarle, ecc. Significa non interferire con l'esecuzione del processo. Questo è a mia discrezione.

  • I concorrenti sono limitati a un massimo di tre iscrizioni. Se invii di più, segnerò solo i primi tre inviati. Se si desidera revocarne uno, eliminarlo.

  • Le voci non possono esistere esclusivamente per sostenere altre voci. Ognuno dovrebbe giocare per vincere per il proprio merito.

  • Il tuo programma può generare un massimo di un processo figlio alla volta ( discendenti totali , non diretti). Ad ogni modo, assicurati di non andare oltre il timeout. Non puoi invocare la Gameclasse stessa in alcun modo.

Risultati - 29 aprile 2014

Ecco i risultati dell'ultimo torneo di dieci round:

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

I pacchetti inviati prima delle 09:00 EDT 2014/04/29 sono inclusi in questa corsa.

Puoi anche visualizzare i dettagli per ogni round . Per qualche motivo ho deciso di numerare i round all'indietro, quindi inizia con "round 10".

aggiornamenti

23/04/2014: FGreg ha segnalato un bug relativo ai timeout (grazie!). È stata implementata una correzione, quindi i tester vorranno aggiornare il loro codice del programma di controllo.


28
Mi piacciono queste domande sul re della collina!
Cruncher,

2
@Manu Ho scritto i robot di esempio su Windows 7 e testato su Win e Linux. Quali problemi stai riscontrando con loro?
Geobits

2
Queste domande sul re della collina sono piuttosto fantastiche, e questa è decisamente interessante. Ora ho due diversi pacchetti in lavorazione!
mackthehobbit,

2
@githubphagocyte Non voglio davvero uccidere un pacchetto al primo timeout, semplicemente perché ho visto timeout anche programmi semplici ogni 40k + turni o simili. Ho commesso la modifica della denominazione nel controller. I turni sono ora noti come turni in tutto il programma, a meno che non ne abbia perso uno da qualche parte.
Geobits il

2
@Geobits eh, va bene per me. Sai, sembra molto simile a un progetto di ricerca che sta facendo uno dei miei professori di fisica, con il quale potrei essere d'aiuto durante l'estate. Spiegherò un po 'più avanti se posso.
krs013,

Risposte:


10

Chiaroveggente

Codice aggiornato per affrontare AbleDogs

Woo hoo! Finalmente batte quel Netcats! Ho ampliato il codice esistente (crediti a Geobits!) Con alcune piccole modifiche per creare questo pacchetto di previsioni future. Niente batte i predatori che sanno dove si sposteranno le prede!

Da due test che ho fatto, il mio gruppo ha sempre vinto contro Netcats. Ma questo non funzionerà altrettanto bene se non ci sono altri pacchetti, poiché la previsione fallisce ancora se ci sono troppe altre prede nelle vicinanze.

Probabilmente posso includere il trucco di CivilizedBeasts per ridurre sostanzialmente il numero di prede durante le prime migliaia di turni.

Fatto in 5.21 minuti
Chiaroveggente (1): Turn 9270: Punteggio 100
EcoCamel.pl (3): Turn 8118: Score 80
Netcats (0): Turn 6111: Score 64
RubyVultures.rb (5): Turn 4249: Score 51
RubySpiders.rb (4): Turn 3495: Score 40
CivilizedBeasts (2): Turn 3176: Score 32
ChaserPack (6): Turn 2492: Score 25

Dal nome del mio pacchetto, dovresti sapere quale strategia utilizzo = D

Modifica :

  • Sistema di gestione dei pacchetti aggiornato per non inseguire la stessa preda (e anche cercare di trovare la migliore corrispondenza!)
  • Migliora il processo errante quando il numero di prede è ridotto (questo è fondamentale per una vittoria!).
  • Migliora casi speciali quando la versione precedente è rimasta bloccata all'angolo.
  • Risolto un bug nell'algoritmo di rilevamento dei predatori (ora è abbastanza preciso!)
  • flock[ALIGN]Fattore di preda incluso
  • Tieni una preda come animale domestico se il cibo è scarso
  • Crea una tana in cui il branco radunerà le sue prede
  • Attira il predatore vicino a inseguire la nostra preda, che non vincerà

Ho contato quante prede mangia ogni pacchetto, ed ecco il risultato:

Chiaroveggente (1) ha consumato 916 prede in 9270 turni (0,099 prede / turno)
EcoCamel.pl (3) ha consumato 73 prede in 8118 turni (0,009 prede / turno)
Netcats (0) ha consumato 563 prede in 6111 turni (0,092 prede / turno)
RubyVultures.rb (5) ha consumato 77 prede in 4249 turni (0,018 prede / turno)
RubySpiders.rb (4) ha consumato 293 prede in 3495 turni (0,084 prede / turno)
CivilizedBeasts (2) ha consumato 10 prede in 3176 turni (0,003 prede / turno)
ChaserPack (6) ha consumato 43 prede in 2492 turni (0,017 prede / turno)

Il mio branco è molto aggressivo e penso che la maggior parte dei 916 conti derivi dal rubare le prede da Netcats, proprio come RubySpiders.

Purtroppo CivilizedBeasts sta perdendo a causa del cammello centrale di EcoCamel.

E EcoCamel (con fame 500 importante) è abbastanza efficiente, mangia abbastanza per sopravvivere fino alla fine.

Anche con questo chiaroveggente aggiornato, il gioco raggiunge a malapena 10.000 turni.

Il codice:

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

1
Sembra molto buono, il tuo è davvero meglio di netcats nel mio gioco. Ma odio il fatto che non posso gestire gli altri predatori poiché le mie bestie fanno davvero un brutto lavoro nelle tue statistiche (mentre evilcamel fa troppo bene). Forse devo provare a installare un compilatore perl o giù di lì.
Herjan,

Sì, penso che il tuo metodo non funzioni se c'è un predatore nel mezzo, come discusso nella tua risposta. Ho provato a implementare un'altra versione che si comporta in modo simile alla tua. Può cambiare la formazione in base al numero di predatori disponibili, quindi è abbastanza divertente da guardare, anche se non molto meglio del tuo.
solo

Sì, la mia strategia può essere aggiornata in molti modi come altre formazioni con un altro importo con i membri perché le mie bestie sono condannate con <4 predatori. O luoghi casuali da raccogliere (anziché solo al centro), ad esempio. Ma sono troppo pigro per implementarlo (ora). E non sarà mai buono come questo dal momento che se la preda si abbassa la mia tattica non funziona. Questo è quando hai bisogno di una bestia come la tua (hai già menzionato per iniziare con la mia tattica e quando la preda si abbassa per usare questa tattica). Quindi immagino tu l'abbia già pensato.
Herjan,

Sto affrontando un'altra sfida in questo momento, e GeoBits sembra aver perso interesse per questo, quindi lo lascerò in sospeso per un po 'a meno che i risultati non vengano aggiornati. Ho idee per un paio di altri contributi, quindi spero che questa sfida sarà mantenuta viva. Daremo un'occhiata al tuo aggiornamento, ovviamente.

15

Netcats

Ecco un pacchetto per iniziare ragazzi. Estende la GenericPackclasse inclusa con il programma di controllo. È stato migliorato dalla pubblicazione originale e non muore più di fame con una mandria sparsa.

Netcats usa una formazione a rete a forma di V per intrappolare le prede nell'angolo, dove possono mangiarle nel tempo libero. La rete è formata con un elemento "testa" al centro. Una volta che la testa mangia, scambia i posti con il membro del branco più affamato, dal momento che la testa è normalmente la prima ad avere l'opportunità di mangiare.

La rete inizia piuttosto piccola, ma si allarga quando la mandria si restringe per poter pescare il campo in modo più efficiente.

Se non sono visibili prede, la formazione si allarga in un modello di ricerca ingenuo che copre gran parte dell'isola.

Una volta che il pacchetto si riduce a due membri, la rete non funziona. A quel punto ognuno va per la propria strada, mangiando avidamente la cosa più vicina che può trovare e facendo una passeggiata semi-casuale altrimenti.

Questa versione sopravvive molto meglio degli ingenui Netcats visti nel video collegato alla domanda.

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

11

Ragni rubini

Poichè a volte di meno è di più e molte soluzioni probabilmente proverebbero comunque ad angolare la preda ...

Pensavo che il mio branco potesse semplicemente dividersi e aspettare che gli altri facessero il lavoro.

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

Avvertenza: in realtà non rimane in esecuzione, né legge input quando arriva né risponde rapidamente. Tuttavia, poiché funziona bene con il controller spero che si qualifichi senza ulteriori aggiustamenti.


4
+1 prima soluzione parassita. Penso che questo tipo di risposta aumenterà la qualità di altre risposte eliminando gradualmente le lacune ...
trichoplax,

@githubphagocyte Avevo in mente un parassita più intelligente, ma questo è più efficiente in termini di tempo di vita / linee di codice. Spero di trovare il tempo per implementarlo.
Legat,

Forse @Synthetica sta codificando la mia idea in questo momento. O se la sua idea è ancora un'altra, potremmo presto avere più parassiti che cacciatori;)
Legat,

1
@githubphagocyte ci è permesso di fare tre voci, quindi posterò un altro pacchetto una volta pronto. Tuttavia, trovo interessante che questo sia stato codificato nel frattempo e potrebbe rivelarsi più efficace. Sfrutta davvero bene Netcats e in realtà sopravvive al mio primo pacchetto di cacciatori.
Legat,

3
Questo può entrare così com'è, anche se mi ci è voluto un secondo per capire il perché. Sembra fare di meglio più Netcat aggiungi (il che ha senso). +1 da me, vediamo che tipo di cacciatori escono per evitare gli angoli :)
Geobits

11

CivilizedBeasts

Finalmente, è tempo di sfoggiare le mie bestie!

La mia razza pensa che la caccia sia un po 'primitiva, quindi lavorano insieme in una squadra di 4 e quindi abbandonano il loro 5 ° alleato, perché: meno predatori = più prede per se stessi. Ciò che sostanzialmente fanno è ciò che fanno gli umani, catturano le prede e si prendono cura del loro bestiame;)

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

Diventa abbastanza difficile per il mio seno sopravvivere con meno di 200 prede a turno + -12.000 con solo Netcats nemici nel gioco. Sarai felice con questa razza dal momento che divora davvero enormi quantità di prede con velocità come nessun'altra razza può mai (non che i grandi e veloci massacri garantiscano la vittoria, ma influenza il (lungo) tempo che un intero round impiega considerevolmente).


3
Se per " prenditi cura di loro " intendi " mandarli ripetutamente a metà e massacrarli / mangiarli ", allora sì, lo fanno bene. +1
Geobits il

È divertente, con la versione non mutata (originale) di Evil Camels, la tattica civilizzata è totalmente inefficiente a causa del "cammello centrale".
user2846289

1
@VadimR Crap, grazie per aver aggiornato il tuo cammello: PI non posso provarlo dal momento che non è Java ma so che la mia strategia è un po 'inutile con i predatori nel mezzo del mio territorio: P
Herjan

5
È di nuovo Herjan! Inoltre "Diventa abbastanza difficile per il mio seno sopravvivere con meno di 200 prede" (enfasi aggiunta). Non mi rendevo conto che la vitalità del tuo seno dipende dal numero di prede in una simulazione al computer ....
Justin,

5

Ruby Vultures

Ecco che arriva un branco di parassiti più attivi . Stanno cercando di circondare il predatore in movimento più vicino , in modo da poter rubare la sua preda . Sono un po 'dipendenti dalla fortuna in quanto non hanno un modo intelligente di scegliere chi seguire, ma di solito battono inseguitori e talvolta ragni .

Non sono ancora finiti, dato che l'ho pubblicato per spingere il tempo :)

Spero di:

  • costringili a cercare predatori al di fuori del campo visivo
  • prendere in considerazione la preda - spesso una di esse è tra un altro branco e la preda!
  • inizia a ruotarli per evitare di morire di fame quando tutti gli altri sono ben nutriti

22 aprile 2014: aggiunta la noia , che li rende meno appiccicosi e consente loro di cacciare le prede da soli e cercare predatori

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

Hai sicuramente bisogno di più "cacciatori" nel mix. Così com'è, questi tendono ad attaccarsi ad altri parassiti (poiché questa è la maggioranza sul campo). Mi piace guardarli, però, e vedere come sarebbero efficaci con un diverso mix di concorrenti.
Geobits il

Oh sì, nel mio ambiente di test ho altri due pacchetti di cacciatori. Senza di loro gli avvoltoi sono probabilmente del tutto all'oscuro. Soprattutto che i netcat possono lavorare rapidamente gli angoli senza essere visti dal centro.
Legat,

Penso, so cosa potrebbe essere preoccupante in particolare. Danza di guerra dei cammelli malvagi. @Geobits che ne dici di mettere i combattimenti su Youtube? 10 round non è troppo per rimanere guardabili. Certo, sarebbe necessario il quartier generale. Non mi aspetto milioni di spettatori, ma sarebbe divertente vedere come si comportano i tuoi branchi e magari tifare per loro un po ':)
Legat

1
L'intero torneo potrebbe essere un po 'lungo (~ 8 minuti per round ora) per attirare l'attenzione, ma la registrazione di un round "spettatore" potrebbe funzionare. Ci penserò su piste future.
Geobits il

@Geobits La velocità varia molto durante un round di 8 minuti? Mi chiedo se valga la pena registrare un fotogramma per turno in modo che possano essere riprodotti a una velocità costante, piuttosto che rallentare durante le parti ad alta intensità di calcolo. Ai fini di YouTube, intendo.
trichoplax,

5

Cammelli ecologici malvagi

Modifica: Mutazione # 2. Oh, no, sono in ritardo con la mia implementazione della previsione del movimento delle prede, per essere il primo a battere i Netcats. OK, così sia.

Questa mutazione ha $hunger_criticalvariabile (costante). Modificandolo su un valore superiore a 1000, Cammelli caccia sempre, come i Chiaroveggenti. Poi:

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

Se $hunger_criticalè impostato, ad esempio, su 500 (come di seguito), i miei Cammelli (dopo aver visto gli orrori della civiltà ) cercano di comportarsi in modo ecologico (quindi hanno cambiato il nome della loro razza), cioè uccidono solo quando hanno fame. Se non hanno fame, pattugliano le aree critiche dell'isola - al centro e agli angoli, per evitare inutili macellazioni da parte di altri cacciatori. Bene, con il centro, funziona più o meno. L'idea di girarsi negli angoli era di scacciare la preda e rendere la vita più difficile ai gatti e ai parassiti. Beh, non funziona. Stupide prede vanno comunque negli angoli.

È interessante, inoltre, che il flock[ALIGN]componente può essere indovinato solo dai predatori e la mia implementazione è diversa da quella di justhalf. Temo che ci sia un bug minore nel mio rip-off implementazione di codice Geobits', guardare / confrontando la caccia individuale di Camel vs chiaroveggenti.

E il programma è un po 'lungo, mi dispiace.


Modifica: Mutazione n. 1. L'Isola risulta essere piuttosto radioattiva (ciò spiega la mancanza di vegetazione e la natura inspiegabile delle creature 'preda'), quindi ecco la prima mutazione dei miei Cammelli. Ognuno di loro può diventare cacciatore da solo, se ha fame o se non c'è un angolo libero per tutti. Hunter cerca di inseguire attivamente le prede vicine. Se non ce n'è, pattuglia il cerchio intorno al centro dell'isola, quindi insegue la creatura più vicina quando la trova. Sfortunatamente, la direzione della preda diventa imprevedibile quando è vicino al suo sciame (vale la pena investigare ...), quindi l'inseguimento da solista non è molto efficiente. Ma se ci riesce, il Camel va a digerire nell'angolo libero più vicino (se presente). Quando il livello della fame è al di sotto di un certo livello, ogni Cammello abbandona il suo angolo (probabilmente maledicendo Netcats ("dov'è il cibo?" )) e passa da solo il roaming gratuito. E così via.


Lo stesso scherzo raccontato due volte non è divertente, ma (1) ho dovuto iniziare da qualche parte e sono nuovo di queste cose, (2) onesto, ho pensato alle tattiche d'angolo (e chi non l'ha fatto?), Guardando Netcats, prima di Ruby I ragni apparvero sull'isola.

Quindi, hai mai sentito parlare dei cammelli carnivori? Poveri animali si sono svegliati un giorno su questa isola abbandonata da Dio per non trovare erba né alberi, ma un sacco di strane piccole cose verdi anche se commestibili (abbastanza fastidiose). Non avendo abitudini di caccia (ma presto muteranno, spero), i miei Cammelli hanno sviluppato uno schema molto malvagio per sopravvivere: si dividono e vanno ciascuno in 1 di 4 angoli, e il 5 ° va al centro (per morire lì prima, come si scopre). Sulle loro destinazioni attendono pazientemente, esibendosi in una specie di danza di guerra sui cammelli, o forse cercano solo di non calpestare altri animali già lì, ragni e tutto il resto ...

#!/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__

5
Questo deve essere lo script perl più leggibile che abbia mai visto su questo sito.
Geobits il

Devi andare esattamente all'angolo per scacciarli efficacemente, altrimenti parteciperai effettivamente alla macelleria di Netcats, ahah
solo il

@justhalf, è come ho detto: il piano non ha funzionato. Neanche i parassiti seduti negli angoli hanno scacciato la preda. Hm-m, forse 2 o più bestie che pattugliano un angolo aiuteranno.
user2846289

I tuoi cammelli sono abbastanza buoni in realtà! Per fortuna (per me) ho migliorato i miei chiaroveggenti, quindi il più delle volte (non sempre), il mio branco vince contro i tuoi durante l'ultima battaglia. Interessante!
solo

1
Se sei più vicino di 8 (20-2 * 6) unità dalla preda, possiamo vedere qualsiasi movimento di tutte le altre prede che si trovano entro 30 unità dalla nostra preda nel turno in corso. E la vecproprietà è fondamentalmente solo lo spostamento dalla svolta precedente alla svolta corrente. E come ho detto, facciamo corrispondenze dal turno precedente per scoprire quale preda va da che parte, non possiamo fare affidamento sull'ordine delle prede. Ciò è possibile perché le prede di solito (nello scenario tipico) mantengono una distanza sufficiente l'una dall'altra (> 12 unità), quindi la maggior parte delle volte possiamo abbinare le prede nel turno precedente al turno corrente.
solo il

4

AbleDogs - PHP

Questi simpatici cani hanno imparato a mordere i polpacci di una preda per spingerla lungo i muri. Amano anche vagare per il pascolo alla ricerca di nuove prede. Infine, è stato insegnato loro ad astenersi dal mangiare a meno che non abbiano davvero bisogno delle calorie.

Inserisci il codice in un AbleDogsfile ed eseguilophp 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();
}
?>

Considerazioni generali

  • È il fine gioco che conta. Puoi avere l'algoritmo di caccia più intelligente di sempre, se non riesci a individuare e catturare le ultime poche prede più velocemente dell'opposizione, perdi.

  • Se i tuoi predatori non riescono a catturare una preda da sola (o almeno in coppia), brinderai non appena la densità della preda diminuirà abbastanza da fare affidamento sulla fortuna cieca o sul bloccare le prede negli angoli.

  • Un predittore del movimento delle prede è sostanzialmente obbligatorio. Non riesco a immaginare di battere un programma basato su predittori senza avere il tuo predittore.

Inseguimento della coda

Il modo più inefficace per catturare una preda è inseguirla. Supponendo che un singolo predatore insegua una singola preda e nessuna influenza esterna (muri, altre prede, ecc.), Un inseguimento della coda potrebbe durare per sempre. Non appena si entra nel raggio di visione della preda di 30 unità, la preda fugge come velocità 6 per la tua 6.1, quindi guadagni 1 distanza per turno: in linea retta, avrai bisogno di circa 300 giri per ottenerla.

Tenendo conto delle dimensioni dell'arena, una preda viaggerà al massimo della diagonale di un quadrato di 500 unità prima di colpire un muro o un angolo, il che richiederà al massimo 117 turni.

La strategia vincente è ovviamente quella di trovare un modo per rallentare le prede, ovvero avendo un altro predatore o un muro / angolo di fronte.

Predictor

Con una velocità della preda di 6, una preda può spostarsi su un'area di 36 * unità al quadrato. Con un raggio di cattura di 1, un'ipotesi cieca su dove sarà la prossima preda ha una probabilità di 1/36 * pi (circa l'1%) di avere successo. Chiaramente bisogna fare qualcosa per migliorarlo!

Guardando il codice del motore di simulazione, puoi vedere che gli input sono:

  • posizioni visibili di prede e predatori
  • velocità di prede precedenti

Mentre tutte le posizioni sono disponibili, le precedenti velocità di prede non lo sono. L'unico modo per calcolare queste velocità sarebbe quello di tracciare ogni singola preda da una svolta all'altra, il che è quasi impossibile da fare (a meno che non si implementi un algoritmo di tracciamento del movimento molto intelligente). Quindi un predittore può facilmente riprodurre tutti i termini del calcolo, ad eccezione del contributo di velocità che deve essere indovinato.

Nel caso di una singola preda, la velocità può essere monitorata senza troppi problemi, il che consente di costruire un predittore "perfetto" per catturare una preda isolata dal gregge. Questo è praticamente tutto ciò di cui hai bisogno per il gioco finale, quando le prede sono troppo poche per interagire tra loro. Quando le prede sono abbondanti e l'effetto flock è abbastanza forte da ingannare il predittore, la pura densità delle prede compenserà gli errori (se non catturi quello a cui stavi mirando, è probabile che otterrai uno dei suoi amici più vicini ).

Prede minacciose

Con l'esatta conoscenza del calcolo della velocità della preda, è possibile "guidare" una determinata preda verso una direzione desiderata, regolando la posizione di un predatore.

Ciò consente di appuntare una preda contro un muro o dirigerla verso un altro membro del branco. Ho provato alcune strategie raffinate, come pizzicare una preda tra due membri del branco. Sfortunatamente, questo si è rivelato meno efficiente dell'attuale routine "pin and scan", dal momento che tenere due predatori occupati per inseguire una singola preda lascia l'opposizione con troppi predatori liberi di esplorare il pascolo.

Rubare le prede

Una caratteristica del comportamento delle prede è che l'influenza di un predatore aumenta proporzionalmente alla sua distanza dalla preda (purché rimanga nel raggio di visione della preda). Più un predatore si avvicina a una preda, meno la preda si allontanerà da essa.

Significa che quando due predatori competono per catturare una preda, il più vicino è tenuto a prenderlo per primo. Persino un contendente super intelligente che sarebbe riuscito a posizionarsi proprio di fronte all'asse cacciatore / preda avrebbe praticamente spaventato la preda nelle fauci del contendente.

Per riuscire a rubare una preda, sono necessari almeno un paio di predatori. Uno andrà per l'uccisione e gli altri rimarranno nel raggio di visione della preda, il più lontano possibile dalla preda per massimizzare l'influenza e spingere la preda verso il cacciatore.

Inoltre, ogni cambio di direzione consentirà alla competizione di tagliare l'angolo verso la preda e mantenere dietro il contendente è possibile solo se il "goader" era abbastanza vicino alla preda all'inizio dell'azione.

Quindi il furto di prede ha solo una possibilità di successo se le posizioni iniziali dei "ladri" sono favorevoli e puoi risparmiare almeno un secondo predatore. Nella mia esperienza, questo non vale la complessità.

Modifiche suggerite

Per consentire strategie più complesse, spostare il predatore al di sopra della velocità massima della preda potrebbe avere un costo in punti di fame, proporzionale all'eccesso di velocità. Supponiamo ad esempio che passare alla velocità 6 sia gratuito e ogni punto di velocità superiore a 6 costa 100 punti fame (andare a 6,3 costa 30 punti fame per turno, bruciare 1000 punti fame permetterebbe di raggiungere la velocità 16 per un turno - e morire se si non catturare una preda che lo fa!).

Invece di dare l'uccisione a un predatore casuale quando più di uno è abbastanza vicino da mangiare una preda, suggerisco di dividere il guadagno (ad esempio, 3 predatori otterrebbero 333.33 punti fame ciascuno). Ciò consentirebbe strategie di gioco più complesse (oscurare un predatore nemico sarebbe utile se pensi di avere più punti di fame, per esempio).

Il colore speciale per il primo pacchetto è piuttosto difficile da vedere. Suggerisco ciano o arancio anziché blu.


Finalmente un altro concorrente! Ho implementato di proposito ogni punto che hai citato, tranne per il furto di prede, per il quale sono stato soddisfatto dell'attuale effetto collaterale. Dai tuoi commenti sembra che tu stia vincendo contro il mio Chiaroveggente? È interessante, controllerò domani. = D. Inoltre, puoi provare il mio aggiornamento della GUI per vedere una grafica migliore (almeno secondo me).
solo l'

Non sto vincendo tutto il tempo. Dipende da chi è più vicino alle ultime prede quando si generano. I miei cani capaci potrebbero avere un vantaggio statistico, però. Ho anche ottimizzato il motore di simulazione per visualizzare ciascuna squadra in un colore diverso, ma alla fine si è rivelato troppo colorato per i miei gusti, quindi ho optato per l'arancione anziché il blu per la 1a squadra e tutto il rosso.

Wow, ho appena inviato la tua richiesta. È pazzesco, non sapevo che riuscissi a fermare la preghiera così. E ho sentito un po 'tradito che quando la preda è esattamente al limite, i miei predatori non saranno in grado di rilevarli, questo è sicuramente un grosso svantaggio per me.
solo il

Questa è un'aritmetica vettoriale per te :). Questo è ciò che ho cercato di spiegare nel mio post: per una singola preda, conosci tutti i parametri di movimento (inclusa la velocità precedente), quindi puoi calcolare una posizione di predatore che produrrà la velocità della preda appropriata per farla stare ferma vicino a un muro.

1
Bene, la soluzione dell'equazione è hard-coded (non sono necessarie iterazioni). Fondamentalmente si calcola il vettore che la preda dovrebbe usare per la velocità della prossima svolta se il proprio predatore non fosse presente. Data la direzione in cui vuoi che vada la preda, deduci la differenza di vettore necessaria per rendere il punto di velocità della preda in quella direzione. Questa differenza vettoriale ti dà la posizione del predatore rispetto alla preda. Questo ti lascia con un certo grado di libertà che ti consente (entro certi limiti) di selezionare una distanza dalla preda.

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

Avrai bisogno della piattaforma haskell per eseguire questo. Quindi si utilizza il runhaskellcomando per eseguirlo. Il mio branco attende che la preda venga da loro.


+1 per una soluzione scheletro in una nuova lingua. Presumibilmente sei felice che le persone costruiscano nuove strategie in aggiunta a questo?
trichoplax,

Certo, perché no. (Anche se, in qualche modo, non fa nulla oltre a produrre un output costante e uscire su "dead \ 0", quindi non sono sicuro che sarebbe molto utile.)
PyRulez,

Consiglio comunque a chiunque lo esegua di utilizzare l' -silentopzione, però ...
Geobits,

3

Non è una voce, sono sempre interessato ad aggiungere colori personalizzati per ogni voce partecipante in ;)

E anche il processo alimentare non viene visualizzato cambiando il colore, ma cambiando invece le dimensioni, in modo che possiamo vedere più eventi alimentari in breve tempo.

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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.