Hunger Gaming - Essen oder Sterben

60

Hunger Gaming - Essen oder Sterben

Wenn du nicht isst, stirbst du. Wenn Sie essen, leben Sie (bis Sie sterben). Du wirst sterben, also versuche zuletzt zu sterben.

Überblick

Es gibt eine Insel mit einer Herde von Beutetieren. Du kontrollierst ein Rudel mit fünf Raubtieren. Ihr Ziel ist es, Ihren Rucksack am Leben zu erhalten. Tun Sie dies, indem Sie Beute essen. Die Beute neigt dazu, vor Raubtieren davonzulaufen und zu versuchen, ansonsten in einer Herde zu bleiben. Natürlich befindet sich Ihr Rudel auf dem gleichen Feld wie jedes andere Rudel , daher wird die Konkurrenz versuchen, sie zu essen, bevor Sie können. Lass dich nicht entmutigen, sonst wirst du verhungern.

Spielanleitung

Erstellen und senden Sie ein Befehlszeilenprogramm, um Ihr Paket zu leiten. Es empfängt Statusinformationen vom Steuerprogramm auf STDIN und gibt Befehle auf STDOUT aus. Das Format wird unten detailliert beschrieben. Jedes Programm wird nur einmal ausgeführt und muss so lange ausgeführt werden, bis keine Mitglieder mehr am Leben sind. Sie müssen die eingegangenen Eingaben sofort lesen und schnell reagieren. Für jede Antwort gibt es ein striktes Timeout von 200 ms. Wenn Sie bis dahin nicht geantwortet haben, erhält Ihr Rudel keine neuen Anweisungen für die aktuelle Runde.

Wenn Ihr Programm nicht von der Steuerung ausgeführt werden kann, wird es als ungültig betrachtet. Bitte geben Sie die Befehlszeilenzeichenfolge an, die ich zum Ausführen Ihrer Übermittlung verwenden muss. Wenn es spezielle Anweisungen gibt (zum Einrichten von Compilern usw.), fügen Sie diese bitte bei. Wenn ich es nicht zum Laufen bringen kann, werde ich Sie in Kommentaren um Unterstützung bitten. Wenn Sie nicht antworten, kann ich Ihre Einsendung nicht akzeptieren.

Das Turnier findet auf einem 64-Bit-Linux-System statt. Beachten Sie dies, wenn Sie die erforderlichen Anweisungen geben.

Einzelheiten

  • Jede Kreatur die Position und Richtung sind in der Form eines Paares von doppeltgenauen Gleitkommazahlen (z double) repräsentiert , deren xund yKoordinaten, respectively.

  • Jede Kreatur gilt als ein Punkt. Dies bedeutet, dass sie sich überlappen und denselben Raum einnehmen können. Sie werden nicht beiseite gestoßen, und es gibt kein Konzept der Kollision mit anderen Kreaturen.

  • Die Insel ist ein Quadrat, 500 Einheiten an einer Seite. Wenn Sie versuchen, diese Grenzen zu überschreiten, werden Sie an der Kante festgeklemmt. Der Ursprung {0,0}befindet sich oben links, wobei er xnach rechts und ynach unten zunimmt. Auch hier wird die Karte nicht umgebrochen .

  • Das Spiel beginnt mit 1500+ (packCount * 50) Beutetieren. Sie werden in der Mitte der Insel versammelt, entscheiden sich aber schnell für den Umzug.

  • Die Packs werden in einem gleichmäßig verteilten Kreis um den Umfang herum angeordnet. Die Packreihenfolge wird gemischt, rechnen Sie also nicht damit, an einem bestimmten Ort zu beginnen.

  • Beutetiere können alle anderen Tiere innerhalb eines Radius von 30 Einheiten sehen. Sie können sich mit maximal 6,0 Einheiten pro Spielzug bewegen .

  • Raubtiere können alle anderen Tiere in einem Radius von 50 Einheiten sehen. Sie können sich mit maximal 6,1 Einheiten pro Runde bewegen . Dies bedeutet, dass sie Beute sehen können, bevor sie gesehen werden, und ihnen (kaum) entkommen können.

  • Raubtiere leben und sterben entsprechend ihrem Hungerlevel . Sie beginnt bei 1000 und verringert sich bei jeder Umdrehung um eins. Befindet sich ein Raubtier nach einer Bewegung innerhalb einer Beuteeinheit, frisst es diese automatisch. Dies entfernt die Beute und setzt den Hunger des Raubtiers auf 1000. Jeder Raubtier darf nur eine Beute pro Spielzug essen. Wenn sich mehr als eine in Reichweite befinden, frisst sie diejenige, zu der die Schleife zuerst gelangt (nicht unbedingt die nächstgelegene). Ein Raubtier stirbt, wenn sein Hunger Null erreicht.

  • Die Pakete beginnen mit jeweils fünf Mitgliedern . Alle 5000 Runden bringen alle noch im Spiel befindlichen Packs ein neues Mitglied hervor. Es wird im sichtbaren Bereich eines anderen Rudelmitglieds platziert. Stellen Sie sicher, dass Ihre Einträge mehr als fünf Mitglieder verarbeiten können.

  • Alle 1000 Runden wird mehr Beute erscheinen. Die Anzahl der neuen Beute ist die Anzahl der lebenden Raubtiere minus eins.

  • Raubtiere können andere Raubtiere nicht angreifen. Sie essen Beute, wenn sie sie fangen. Das ist es.

  • Die Reihenfolge innerhalb einer Runde ist:

    • Alle Beute treffen Entscheidungen
    • Alle Raubtiere treffen Entscheidungen
    • Alle Beute bewegen sich
    • Alle Raubtiere bewegen sich / fressen
  • Die Reihenfolge, in der jedes Rudel seine Entscheidungen trifft / einzieht, wird in jeder Runde zufällig festgelegt.

Protokoll (allgemein)

Die gesamte Kommunikation erfolgt im Zeichenfolgenformat US-ASCII. Zahlen werden mit Java's Double.toString()oder in Strings konvertiert Integer.toString(). Ihre Ausgabe muss so formatiert sein, dass sie von Java gelesen werden kann Double.valueOf(String)(Sie geben keine ganzen Zahlen aus). Einzelheiten zu syntaktisch analysierbaren Formaten finden Sie in der Dokumentation zuDouble . Alle Felder in einer Zeile werden durch den Standard getrennt \tCharakter und Zeilenumbrüche sind \n. Die gesamte Zeichenfolge wird mit einem Null-Byte beendet \0.

In den folgenden Beispielen <>markiere ich die Felder aus Gründen der Lesbarkeit. Diese sind in den tatsächlichen Zeichenfolgen nicht vorhanden.

Protokoll (Eingabe)

Die Länge der Eingabezeichenfolge hängt davon ab, wie viele Kreaturen in Ihrem Rudel sichtbar sind. Es kann mehr als 100.000 Zeichen enthalten, seien Sie also darauf vorbereitet. Das Grundsetup ist:

  • Zeile 0: Grundlegende Informationen zum Spiel. turnist die aktuelle Zugnummer und die Anzahl ist die Gesamtzahl der Beutetiere und Raubtiere, die noch auf dem Spielfeld sind. Diese sind integerin Stringform.

    <turn>\t<preyCount>\t<predatorCount>\n
    
  • Zeile 1: Die eindeutigen IDs und Hungerlevel Ihrer Rudelmitglieder. Diese werden nicht für jede Eingabe in derselben Reihenfolge angegeben. Verwenden Sie die eindeutigen IDs, um einzelne Mitglieder zu verfolgen, nicht die Reihenfolge, in der sie in der Eingabe angezeigt werden. Auch dies sind integerals Zeichenfolgen. Für eine Zweierpackung wäre dies:

    <id[0]>\t<hunger[0]>\t<id[1]>\t<hunger[1]>\n
    
  • Zeile 2: Die Positionen Ihrer Packmitglieder in derselben Reihenfolge wie in Zeile 1 angegeben . Dies sind doubleals Zeichenfolge:

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

Die folgenden Zeilen geben die Sichtbarkeit jedes Packmitglieds in der Reihenfolge an , in der sie in Zeile 1 angegeben sind . Diese werden als zwei Zeilen pro Mitglied angegeben.

Die erste für jede besteht aus Orten für die Beute, die er sehen kann. Das zweite sind Orte für die Raubtiere, die er sehen kann. Diese Standorte sind insgesamt nicht eindeutig. Wenn beispielsweise zwei Rudelmitglieder dasselbe Tier sehen können, befindet es sich in der Zeichenfolge beider Mitglieder. Auch Ihre eigenen Rudelmitglieder werden enthalten sein. Wenn Sie sie ausschließen möchten, möchten Sie möglicherweise Standorte mit Ihren eigenen Mitgliedern vergleichen. Alle Speicherorte sind im doubleZeichenfolgenformat.

Für jedes lebende Mitglied:

<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

Schließlich steht das letzte Zeichen \0am Anfang der nächsten Zeile.

Ausnahme: Wenn Sie die Eingabe erhalten dead\0, ist Ihr Rucksack tot. Beenden Sie Ihr Programm bitte mit Vorbehalt. Der Controller sollte alle lebenden Prozesse herunterfahren, wenn er geschlossen ist, aber ich möchte lieber keine Zombie-Prozesse überall haben. Aus Höflichkeitsgründen können Sie eine Zeitüberschreitung für die Eingabe angeben. Zum Beispiel endet meine Beispielklasse, wenn sie 15 Sekunden lang keine Eingabe erhält.

Protokoll (Ausgabe)

Die Ausgabe ist einfach. Sie geben doublefür jedes Live-Pack-Mitglied ein Wertepaar an . Diese stellen die Bewegung dar, die Sie in dieser Runde einnehmen möchten. Wenn sich Ihre Kreatur beispielsweise gerade in befindet {100.0, 100.0}und Sie ihnen den Befehl geben {-1.0, 1.0}, werden sie sich zu bewegen {99.0, 101.0}. Alle Zahlen werden in einer einzelnen Zeile durch Tabulatoren getrennt.

Wenn Sie beispielsweise 3 Packmitglieder am Leben hätten, wäre dies eine gültige Antwort:

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

Dies würde Ihre Kreaturen bewegen {1.0,-1.0}, {2.0,-2.0}und {3.0,-3.0}. Die Bestellung ist die gleiche wie in der Eingabe erhalten. Vergiss das Ende nicht \0!

Wenn Sie eine ungültige Eingabe machen, folgen schlechte Ergebnisse. Wenn eine einzelne Zahl nicht zu a analysiert werden kann double, wird sie zu Null. Wenn die Zeichenfolge als Ganzes nicht analysiert werden kann, werden keine neuen Anweisungen gegeben, und Ihr gesamtes Paket verwendet die Anweisungen aus der vorherigen Runde.

Alle Richtungen werden auf einen maximalen Abstand von 6,1 Einheiten geklemmt. Sie können sich langsamer bewegen, wenn Sie möchten. Zum Beispiel {1, 0}wird Sie eine Einheit bewegen. {6,8}(Distanz 10) bewegt nur 6.1 Einheiten und reduziert sich auf ungefähr {3.66, 4.88}. Die Richtung bleibt konstant.

Wichtig: Das Steuerprogramm liest sowohl Ihren STDOUT als auch Ihren STDERR. Wenn Sie eine Ausnahme auslösen und an STDERR drucken, ist es sehr unwahrscheinlich, dass die Nachricht in Form einer gültigen Antwort vorliegt. Versuchen Sie dies zu vermeiden.

Steuerungsprogramm / Testen

Die Quelle für den Controller finden Sie hier auf bitbucket.org . Sie müssen es kompilieren, bevor Sie es ausführen können. Die Hauptklasse ist Gameund alle Klassen sind im Standardpaket enthalten. Fügen Sie zum Ausführen den Befehl jedes Pakets als separates Argument ein. Wenn Sie beispielsweise Java ChaserPack und Python LazyPack.py ausführen möchten, können Sie Folgendes verwenden:

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

Auf der Karte werden Beute in Grün und Raubtiere in Rot angezeigt. Unabhängig davon, welches Paket das erste ist , das als Argument angegeben wird, wird es stattdessen blau angezeigt. Dies soll zu Testzwecken die Unterscheidung erleichtern. Raubtiere blinken auch fünf Frames lang weiß, wenn sie fressen.

Das Spiel wird fortgesetzt, bis der letzte Raubtier verhungert und auf die Konsole geschrieben wird, wenn Hunger oder Aussterben eintreten. Sobald das Spiel beendet ist, wird die Punktzahl für jedes Paket vergeben. Wenn Sie die Verhungern / Aussterben-Ereignisse nicht sehen möchten, können Sie das -silentArgument verwenden. Dann wird nur das Endergebnis ausgegeben. Sie müssen dies als erstes Argument übergeben :

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

Im Lieferumfang ist ein Java-Paket mit dem Namen "Skeleton" enthalten GenericPack. Es enthält die grundlegenden Ein- / Ausgabeoperationen, die benötigt werden. Es soll ein klares Beispiel geben, wie man parst und antwortet. Wenn Sie eine Vorlage in einer anderen Sprache hinzufügen möchten, lassen Sie es mich wissen.

Ebenfalls enthalten ist ein Raubtier, das auf der Vorlage basiert ChaserPack. Es wird nicht in das Turnier aufgenommen und ist nur zu Testzwecken enthalten. Die Leistung ist aufgrund eines absichtlichen Zielfehlers ziemlich schlecht. Wenn Sie es nicht schlagen können, versuchen Sie es weiter.

Unten sehen Sie ein Beispiel für den Ablauf des Steuerungsprogramms (für Video klicken). Die Videoqualität ist nicht besonders gut (sorry), aber Sie können ein Gefühl dafür bekommen, wie sich die Beute bewegt. ( Vorsicht: Audio )

Bildschirmfoto

Wertung

Der Gewinner wird nach Turnier ermittelt und erhält in jeder Runde Punkte.

Jede Runde geht weiter, bis alle Raubtiere tot sind. Jedes Rudel wird danach gewertet, wann sein letztes Mitglied an Hunger gestorben ist. Sie erhalten dann auf der Grundlage der Bestellung Punkte. Punkte werden für zehn Runden gesammelt, und der Sieger ist das Rudel mit den höchsten Gesamtpunkten.

Der erste Platz für jede Runde erhält 100 Punkte. Für jeden weiteren Platz wird die Belohnung um 20% reduziert (abgerundet). Dies wird fortgesetzt, bis die Punkte Null erreichen (nach dem 17. Platz). Die Plätze 18+ erhalten keine Punkte für die Runde. Packs, die gleichberechtigt sind, erhalten gleiche Punkte. Zum Beispiel:

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

Die maximal mögliche Punktzahl im Verlauf des Turniers beträgt 1000 Punkte, vom ersten Platz alle zehn Male.

Wenn mehrere Programme das Turnier mit dem ersten Platz beenden, wird ein weiteres Turnier mit zehn Runden abgehalten, wobei nur die Einträge mit dem ersten Platz eingereicht werden. Dies wird so lange fortgesetzt, bis ein Sieger hervorgeht.

Ich werde versuchen, ungefähr wöchentlich ein Turnier zu veranstalten, oder wenn neue Einreichungen eingehen.

Zusatzregeln (fair spielen!)

  • Sie dürfen keine externen Ressourcen lesen oder darauf schreiben. Da Sie Ihr Programm nicht mehrmals aufrufen, können alle Statusinformationen intern gespeichert werden.

  • Stören Sie nicht andere Prozesse / Einreichungen. Dies bedeutet nicht, dass Sie nicht versuchen, ihre Beute zu stehlen, ihnen zu entkommen usw. Dies bedeutet , dass Sie den Ablauf des Prozesses nicht stören. Dies liegt in meinem Ermessen.

  • Die Teilnehmer sind auf maximal drei Einsendungen beschränkt. Wenn Sie mehr einreichen, erziele ich nur die ersten drei eingereichten Punkte. Wenn Sie einen widerrufen möchten, löschen Sie ihn.

  • Einträge dürfen nicht nur existieren, um andere Einträge zu unterstützen. Jeder sollte spielen, um für sich zu gewinnen.

  • Ihr Programm kann maximal einen untergeordneten Prozess gleichzeitig erzeugen ( insgesamt Nachkommen, nicht direkt). Stellen Sie in jedem Fall sicher, dass Sie die Zeitüberschreitung nicht überschreiten. Sie dürfen die GameKlasse selbst in keiner Weise aufrufen .

Ergebnisse - 29. April 2014

Hier sind die Ergebnisse des letzten Turniers mit zehn Runden:

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

Packs, die vor 09:00 EDT 2014/04/29 eingereicht wurden, sind in diesem Lauf enthalten.

Sie können auch die Details für jede Runde anzeigen . Aus irgendeinem Grund habe ich mich entschlossen, die Runden rückwärts zu nummerieren, also beginnt es mit "Runde 10".

Aktualisierung

23.04.2014: FGreg hat einen Fehler im Zusammenhang mit Timeouts gemeldet (danke!). Ein Fix wurde implementiert, sodass Tester ihren Steuerprogrammcode aktualisieren möchten.

Geobits
quelle
28
Ich mag diese King of the Hill-Fragen!
Cruncher
2
@Manu Ich habe die Beispiel-Bots unter Windows 7 geschrieben und sowohl unter Windows als auch unter Linux getestet. Welche Probleme haben Sie mit ihnen?
Geobits
2
Diese King-of-the-Hill-Fragen sind ziemlich großartig, und diese Frage ist definitiv interessant. Ich habe jetzt zwei verschiedene Packungen in Arbeit!
Mackthehobbit
2
@githubphagocyte Ich möchte nicht wirklich eine Packung bei der ersten Auszeit töten, einfach weil ich selbst einfache Programme gesehen habe, die alle 40k + Umdrehungen oder ähnliches eine Auszeit haben. Ich habe die Namensänderung im Controller durchgeführt. Abbiegungen werden jetzt im gesamten Programm als Abbiegungen bezeichnet, es sei denn, ich habe irgendwo eine verpasst.
Geobits
2
@ Geobits eh, das ist in Ordnung für mich. Weißt du, das sieht einem Forschungsprojekt sehr ähnlich, das einer meiner Physikprofessoren durchführt und bei dem ich möglicherweise über den Sommer hinweg helfen werde. Ich werde das später erklären, wenn ich kann.
krs013

Antworten:

10

Hellseher

Code für AbleDogs aktualisiert

Woo hoo! Schliesslich schlägt das Netcats! Ich habe den vorhandenen Code (Dank an Geobits!) Mit einigen kleinen Änderungen erweitert, um dieses zukünftige Vorhersagepaket zu erstellen. Nichts schlägt Raubtiere, die wissen, wohin sich die Beute bewegt!

Von zwei Tests, die ich gemacht habe, hat mein Rudel immer gegen Netcats gewonnen. Dies funktioniert jedoch nicht so gut, wenn keine anderen Rudel vorhanden sind, da die Vorhersage immer noch fehlschlägt, wenn zu viele andere Beutetiere in der Nähe sind.

Wahrscheinlich kann ich den Trick von CivilizedBeasts einbeziehen, um die Anzahl der Beute in den ersten paar Tausend Runden erheblich zu reduzieren.

Fertig in 5,21 Minuten
Hellseher (1): Runde 9270: 100 Punkte
EcoCamel.pl (3): Runde 8118: 80 Punkte
Netcats (0): Turn 6111: Score 64
RubyVultures.rb (5): Runde 4249: Punktzahl 51
RubySpiders.rb (4): Turn 3495: Score 40
CivilizedBeasts (2): Runde 3176: Punktzahl 32
ChaserPack (6): Turn 2492: Score 25

Aus dem Namen meines Rudels sollte hervorgehen, welche Strategie ich verwende = D

Bearbeiten :

  • Aktualisiertes Pack-Management-System, um nicht die gleiche Beute zu jagen (und auch die beste Übereinstimmung zu finden!)
  • Verbessern Sie den Wandervorgang, wenn die Anzahl der Beute gering ist (dies ist entscheidend für einen Sieg!).
  • Verbessere Sonderfälle, wenn die Vorgängerversion nur an der Ecke hängen bleibt.
  • Ein Fehler im Algorithmus zur Raubtiererkennung wurde behoben (jetzt ist er ziemlich genau!)
  • Eingeschlossen Beute flock[ALIGN]Faktor
  • Halten Sie eine Beute als Haustier, wenn das Futter knapp ist
  • Erstelle eine Höhle, in die das Rudel seine Beute treibt
  • Locken Sie in der Nähe befindliche Raubtiere auf die Jagd nach unserer Beute, die sie nicht gewinnen werden

Ich habe gezählt, wie viele Beute jedes Rudel frisst, und hier ist das Ergebnis:

Hellseher (1) verbrauchte 916 Beute in 9270 Runden (0,099 Beute / Runde)
EcoCamel.pl (3) verbrauchte 73 Beute in 8118 Runden (0,009 Beute / Runde)
Netcats (0) haben in 6111 Runden 563 Beute verbraucht (0,092 Beute / Runde)
RubyVultures.rb (5) verbrauchte 77 Beute in 4249 Runden (0,018 Beute / Runde)
RubySpiders.rb (4) verbrauchte 293 Beute in 3495 Runden (0,084 Beute / Runde)
CivilizedBeasts (2) haben in 3176 Runden 10 Beute verbraucht (0,003 Beute / Runde)
ChaserPack (6) verbrauchte 43 Beute in 2492 Runden (0,017 Beute / Runde)

Mein Rudel ist sehr aggressiv, und die meisten 916 zählen meiner Meinung nach dazu, Netcats Beute zu stehlen, genau wie RubySpiders.

CivilizedBeasts verliert leider aufgrund des mittleren Kamels von EcoCamel.

Und EcoCamel (mit kritischem Hunger 500) ist ziemlich effizient, es isst gerade genug, um bis zum Ende zu überleben.

Auch mit diesem aktualisierten Hellseher erreicht das Spiel kaum 10.000 Runden.

Der Code:

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;
    }
}
nur zur Hälfte
quelle
1
Sieht sehr gut aus, deine macht sich in meinem Spiel in der Tat besser als die Netcats. Aber ich hasse es, dass ich die anderen Raubtiere nicht rennen kann, da meine Bestien einen wirklich schlechten Job in deinen Statistiken machen (während Evilcamel viel zu gut ist). Vielleicht muss ich versuchen, einen Perl-Compiler zu installieren oder so.
Herjan
Ja, ich denke, Ihre Methode funktioniert nicht, wenn sich ein Raubtier in der Mitte befindet, wie in Ihrer Antwort besprochen. Ich habe versucht, eine andere Version zu implementieren, die sich ähnlich wie Ihre verhält. Es kann die Formation in Abhängigkeit von der Anzahl der verfügbaren Raubtiere ändern. Es macht also Spaß, sie zu beobachten, obwohl sie nicht viel besser ist als deine.
Nur die Hälfte des
Ja, meine Strategie kann in vielerlei Hinsicht verbessert werden, wie andere Formationen mit einer anderen Anzahl von Mitgliedern, da meine Bestien mit <4 Raubtieren zum Scheitern verurteilt sind. Oder zum Beispiel zufällige Orte zum Sammeln (anstatt nur der Mitte). Aber ich bin zu faul, um das (jetzt) ​​umzusetzen. Und es wird nie so gut wie dieses sein, da meine Taktik einfach nicht funktioniert, wenn die Beute niedrig wird. Dann brauchst du eine Bestie wie deine (du hast bereits erwähnt, dass ich mit meiner Taktik beginnen soll, und wenn die Beute knapp wird, um diese Taktik anzuwenden). Ich denke, Sie haben das bereits durchdacht.
Herjan
Ich bin gerade auf einer anderen Herausforderung und GeoBits scheint das Interesse an dieser verloren zu haben, also lasse ich sie eine Weile stehen, es sei denn, die Ergebnisse werden aktualisiert. Ich habe Ideen für ein paar andere Einreichungen, also hoffe ich, dass diese Herausforderung am Leben bleibt. Ich werde mir natürlich Ihr Update ansehen.
15

Netcats

Hier ist ein Paket, mit dem ihr loslegen könnt. Es erweitert die GenericPackim Steuerungsprogramm enthaltene Klasse. Es wurde seit dem ursprünglichen Posting verbessert und hungert nicht länger mit einer spärlichen Herde.

Netzkatzen benutzen eine V-förmige Netzformation, um Beute in der Ecke zu fangen, wo sie sie in ihrer Freizeit fressen können. Das Netz wird mit einem "Kopf" -Element in der Mitte gebildet. Sobald der Kopf isst, tauscht er die Plätze mit dem hungrigsten Rudelmitglied aus, da der Kopf normalerweise der erste ist, der die Gelegenheit zum Essen bekommt.

Das Netz fängt eher klein an, erweitert sich aber, wenn die Herde kleiner wird, um das Feld effizienter zu durchforsten.

Wenn keine Beute sichtbar ist, erweitert sich die Formation zu einem naiven Suchmuster, das den größten Teil der Insel abdeckt.

Sobald das Rudel auf zwei Mitglieder heruntergekommen ist, funktioniert das Netz einfach nicht mehr. An diesem Punkt geht jeder seinen eigenen Weg, isst gierig das nächste, was er finden kann und geht ansonsten halb zufällig spazieren.

Diese Version überlebt viel besser als die naiven Netcats, die in dem in der Frage verlinkten Video zu sehen sind .

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

public class Netcats extends GenericPack {

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

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

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

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

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

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

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

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

        Collection<Animal> yummy = getFoods(head);

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

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

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

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

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

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

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

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

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

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

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

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

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

        if (target == null)
            return false;

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

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

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

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

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

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

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

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

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

Rubin Spinnen

Da manchmal weniger mehr ist und viele Lösungen wahrscheinlich trotzdem versuchen würden, die Beute in die Enge zu treiben ...

Ich dachte, mein Rudel könnte sich aufteilen und warten, bis andere die Arbeit erledigen.

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

Vorsichtsmaßnahme: Es läuft nicht weiter, es liest keine Eingaben, wenn es eingeht, und es reagiert auch nicht schnell. Trotzdem hoffe ich, dass es ohne weitere Anpassungen funktioniert, da es mit dem Controller gut funktioniert.

Legat
quelle
4
+1 erste Parasitenlösung. Ich denke, diese Art der Antwort wird die Qualität anderer Antworten
steigern,
@githubphagocyte Ich hatte einen intelligenteren Parasiten im Sinn, aber dies ist effizienter in Bezug auf die Lebenszeit / Codezeilen. Ich hoffe, ich werde Zeit finden, es umzusetzen.
Legat
Vielleicht programmiert @Synthetica gerade meine Idee. Oder wenn seine Idee noch eine andere ist, könnten wir bald mehr Parasiten als Jäger haben;)
Legat
1
@githubphagocyte Wir dürfen drei Einträge machen, also werde ich ein weiteres Pack posten, sobald es fertig ist. Trotzdem finde ich es interessant, dass dieses in der Zwischenzeit codiert wurde und es sich als wirksamer erweisen könnte. Netcats wird sehr gut genutzt und überlebt meinen ersten Jägerrucksack.
Legat
3
Dies kann so eingegeben werden, wie es ist, auch wenn ich eine Sekunde gebraucht habe, um herauszufinden, warum. Scheint besser zu sein, je mehr Netcats du hinzufügst (was Sinn macht). +1 von mir, mal sehen, welche Art von Jägern kommen, um Ecken zu vermeiden :)
Geobits
11

CivilizedBeasts

Endlich Zeit, meine Bestien zu zeigen!

Meine Rasse findet die Jagd etwas primitiv, deshalb arbeiten sie in einem 4-köpfigen Team und geben ihren 5. Verbündeten auf, weil: weniger Raubtiere = mehr Beute für sich. Was sie im Grunde tun, ist was Menschen tun, sie fangen Beute und kümmern sich gut um ihr Vieh;)

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

Es wird ziemlich schwierig für meine Brüste, mit weniger als 200 Beutetieren in Runde + -12.000 mit nur feindlichen Netcats im Spiel zu überleben. Sie werden mit dieser Rasse zufrieden sein, da sie wirklich riesige Mengen an Beute mit einer Geschwindigkeit verschlingt, wie es keine andere Rasse jemals kann (nicht so schnelle und große Schlachten führen zum Sieg, aber es beeinflusst die (lange) Zeit, die eine ganze Runde dauert, erheblich).

Herjan
quelle
3
Wenn mit " gut auf sie aufpassen " gemeint ist, "sie wiederholt in die Mitte treiben und sie schlachten / essen ", dann machen sie das gut. +1
Geobits
Es ist lustig, mit der nicht-mutierten (Original-) Version von Evil Camels ist die zivilisierte Taktik wegen 'Center Camel' völlig ineffizient.
user2846289
1
@VadimR Mist, danke, dass du dein Kamel aktualisiert hast: PI kann es nicht testen, da es kein Java ist, aber ich weiß, dass meine Strategie mit Raubtieren in der Mitte meines Territoriums irgendwie nutzlos ist: P
Herjan
5
Es ist wieder Herjan! Auch "Es wird ziemlich schwierig für meine Brüste , mit weniger als 200 Beute zu überleben" (Hervorhebung hinzugefügt). Ich wusste nicht, dass die Vitalität Ihrer Brüste von der Anzahl der Beutetiere in einer Computersimulation abhängt ...
Justin
5

Ruby Geier

Hier kommt eine Packung aktiverer Parasiten . Sie versuchen, sich am nächsten bewegenden Raubtier zu umgeben , damit sie seine Beute stehlen können . Sie sind ein bisschen glücksabhängig, da sie keine kluge Wahl haben, wem sie folgen sollen, aber sie schlagen gewöhnlich Verfolger und manchmal Spinnen .

Sie sind noch nicht ganz fertig, da ich dies gepostet habe, um das Tempo zu erhöhen :)

Ich hoffe auf:

  • lassen sie nach räubern außerhalb des sichtfeldes suchen
  • berücksichtige die Beute - oft ist eine zwischen einer anderen Packung und der Beute!
  • fange an, sie zu drehen , um ein Verhungern zu vermeiden, wenn alle anderen gut ernährt sind

22. April 2014: Langeweile wurde hinzugefügt , wodurch sie weniger klebrig werden und auf eigene Faust nach Beute suchen und nach Raubtieren suchen können

class Animal
  attr_accessor :x, :y
end

class Hunter < Animal
  attr_accessor :id, :bored

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

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

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

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

  def member_by_order index
    member_by_id @order[index]
  end

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

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

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

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

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

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

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

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

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

Pack.new
Legat
quelle
Sie brauchen definitiv mehr "Jäger" in der Mischung. Wie es ist, neigen diese dazu, sich an andere Parasiten zu binden (da dies die Mehrheit auf dem Feld ist). Ich beobachte sie jedoch gerne und kann sehen, wie sie mit einer anderen Mischung von Konkurrenten effektiv wären.
Geobits
Oh ja, in meiner Testumgebung habe ich zwei andere Jäger-Packs. Ohne sie sind Geier wahrscheinlich ahnungslos. Besonders, dass Netzkatzen die Ecken schnell bearbeiten können, ohne von der Mitte aus gesehen zu werden.
Legat
Ich denke, ich weiß, was sie besonders beunruhigen könnte. Kriegstanz der bösen Kamele. @Geobits wie wäre es, die Kämpfe auf Youtube zu stellen? 10 Runden sind nicht zu viel, um beobachtbar zu bleiben. Natürlich wäre HQ nötig. Ich erwarte nicht Millionen von Zuschauern, aber es wäre unterhaltsam zu sehen, wie sich deine Rudel entwickeln und sie vielleicht ein wenig anfeuern :)
Legat
1
Das gesamte Turnier könnte etwas lang sein (~ 8 Minuten pro Runde), um die Aufmerksamkeit zu erhalten, aber die Aufzeichnung einer "Zuschauer" -Runde könnte funktionieren. Ich werde es mir überlegen, wie es in Zukunft weitergehen soll.
Geobits
@Geobits Variiert die Geschwindigkeit während einer 8-minütigen Runde stark? Ich frage mich, ob es sich lohnt, ein Bild pro Runde aufzunehmen, damit sie mit konstanter Geschwindigkeit wiedergegeben werden können, anstatt sich bei rechenintensiven Parts zu verlangsamen. Für YouTube-Zwecke meine ich.
Trichoplax
5

Böse Öko-Kamele

Bearbeiten: Mutation # 2. Oh nein, ich bin spät dran mit der Umsetzung der Vorhersage von Beutebewegungen, um als erster die Netcats zu besiegen. OK, so sei es.

Diese Mutation ist $hunger_criticalvariabel (konstant). Wenn Sie den Wert auf über 1000 ändern, jagen Kamele immer wie Hellseher. Dann:

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

Wenn $hunger_criticalzB 500 (wie unten) eingestellt ist, versuchen sich meine Kamele (nachdem sie die Schrecken der Zivilisation gesehen haben ) umweltfreundlich zu verhalten (daher haben sie den Namen ihrer Rasse geändert), dh sie töten nur, wenn sie hungrig sind. Wenn sie nicht hungrig sind, patrouillieren sie in kritischen Inselgebieten - in der Mitte und in den Ecken -, um zu verhindern, dass andere Jäger sinnlos schlachten. Nun, mit der Mitte funktioniert es mehr oder weniger. Die Idee, in Ecken zu kreisen, war, die Beute zu verscheuchen und den Katzen und Parasiten das Leben zu erschweren. Nun, es funktioniert nicht. Dumme Beute geht sowieso in die Ecke.

Interessant ist auch, dass die flock[ALIGN]Komponente nur von Raubtieren erraten werden kann und meine Implementierung sich von der von justhalf unterscheidet. Ich fürchte , es gibt einige kleinere Fehler in meiner Abzocke Umsetzung Geobits' Code, gerade / Vergleich der einzelnen Jagd auf Kamele vs Clairvoyants.

Und das Programm ist schon ziemlich lang, sorry.


Bearbeiten: Mutation # 1. Die Insel erweist sich als ziemlich radioaktiv (das erklärt den Mangel an Vegetation und die unerklärliche Natur von 'Beutetieren'). Hier ist also die erste Mutation meiner Kamele. Jeder von ihnen kann Solojäger werden, wenn er Hunger hat oder es keine freie Ecke für alle gibt. Hunter versucht aktiv, Beute in der Nähe zu verfolgen. Wenn es keine gibt, patrouilliert sie in einem weiten Kreis um die Inselmitte und jagt die nächste Kreatur, wenn sie sie findet. Leider wird die Richtung der Beute unvorhersehbar, wenn sie sich ihrem Schwarm nähert (es lohnt sich, nachzuforschen ...), so dass die Jagd auf Solo nicht sehr effizient ist. Gelingt dies jedoch, geht das Kamel zur nächsten freien Ecke (falls vorhanden), um zu verdauen. Wenn das Hungerlevel unter einem bestimmten Level liegt, verlässt ein Kamel seine Ecke (wahrscheinlich verflucht Netcats). )) und geht frei von selbst Roaming. Und so weiter.


Der gleiche Witz, der zweimal erzählt wurde, ist nicht lustig, aber (1) ich musste irgendwo anfangen und bin neu in diesen Dingen. (2) Ehrlich, ich habe über Eckentaktiken nachgedacht (und wer nicht?) Und Netcats vor Ruby beobachtet Spinnen tauchten auf der Insel auf.

Also schon mal was von fleischfressenden Kamelen gehört? Arme Tiere wachten eines Tages auf dieser gottverlassenen Insel auf und fanden weder Gras noch Bäume, aber jede Menge seltsames Grün, obwohl essbare, sich schnell bewegende (ziemlich ärgerliche) Kleinigkeiten. Da meine Kamele keine Jagdgewohnheiten haben (aber sie werden hoffentlich bald mutieren), entwickelten sie ein sehr böses Überlebensmodell: Sie teilen sich und gehen jeweils in eine von vier Ecken, und die fünfte geht in die Mitte (um dort zuerst zu sterben) es stellt sich heraus). An ihren Bestimmungsorten warten sie geduldig und führen eine Art Kamelkriegstanz durch, oder sie versuchen einfach, nicht auf andere Tiere zu treten, die bereits dort sind, Spinnen und alle ...

#!/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__
user2846289
quelle
5
Dies muss das lesbarste Perl-Skript sein, das ich bisher auf dieser Site gesehen habe.
Geobits
Sie müssen genau an die Ecke gehen, um sie effektiv zu scheuchen, sonst nehmen Sie nur an der Metzgerei von Netcats teil, haha
halbe
@justhalf, Es ist wie gesagt: Der Plan hat nicht funktioniert. In den Ecken sitzende Parasiten haben die Beute auch nicht vertrieben. Hm-m, vielleicht helfen 2 oder mehr Tiere, die in einer Ecke patrouillieren.
user2846289
Deine Kamele sind eigentlich ziemlich gut! Zum Glück (für mich) habe ich meine Hellseher verbessert, so dass mein Rudel im letzten Kampf die meiste Zeit (nicht immer) gegen deins gewinnt. Interessant!
Nur die Hälfte des
1
Wenn Sie näher als 8 (20-2 * 6) Einheiten von der Beute entfernt sind, können wir jede Bewegung aller anderen Beuten sehen, die sich in der aktuellen Runde innerhalb von 30 Einheiten von unserer Beute befinden. Und die vecEigenschaft ist im Grunde nur die Verschiebung von der vorherigen Abbiegung zur aktuellen Abbiegung. Und wie ich schon sagte, machen wir einen Abgleich von der vorherigen Runde, um herauszufinden, welche Beute in welche Richtung geht. Wir können uns nicht auf die Reihenfolge der Beute verlassen. Dies ist möglich, weil die Beutetiere normalerweise (in typischen Szenarien) genügend Abstand voneinander haben (> 12 Einheiten) und wir daher die meiste Zeit die Beutetiere der vorherigen Runde der aktuellen Runde zuordnen können.
Nur die Hälfte des
4

AbleDogs - PHP

Diese netten Hunde haben gelernt, die Kälber einer Beute zu beißen, um sie an den Wänden entlang zu treiben. Sie lieben es auch, die Weide auf der Suche nach neuen Beutetieren zu durchstreifen. Schließlich wurde ihnen beigebracht, nichts zu essen, es sei denn, sie brauchen die Kalorien wirklich.

Fügen Sie den Code in eine AbleDogsDatei ein und führen Sie ihn mit ausphp 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();
}
?>

Allgemeine Überlegungen

  • Auf das Endspiel kommt es an. Sie können den intelligentesten Jagdalgorithmus aller Zeiten haben. Wenn Sie die letzten einzelnen Beute nicht schneller als der Gegner finden und fangen, verlieren Sie.

  • Wenn Ihre Raubtiere eine Beute nicht alleine (oder zumindest zu zweit) fangen können, stoßen Sie an, sobald die Beutedichte so niedrig ist, dass Sie entweder blindes Glück haben oder Beute in Ecken blockieren können.

  • Ein Beutebewegungs-Prädiktor ist grundsätzlich obligatorisch. Ich kann mir nicht vorstellen, ein auf einem Prädiktor basierendes Programm zu schlagen, ohne einen eigenen Prädiktor zu haben.

Schwanzjagd

Der ineffizienteste Weg, eine Beute zu fangen, ist die Schwanzjagd. Unter der Annahme, dass ein einzelnes Raubtier eine einzelne Beute jagt und keine äußeren Einflüsse (Mauern, andere Beutetiere usw.), könnte eine Schwanzjagd ewig dauern. Sobald Sie den Beutesichtradius von 30 Einheiten eingegeben haben, flieht die Beute mit Geschwindigkeit 6 für Ihre 6.1, sodass Sie 0,1 Distanz pro Umdrehung gewinnen: In einer geraden Linie benötigen Sie ungefähr 300 Umdrehungen, um sie zu erhalten.

Unter Berücksichtigung der Größe der Arena bewegt sich eine Beute höchstens in der Diagonale eines Quadrats von 500 Einheiten, bevor sie an eine Wand oder Ecke stößt, was maximal 117 Runden dauert.

Die Gewinnstrategie besteht offensichtlich darin, einen Weg zu finden, die Beute zu verlangsamen, indem man entweder ein anderes Raubtier oder eine Wand / Ecke davor hat.

Prädiktor

Bei einer Beutegeschwindigkeit von 6 kann sich eine Beute auf eine Fläche von 36 * pi im Quadrat bewegen. Bei einem Fangradius von 1 hat eine blinde Vermutung, wo die Beute als nächstes sein wird, eine Chance von 1/36 * pi (ca. 1%), erfolgreich zu sein. Natürlich muss etwas getan werden, um das zu verbessern!

Anhand des Simulations-Engine-Codes können Sie erkennen, dass die Eingaben wie folgt lauten:

  • sichtbare Beute- und Raubtierpositionen
  • vorherige Beutegeschwindigkeiten

Während alle Positionen verfügbar sind, sind frühere Beutegeschwindigkeiten nicht verfügbar. Die einzige Möglichkeit, diese Geschwindigkeiten zu berechnen, besteht darin, jede einzelne Beute von einer Runde zur nächsten zu verfolgen, was nahezu unmöglich ist (es sei denn, Sie implementieren einen sehr intelligenten Bewegungsverfolgungsalgorithmus). Somit kann ein Prädiktor mit Ausnahme des zu erratenden Geschwindigkeitsbeitrags problemlos alle Terme der Berechnung reproduzieren.

Im Falle einer einzelnen Beute kann die Geschwindigkeit ohne allzu große Probleme verfolgt werden, wodurch ein "perfekter" Prädiktor erstellt werden kann, um eine von der Herde isolierte Beute zu fangen. Das ist im Grunde alles, was Sie für das Endspiel benötigen, wenn zu wenig Beute vorhanden ist, um miteinander zu interagieren. Wenn es reichlich Beute gibt und der Herdeneffekt stark genug ist, um den Prädiktor zu täuschen, gleicht die bloße Dichte der Beute die Fehler aus ).

Beute stacheln

Mit der genauen Kenntnis der Beutegeschwindigkeitsberechnung ist es möglich, eine gegebene Beute in eine gewünschte Richtung zu "lenken", indem die Position eines Raubtiers eingestellt wird.

Dies ermöglicht es, eine Beute gegen eine Wand zu stecken oder sie auf ein anderes Packungsmitglied zu richten. Ich habe ein paar raffinierte Strategien ausprobiert, wie das Klauen einer Beute zwischen zwei Rudelmitgliedern. Leider erwies sich dies als weniger effizient als die derzeitige "Pin and Scan" -Routine, da zwei Raubtiere damit beschäftigt sind, eine einzige Beute zu jagen, so dass der Opposition zu viele Raubtiere zum Erkunden der Weide zur Verfügung stehen.

Beute stehlen

Ein Merkmal des Beuteverhaltens ist, dass der Einfluss eines Raubtiers proportional zu seiner Entfernung von der Beute zunimmt (vorausgesetzt, er bleibt innerhalb des Beutesichtradius). Je näher ein Raubtier einer Beute kommt, desto weniger wird die Beute von ihr weglenken.

Das heißt, wenn zwei Raubtiere miteinander konkurrieren, um eine Beute zu fangen, muss die nächste zuerst kommen. Sogar ein superschlauer Anwärter, der es schaffen würde, sich direkt vor der Verfolger- / Beuteachse zu positionieren, würde die Beute im Grunde in den Kiefer des Anwärters einschüchtern.

Um eine Beute stehlen zu können, werden mindestens zwei Raubtiere benötigt. Einer wird töten, und der andere bleibt so weit wie möglich innerhalb des Beutesichtradius, um den Einfluss zu maximieren und die Beute auf den Jäger zu lenken.

Außerdem kann der Wettkampf bei jeder Richtungsänderung die Kurve zur Beute abschneiden, und ein Zurückbleiben des Gegners ist nur möglich, wenn der "Goader" zu Beginn der Aktion nahe genug an der Beute war.

Diebstahl von Beute hat also nur dann eine Chance, wenn die Ausgangspositionen der "Stealer" günstig sind und Sie mindestens ein zweites Raubtier verschonen können. Nach meiner Erfahrung ist dies die Komplexität nicht wert.

Vorgeschlagene Änderungen

Um komplexere Strategien zu ermöglichen, kann das Bewegen des Raubtiers über die Höchstgeschwindigkeit der Beute Hungerpunkte verursachen, die proportional zur Geschwindigkeitsüberschreitung sind. Nehmen wir zum Beispiel an, dass es kostenlos ist, sich auf Geschwindigkeit 6 zu bewegen, und jeder Geschwindigkeitspunkt über 6 100 Hungerpunkte kostet (6,3 kosten 30 Hungerpunkte pro Runde, wenn 1000 Hungerpunkte verbrannt werden, kann man für eine Runde Geschwindigkeit 16 erreichen - und stirbt, wenn man anlegt Fang keine Beute, wenn du das tust!).

Anstatt ein zufälliges Raubtier zu töten, wenn mehr als eines nahe genug ist, um eine Beute zu fressen, schlage ich vor, den Gewinn zu teilen (zum Beispiel würden 3 Raubtiere jeweils 333,33 Hungerpunkte erhalten). Dies würde interessantere Endspielstrategien ermöglichen (das Beschatten eines feindlichen Raubtiers wäre nützlich, wenn Sie beispielsweise damit rechnen, dass Sie mehr Hungerpunkte haben).

Die Sonderfarbe für die Erstverpackung ist eher schwer zu erkennen. Ich schlage vor, Cyan oder Orange anstelle von Blau.


quelle
Endlich wieder ein Konkurrent! Ich habe jeden Punkt, den Sie erwähnt haben, absichtlich umgesetzt, mit Ausnahme des Diebstahls, bei dem ich mit dem aktuellen Nebeneffekt zufrieden war. Aus Ihren Kommentaren geht hervor, dass Sie gegen meinen Hellseher gewinnen? Das ist interessant, ich werde morgen nachsehen. = D. Sie können auch versuchen, mein GUI-Update zu aktualisieren, um bessere (zumindest meiner Meinung nach) Grafiken zu sehen.
Nur die Hälfte des
Ich gewinne nicht die ganze Zeit. Es kommt darauf an, wer den letzten Beutetieren am nächsten ist, wenn sie erscheinen. Meine fähigen Hunde könnten jedoch einen statistischen Vorteil haben. Ich habe auch die Simulations-Engine optimiert, um jedes Team in einer anderen Farbe anzuzeigen, aber am Ende erwies sie sich für meinen Geschmack als zu bunt, sodass ich mich für Orange anstelle von Blau für das 1. Team und für alles andere Rot entschieden habe.
Wow, ich habe gerade Ihre Einreichung ausgeführt. Das ist verrückt, ich wusste nicht, dass du das Beten so zum Stillstand bringen kannst. Und ich fühlte mich ein bisschen betrogen, dass meine Raubtiere sie nicht entdecken können, wenn die Beute genau am Rand ist. Das ist definitiv ein großer Nachteil für mich.
Nur die Hälfte des
Das ist Vektorarithmetik für Sie :). Das habe ich in meinem Beitrag versucht zu erklären: Für eine einzelne Beute kennen Sie alle Bewegungsparameter (einschließlich der vorherigen Geschwindigkeit), sodass Sie eine Raubtierposition berechnen können, die die entsprechende Beutegeschwindigkeit ergibt, um sie in der Nähe einer Mauer zum Stillstand zu bringen.
1
Nun, die Gleichungslösung ist hartcodiert (es sind keine Iterationen erforderlich). Grundsätzlich berechnen Sie den Vektor, den die Beute für die Geschwindigkeit der nächsten Runde verwenden sollte, wenn Ihr Raubtier nicht da wäre. Wenn Sie die Richtung angeben, in die die Beute fliegen soll, schließen Sie daraus die Vektordifferenz, die erforderlich ist, damit die Beutegeschwindigkeit in diese Richtung zeigt. Diese Vektordifferenz gibt Ihnen die Position des Raubtiers relativ zur Beute an. Dadurch haben Sie einen Freiheitsgrad, mit dem Sie (in bestimmten Grenzen) einen Abstand zur Beute wählen können.
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

Sie benötigen die Hashell-Plattform , um dies auszuführen. Dann verwenden Sie den runhaskellBefehl, um es auszuführen. Mein Rudel wartet darauf, dass die Beute zu ihnen kommt.

PyRulez
quelle
+1 für eine Skelettlösung in einer neuen Sprache. Vermutlich freuen Sie sich, wenn die Leute darüber hinaus neue Strategien entwickeln?
Trichoplax
Sicher warum nicht. (Obwohl es sozusagen nichts anderes tut, als eine konstante Ausgabe zu erzeugen und "dead \ 0" zu beenden, bin ich mir nicht sicher, ob es sehr nützlich wäre.)
PyRulez,
Ich würde dennoch jedem, der dies -silent
ausführt,
3

Kein Eintrag, ich bin immer daran interessiert, jedem teilnehmenden Eintrag in Farbe hinzuzufügen ;)

Und auch der Essprozess wird nicht durch Ändern der Farbe, sondern durch Ändern der Größe visualisiert, sodass wir in kurzer Zeit mehrere Essvorgänge sehen können.

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;
}
nur zur Hälfte
quelle