Jeu de la faim - Manger ou mourir


60

Jeu de la faim - Manger ou mourir

Si tu ne manges pas, tu meurs. Si vous mangez, vous vivez (jusqu'à votre mort). Vous allez mourir, alors essayez de mourir en dernier.

Vue d'ensemble

Il y a une île peuplée d'un troupeau de proies. Vous contrôlez un groupe de cinq prédateurs. Votre but est de garder votre sac en vie. Faites cela en mangeant une proie. La proie a tendance à fuir les prédateurs et à essayer de rester dans un troupeau autrement. Bien sûr, votre paquet sera sur le même terrain que tous les autres , alors la compétition essaiera de les manger avant que vous puissiez. Ne laissez pas cela vous décourager, sinon vous mourrez de faim.

Comment jouer

Créez et soumettez un programme en ligne de commande pour diriger votre pack. Il recevra les informations d'état du programme de contrôle sur STDIN et les commandes de sortie sur STDOUT. Le format est décrit en détail ci-dessous. Chaque programme ne sera exécuté qu'une seule fois et doit rester actif jusqu'à ce qu'il ne reste plus aucun membre du pack en vie. Vous devrez lire les commentaires dès leur réception et réagir rapidement. Il y a un délai d'attente strict de 200 ms pour chaque réponse. Si vous n'avez pas répondu à ce moment-là, votre pack ne recevra pas de nouvelles instructions pour le tour en cours.

Si votre programme ne peut pas être exécuté par le contrôleur, il ne sera pas considéré comme valide. Veuillez inclure la chaîne de ligne de commande que je devrai utiliser pour exécuter votre soumission. S'il y a des instructions spéciales (pour installer les compilateurs, etc.), veuillez les inclure. Si je ne parviens pas à le faire fonctionner, je vous demanderai de l’aide pour vos commentaires. Si vous ne répondez pas, je ne serai pas en mesure d'accepter votre soumission.

Le tournoi se déroulera sur un système Linux 64 bits. Gardez cela à l'esprit lorsque vous donnez les instructions nécessaires.

Détails

  • La position et la direction de chaque créature se présentent sous la forme d'une paire de nombres à virgule flottante double précision (p double. Ex. ) Représentant leurs coordonnées xet y.

  • Chaque créature est considérée comme un point. Cela signifie qu'ils peuvent se chevaucher et occuper le même espace. Vous ne serez pas mis de côté et il n'y a pas de concept de collision avec d'autres créatures.

  • L'île est une place, 500 unités sur le côté. Si vous essayez d'aller au-delà de ces limites, vous serez coincé sur le bord. L'origine {0,0}est en haut à gauche, avec une xaugmentation à droite et yune diminution. Encore une fois, la carte ne se termine pas .

  • Le jeu commence avec plus de 1500 proies (packCount * 50) . Ils seront rassemblés au centre de l'île, mais décident rapidement de commencer à bouger.

  • Les paquets seront disposés dans un cercle uniformément espacé autour du périmètre. La commande du paquet est mélangée, alors ne comptez pas commencer dans un endroit particulier.

  • Les animaux proies peuvent voir tous les autres animaux dans un rayon de 30 unités. Ils peuvent se déplacer à un maximum de 6,0 unités par tour.

  • Les prédateurs peuvent voir tous les autres animaux dans un rayon de 50 unités. Ils peuvent se déplacer à un maximum de 6,1 unités par tour. Cela signifie qu'ils peuvent voir leurs proies avant d'être vues et (à peine) les distancer.

  • Les prédateurs vivent et meurent en fonction de leur faim . Il commence à 1000 et diminue d'un tour à chaque tour. Si, après le déplacement, un prédateur se trouve à moins d'une unité de proie, il le mangera automatiquement. Cela supprime la proie et règle la faim du prédateur à 1 000. Chaque prédateur ne peut manger qu'une seule proie par tour. S'il y en a plus d'un dans la plage, il mangera celui que la boucle arrive en premier (pas nécessairement le plus proche). Un prédateur meurt si sa faim atteint zéro.

  • Les packs commencent par cinq membres chacun. Tous les 5000 tours, tous les packs encore en jeu engendreront un nouveau membre. Il sera placé à portée visible d'un membre du groupe. Assurez-vous que vos entrées peuvent gérer plus de cinq membres.

  • Tous les 1000 tours, plus de proies vont apparaître. Le nombre de nouvelles proies sera le nombre de prédateurs vivants moins un.

  • Les prédateurs ne peuvent pas attaquer d'autres prédateurs. Ils mangent des proies quand ils l'attrapent. C'est ça.

  • L'ordre dans un tour est:

    • Toutes les proies prennent des décisions
    • Tous les prédateurs prennent des décisions
    • Toutes les proies bougent
    • Tous les prédateurs bougent / mangent
  • L'ordre dans lequel chaque pack prend ses décisions / se déplace sera randomisé à chaque tour.

Protocole (général)

Toutes les communications sont effectuées au format chaîne US-ASCII. Les nombres sont convertis en chaînes avec Java Double.toString()ou Integer.toString(). Votre sortie doit être formatée pour pouvoir être lue par Java Double.valueOf(String)(vous ne produirez pas d’entiers). Pour plus de détails sur les formats analysables, voir la documentation deDouble . Tous les champs d'une ligne sont séparés par le \tcaractère standard et les nouvelles lignes le sont \n. La chaîne entière sera terminée sera un octet nul \0.

Dans les exemples ci-dessous, j'utilise <>pour marquer les champs à des fins de lisibilité. Ce ne sont pas présents dans les chaînes réelles.

Protocole (entrée)

La longueur de la chaîne d’entrée varie en fonction du nombre de créatures visibles par votre pack. Il peut dépasser 100 000 caractères, alors préparez-vous à cela. La configuration de base est la suivante:

  • Ligne 0: informations de base sur le jeu. turnest le numéro de tour actuel et les comptes représentent le nombre total de proies et de prédateurs laissés sur le terrain. Ce sont integersous forme de chaîne.

    <turn>\t<preyCount>\t<predatorCount>\n
    
  • Ligne 1: Identifiants uniques et niveaux de faim des membres de votre meute. Celles-ci ne sont pas données dans le même ordre pour chaque entrée. Utilisez les identifiants uniques pour suivre les membres individuels, pas l'ordre dans lequel ils apparaissent dans l'entrée. Encore une fois, ce sont des integerchaînes. Pour un paquet de deux, ce serait:

    <id[0]>\t<hunger[0]>\t<id[1]>\t<hunger[1]>\n
    
  • Ligne 2: Les positions de vos membres du groupe, dans le même ordre que celui indiqué à la ligne 1 . Ce sont doublecomme chaîne:

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

Les lignes suivantes représentent la visibilité de chaque membre du pack, dans le même ordre que celui indiqué à la ligne 1 . Celles-ci seront données comme deux lignes par membre.

Le premier pour chacun consiste en des emplacements pour la proie qu'il peut voir. La seconde est l’emplacement des prédateurs qu’il peut voir. Ces endroits ne sont pas uniques dans leur ensemble. Par exemple, si deux membres du groupe peuvent voir le même animal, il sera dans la chaîne des deux membres. De plus, vos propres membres du pack seront inclus. Si vous souhaitez les exclure, vous pouvez comparer les emplacements avec vos propres membres. Tous les emplacements sont au doubleformat chaîne.

Pour chaque membre vivant:

<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

Enfin, le dernier caractère sera \0au début de la ligne suivante.

Exception: si vous recevez une entrée dead\0, votre pack est mort. Terminez votre programme avec grâce, s'il vous plaît. Le contrôleur doit arrêter tous les processus en cours lorsqu'il est fermé, mais je préférerais ne pas avoir de processus zombies partout. Par courtoisie, vous pouvez inclure un délai d’entrée. Par exemple, mon exemple de classe se termine s'il ne reçoit aucune entrée pendant 15 secondes.

Protocole (sortie)

La sortie est simple. Vous donnerez une paire de doublevaleurs pour chaque membre du pack live. Ceux-ci représentent le mouvement que vous voudriez qu'ils prennent ce tour-ci. Par exemple, si votre créature est actuellement sur {100.0, 100.0}et que vous lui en donnez la commande {-1.0, 1.0}, ils se déplaceront vers {99.0, 101.0}. Tous les numéros seront sur une seule ligne, séparés par des tabulations.

Par exemple, si vous aviez 3 membres du pack en vie, cette réponse serait valide:

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

Cela déplacer vos créatures par {1.0,-1.0}, {2.0,-2.0}et {3.0,-3.0}. L'ordre est le même que celui reçu dans l'entrée. N'oubliez pas la fin \0!

Si vous donnez une entrée invalide, vous obtiendrez de mauvais résultats. Si un nombre unique ne peut pas être analysé comme un double, il deviendra zéro. Si la chaîne dans son ensemble ne peut pas être analysée, aucune nouvelle instruction ne sera donnée et l'ensemble de votre pack utilisera les instructions du tour précédent.

Toutes les directions seront serrées à une distance maximale de 6,1 unités. Vous pouvez vous déplacer plus lentement que cela si vous le souhaitez. Par exemple, {1, 0}vous déplacerez une unité. {6,8}(distance 10) ne vous déplacera que 6,1 unités et sera réduit à environ {3.66, 4.88}. La direction reste constante.

Important: le programme de contrôle lit votre STDOUT et votre STDERR. Si vous générez une exception et imprimez sur STDERR, il est très peu probable que le message se présente sous la forme d'une réponse valide. Essayez d'éviter de faire cela.

Programme de contrôle / test

La source du contrôleur peut être trouvée ici à bitbucket.org . Vous aurez besoin de le compiler avant de lancer. La classe principale est Game, et toutes les classes sont dans le package par défaut. Pour exécuter, incluez la commande de chaque pack en tant qu'argument séparé. Par exemple, si vous souhaitez exécuter un Java ChaserPack et un Python LazyPack.py, vous pouvez utiliser:

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

Sur la carte, les proies apparaissent en vert et les prédateurs en rouge. Cependant, quel que soit le paquet, le premier paquet donné en argument sera coloré en bleu. Ceci est destiné à les distinguer plus facilement à des fins de test. Les prédateurs clignotent également en blanc pendant cinq images lorsqu'ils mangent.

Le jeu se poursuivra jusqu'à ce que le dernier prédateur meure de faim, écrivant sur la console à mesure que des événements de famine ou d'extinction se produisent. Une fois le jeu terminé, le score sera attribué à chaque groupe. Si vous ne voulez pas voir les événements de famine / extinction, vous pouvez utiliser l' -silentargument. Ensuite, il ne sortira que le score final Vous devez passer ceci comme premier argument :

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

Inclus est un pack Java squelette nommé GenericPack. Il comprend les opérations d'entrée / sortie de base nécessaires. Il est là pour donner un exemple clair de la façon d’analyser et de répondre. Si vous souhaitez ajouter un modèle dans une autre langue, faites-le moi savoir.

Un prédateur basé sur le modèle est également inclus ChaserPack. Il ne sera pas inclus dans le tournoi et est uniquement inclus à des fins de test. Il fonctionne assez mal en raison d'un défaut de ciblage intentionnel. Si vous ne pouvez pas le battre, continuez d'essayer.

Vous trouverez ci-dessous un exemple d'exécution du programme de contrôle (cliquez pour voir la vidéo). La qualité vidéo n'est pas excellente (désolée), mais vous pouvez avoir une idée de la façon dont la proie bouge. ( attention: audio )

capture d'écran

Notation

Le gagnant sera déterminé par tournoi, en gagnant des points à chaque tour.

Chaque tour se poursuit jusqu'à ce que tous les prédateurs soient morts. Chaque meute sera notée en fonction du moment où son dernier membre est décédé de faim. Ils se verront attribuer des points en fonction de la commande. Les points seront cumulés pendant dix tours et le vainqueur est le groupe qui totalise le plus de points.

La première place pour chaque tour recevra 100 points. Après chaque place, la récompense sera réduite de 20% (arrondi à la baisse). Cela continuera jusqu'à ce que les points atteignent zéro (après la 17ème place). Les places de 18 ans et plus ne recevront aucun point pour la ronde. Les packs ex aequo recevront des points égaux. Par exemple:

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

Le maximum de points possibles au cours du tournoi est de 1000, de la première place tous les dix fois.

Si plusieurs programmes mettent fin au tournoi à égalité pour la première place, un autre tournoi de dix tours sera organisé avec uniquement les inscriptions de la première place soumises. Cela continuera jusqu'à ce qu'un vainqueur émerge.

Je vais essayer d’organiser un tournoi environ toutes les semaines ou au fur et à mesure de l’arrivée de nouvelles propositions.

Règles additionnelles (fair play!)

  • Vous ne pouvez pas lire ou écrire sur des ressources externes. Comme vous n'allez pas appeler votre programme plusieurs fois, toute information d'état peut être stockée en interne.

  • Ne pas interférer avec d'autres processus / soumissions. Cela ne signifie pas qu'il ne faut pas essayer de voler leur proie, de les distancer, etc. Cela ne veut pas nuire au bon déroulement du processus. Ceci est à ma discrétion.

  • Les candidats sont limités à un maximum de trois inscriptions. Si vous soumettez plus, je ne marquerai que les trois premiers. Si vous souhaitez en révoquer un, supprimez-le.

  • Les entrées peuvent ne pas exister uniquement pour soutenir d'autres entrées. Chacun devrait jouer pour gagner sur son propre mérite.

  • Votre programme peut générer au maximum un processus enfant à la fois ( nombre total de descendants et non direct). Quoi qu'il en soit, assurez-vous de ne pas dépasser le délai d'attente. Vous ne pouvez en aucun cas invoquer la Gameclasse elle-même.

Résultats - 29 avril 2014

Voici les résultats du dernier tournoi de dix rounds:

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

Les packs soumis avant 09h00 EDT le 2014/04/29 sont inclus dans cette analyse.

Vous pouvez également afficher les détails de chaque tour . Pour une raison quelconque, j'ai décidé de numéroter les tours en arrière, donc cela commence par "tour 10".

Mises à jour

2014/04/23: FGreg a signalé un bogue lié aux délais d'attente (merci!). Un correctif a été implémenté. Les testeurs souhaitent donc mettre à jour le code de leur programme de contrôle.


28
J'aime ces questions du roi de la colline!
Cruncher

2
@Manu J'ai écrit les exemples de bots sous Windows 7 et testé sous win et linux. Quels problèmes avez-vous avec eux?
Geobits

2
Ces questions du roi de la colline sont assez impressionnantes et celle-ci est vraiment intéressante. J'ai deux packs différents dans les travaux maintenant!
mackthehobbit

2
@githubphagocyte Je ne veux pas vraiment tuer un paquet lors du premier dépassement de temps, tout simplement parce que j'ai vu des programmes même simples s'arrêter une fois tous les 40k tours ou similaires. J'ai commis le changement de nom dans le contrôleur. Les virages sont maintenant appelés virages tout au long du programme, à moins que je n’en ai manqué un quelque part.
Geobits

2
@ Geobits hein, c'est bon pour moi. Vous savez, cela ressemble vraiment à un projet de recherche que l'un de mes professeurs de physique est en train de faire, auquel j'aiderai peut-être cet été. J'expliquerai un peu plus tard si possible.
krs013

Réponses:


10

Voyant

Code mis à jour pour faire face à AbleDogs

Woo hoo! Enfin bat que Netcats! J'ai étendu le code existant (crédits à Geobits!) Avec quelques modifications mineures pour créer ce futur pack de prédiction. Rien ne vaut les prédateurs qui savent où la proie va bouger!

Sur deux tests que j'ai faits, mon groupe a toujours vaincu Netcats. Mais cela ne fonctionnera pas aussi bien s'il n'y a pas d'autres packs, car la prédiction échoue toujours s'il y a trop d'autres proies à proximité.

Je peux probablement inclure le truc de CivilizedBeasts pour réduire considérablement le nombre de proies au cours des premiers milliers de tours.

Fait en 5.21 minutes
Clairvoyant (1): Turn 9270: Score 100
EcoCamel.pl (3): Turn 8118: Score 80
Netcats (0): Tour 6111: Score 64
RubyVultures.rb (5): Tour 4249: Score 51
RubySpiders.rb (4): Tour 3495: Score 40
CivilizedBeasts (2): Turn 3176: Score 32
ChaserPack (6): Turn 2492: Score 25

A partir du nom de mon pack, vous devriez savoir quelle stratégie j'utilise = D

Modifier :

  • Mise à jour du système de gestion de la meute pour ne pas chasser la même proie (et essayer de trouver le meilleur match!)
  • Améliorez le processus d'errance lorsque le nombre de proies est faible (c'est crucial pour une victoire!).
  • Améliorez les cas particuliers lorsque la version précédente était bloquée au coin.
  • Correction d'un bug dans l'algorithme de détection des prédateurs (maintenant c'est assez précis!)
  • flock[ALIGN]Facteur de proie inclus
  • Gardez une proie comme animal de compagnie si la nourriture est rare
  • Créer une tanière où la meute va rassembler leurs proies
  • Attirer les prédateurs proches dans la poursuite de notre proie, qu'ils ne gagneront pas

J'ai compté combien de proies chaque paquet mange, et voici le résultat:

Clairvoyant (1) a consommé 916 proies en 9270 tours (0.099 proies / tour)
EcoCamel.pl (3) a consommé 73 proies en 8118 tours (0,009 proies / tour)
Netcats (0) a consommé 563 proies en 6111 tours (0,092 proies / tour)
RubyVultures.rb (5) a consommé 77 proies en 4249 tours (0,018 proies / tour)
RubySpiders.rb (4) a consommé 293 proies en 3495 tours (0,084 proies / tour)
CivilizedBeasts (2) a consommé 10 proies en 3176 tours (0,003 proies / tour)
ChaserPack (6) a consommé 43 proies en 2492 tours (0.017 proies / tour)

Mon paquet est très agressif, et la plupart des 916 comptent, je pense que cela provient du vol de proies de Netcats, tout comme RubySpiders.

CivilizedBeasts perd malheureusement à cause du chameau central d'EcoCamel.

Et EcoCamel (avec le seuil critique de la faim 500) est assez efficace, il mange juste assez pour survivre.

Également avec cette mise à jour de Clairvoyant, le jeu atteint à peine 10 000 tours.

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

1
Ça a l'air très bien, le tien fait en effet mieux que netcats dans mon jeu. Mais je déteste le fait que je ne puisse pas diriger les autres prédateurs car mes bêtes font un très mauvais travail dans vos statistiques (alors que evilcamel fait beaucoup trop bien). Peut-être que je dois essayer d'installer un compilateur Perl ou autre.
Herjan

Oui, je pense que votre méthode ne fonctionne pas s'il y a un prédateur au milieu, comme indiqué dans votre réponse. J'ai essayé d'implémenter une autre version qui se comporte de la même façon que la vôtre. Cela peut changer la formation en fonction du nombre de prédateurs disponibles, donc c'est assez amusant à regarder, bien que pas beaucoup mieux que le vôtre.
moitié

Oui, ma stratégie peut être améliorée à bien des égards, à l'instar d'autres formations avec un autre montant avec des membres, parce que mes bêtes sont condamnées à moins de 4 prédateurs. Ou des endroits aléatoires à rassembler (au lieu de seulement le milieu) par exemple. Mais je suis trop paresseux pour mettre cela en œuvre (maintenant). Et ce ne sera jamais aussi bon que celui-ci puisque si la proie devient basse, ma tactique ne fonctionne tout simplement pas. C'est à ce moment-là que vous avez besoin d'une bête comme la vôtre (vous avez déjà mentionné qu'il fallait commencer par ma tactique et lorsque la proie devient faible pour utiliser cette tactique). Donc, je suppose que vous avez déjà réfléchi à cela.
Herjan

Je suis sur un autre défi en ce moment, et GeoBits semble avoir perdu tout intérêt pour celui-ci, alors je vais le laisser reposer pendant un moment à moins que les résultats ne soient mis à jour. J'ai des idées pour quelques autres propositions, j'espère donc que ce défi sera maintenu. Je vais regarder votre mise à jour, bien sûr.

15

Netcats

Voici un pack pour vous aider à démarrer. Il étend la GenericPackclasse incluse dans le programme de contrôle. Il a été amélioré depuis la publication initiale et ne se laisse plus mourir de faim avec un troupeau clairsemé.

Les Netcats utilisent une formation de filet en forme de vé pour piéger leurs proies dans un coin, où elles peuvent les manger à loisir. Le filet est formé avec un membre "tête" au centre. Une fois que la tête a mangé, elle change de place avec le membre du sac le plus affamé, car la tête est normalement la première à avoir la possibilité de manger.

Le filet commence plutôt petit, mais s'élargit lorsque le troupeau devient plus petit afin de chaluter le champ plus efficacement.

Si aucune proie n'est visible, la formation s'élargit pour former un modèle de recherche naïf couvrant la majeure partie de l'île.

Une fois que le groupe est réduit à deux membres, le réseau ne fonctionne tout simplement pas. À ce stade, chacun suit son propre chemin, mangeant goulûment la chose la plus proche qu'il peut trouver et faisant une promenade semi-aléatoire autrement.

Cette version survit beaucoup mieux que les Netcats naïfs vus dans la vidéo liée à la question.

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

public class Netcats extends GenericPack {

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

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

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

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

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

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

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

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

        Collection<Animal> yummy = getFoods(head);

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

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

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

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

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

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

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

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

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

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

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

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

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

        if (target == null)
            return false;

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

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

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

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

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

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

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

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

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

11

Araignées rubis

Comme parfois moins c'est plus et de nombreuses solutions essaieraient probablement de coincer la proie de toute façon ...

Je pensais que mon sac pourrait se séparer et attendre que les autres fassent le travail.

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

Mise en garde: il ne continue pas réellement à fonctionner, il ne lit pas d'entrée lorsqu'il entre ni ne réagit rapidement. Néanmoins, comme cela fonctionne bien avec le contrôleur, j'espère qu'il sera qualifié sans ajustement supplémentaire.


4
+1 première solution parasite. Je pense que ce type de réponse augmentera la qualité des autres réponses en éliminant progressivement les failles ...
trichoplax

@githubphagocyte J'avais en tête un parasite plus intelligent, mais celui-ci est plus efficace en termes de temps de vie / lignes de code. J'espère que je trouverai le temps de le mettre en œuvre.
Legat

Peut-être que @Synthetica est en train de coder mon idée en ce moment. Ou si son idée est encore une autre, nous pourrions bientôt avoir plus de parasites que les chasseurs;)
Legat

1
@githubphagocyte nous sommes autorisés à faire trois entrées, donc je posterai un autre paquet quand il sera prêt. Néanmoins, je trouve intéressant que celui-ci ait été codé entre-temps et qu'il pourrait s'avérer plus efficace. Il profite très bien des Netcats et survit à mon premier pack de chasseurs.
Legat le

3
Cela peut entrer en l'état, même s'il m'a fallu une seconde pour comprendre pourquoi. Semble mieux faire plus vous ajoutez Netcats (ce qui est logique). +1 de moi, voyons quel genre de chasseurs sortent pour éviter les coins :)
Geobits

11

CivilizedBeasts

Enfin, le temps de montrer mes bêtes!

Ma race pense que la chasse est un peu primitive alors ils travaillent ensemble dans une équipe de 4 et abandonnent ainsi leur cinquième allié, car: moins de prédateurs = plus de proies pour eux-mêmes. Ce qu’ils font, c’est ce que les humains font, ils attrapent des proies et prennent bien soin de leur bétail;)

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

Il devient assez difficile pour mes seins de survivre avec moins de 200 proies au tour + -12.000 avec seulement les Netcats ennemis dans le match. Vous serez heureux avec cette race car elle dévore des quantités massives de proies avec une rapidité inégalée (aucune autre race ne le peut jamais (les massacres rapides et volumineux n'offrent pas la victoire, mais ils influencent considérablement le temps (long) d'un tour complet).


3
Si vous entendez par " prendre bien soin d'eux ", les "grouper à plusieurs reprises au milieu et les massacrer / les manger ", alors oui, ils le font bien. +1
Geobits

C'est drôle, avec la version non-mutée (originale) de Evil Camels, la tactique civilisée est totalement inefficace à cause du "chameau central".
user2846289

1
@VadimR Crap, merci d'avoir mis à jour votre chameau: PI ne peut pas le tester car ce n'est pas Java mais je sais que ma stratégie est inutile avec des prédateurs au milieu de mon territoire: P
Herjan le

5
C'est encore Herjan! Aussi "Il devient assez difficile pour mes seins de survivre avec moins de 200 proies" (soulignement ajouté). Je ne savais pas que la vitalité de vos seins dépendait du nombre de proies dans une simulation sur ordinateur ....
Justin

5

Vautours Rubis

Voici un paquet de parasites plus actifs . Ils essaient d’ entourer le plus proche prédateur en mouvement pour pouvoir voler sa proie . Ils dépendent un peu de la chance car ils n’ont aucun moyen intelligent de choisir qui va suivre, mais ils battent généralement les poursuivants et parfois les araignées .

Ils ne sont pas tout à fait finis, j'ai posté ceci pour pousser le tempo :)

J'espère:

  • faites-les rechercher des prédateurs en dehors du champ de vision
  • tenez compte de la proie - souvent l'une d'elles se trouve entre un autre sac et la proie!
  • commencez à les faire tourner pour éviter de mourir de faim quand tous les autres sont bien nourris

22 avril 2014: ajout de l' ennui , ce qui les rend moins collants et leur permet de chasser leurs proies par eux-mêmes et de rechercher leurs prédateurs

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

Vous avez certainement besoin de plus de "chasseurs" dans le mélange. En l'état, ceux-ci ont tendance à s'attacher à d'autres parasites (puisque c'est la majorité sur le terrain). J'aime les regarder, cependant, et je peux voir comment ils seraient efficaces avec une combinaison différente de concurrents.
Geobits

Oh oui, dans mon environnement de test, j'ai deux autres packs de chasseurs. Sans eux, les vautours sont probablement très ignorants. Surtout que les netcats peuvent rapidement travailler les coins sans être vus du milieu.
Legat

Je pense que je sais ce qui pourrait les troubler en particulier. Danse de guerre des mauvais chameaux. @ Geobits que diriez-vous de mettre les combats sur Youtube? 10 tours n'est pas trop pour rester observable. Bien sûr, un QG serait nécessaire. Nous n'attendrions pas des millions de spectateurs, mais il serait amusant de voir comment vos packs se comporteront et de les encourager un peu :)
Legat le

1
Le tournoi complet pourrait être un peu long (environ 8 minutes par tour maintenant) pour attirer l’attention, mais enregistrer un tour "spectateur" pourrait fonctionner. Je vais y penser pour les courses futures.
Geobits

@Geobits La vitesse varie-t-elle beaucoup au cours d'une ronde de 8 minutes? Je me demande s'il ne vaut pas la peine d'enregistrer une image par tour afin de pouvoir les lire à une vitesse constante plutôt que de ralentir pendant les temps de calcul intensif. Pour les besoins de YouTube, je veux dire.
Trichoplax

5

Mauvais Eco Chameaux

Edit: Mutation # 2. Oh, non, je suis en retard dans mon implémentation de la prédiction du mouvement des proies, pour être le premier à battre les Netcats. OK, ainsi soit-il.

Cette mutation a une $hunger_criticalvariable (constante). En le changeant à une valeur supérieure à 1000, les chameaux chassent toujours, comme les clairvoyants. Ensuite:

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

Si, $hunger_criticalpar exemple, la valeur est 500 (comme ci-dessous), mes chameaux (après avoir vu les horreurs de la civilisation ) essaient de se comporter de manière respectueuse de l'environnement (ils ont donc changé le nom de leur race), c'est-à-dire qu'ils ne tuent que lorsqu'ils ont faim. S'ils n'ont pas faim, ils patrouillent dans les zones critiques de l'île, au centre et dans les coins, pour empêcher toute chasse inutile à d'autres chasseurs. Eh bien, avec center, cela fonctionne plus ou moins. L’idée de tourner dans les coins était de chasser la proie et de rendre la vie plus difficile pour les chats et les parasites. Ça ne marche pas. Une proie stupide va dans les coins quand même.

Il est également intéressant de noter que le flock[ALIGN]composant ne peut être deviné que par des prédateurs, et que mon implémentation est différente de celle de la moitié. Je crains qu'il n'y ait un bug mineur dans mon arnaque mise en œuvre du code de Geobits, regarder / comparer la chasse individuelle des Chameaux vs clairvoyants.

Et le programme est un peu long maintenant, désolé.


Edit: Mutation # 1. L’île s’avère très radioactive (ce qui explique le manque de végétation et la nature inexplicable des créatures «proies»), voici donc la première mutation de mes chameaux. Chacun d'entre eux peut devenir chasseur solo, s'il a faim ou s'il n'y a pas de coin libre pour tout le monde. Hunter tente de poursuivre activement sa proie à proximité. S'il n'y en a pas, il patrouille en grand cercle autour du centre de l'île, puis poursuit la créature la plus proche lorsqu'il la trouve. Malheureusement, la direction de la proie devient imprévisible quand elle est près de son essaim (il est intéressant de se renseigner ...), la chasse en solo n'est donc pas très efficace. Mais si cela réussit, le chameau va digérer le coin libre le plus proche (le cas échéant). Lorsque le niveau de faim est inférieur à un certain niveau, un chameau abandonne son coin (probablement en maudissant Netcats ("où est la nourriture?" )) et se déplace librement en itinérance. Etc.


La même blague racontée deux fois n’est pas drôle, mais (1) je dois commencer quelque part et je suis novice dans ce domaine, (2) honnête, j’ai pensé à la tactique du corner (et qui ne l’a pas fait?), En regardant Netcats, avant Ruby Les araignées sont apparues sur l'île.

Alors, déjà entendu parler de chameaux carnivores? De pauvres animaux se sont réveillés un jour sur cette île abandonnée pour ne trouver ni herbe ni arbre, mais plein de choses étranges, vertes mais comestibles, bougeant rapidement (plutôt gênantes). N'ayant pas d'habitudes de chasse (mais ils muteront bientôt, j'espère), mes chameaux ont développé un stratagème très diabolique pour survivre: ils se sont divisés en quatre coins, et le cinquième va au centre (y mourir le premier, il s'avère). Sur leurs destinations, ils attendent patiemment, exécutant une sorte de danse de guerre de chameaux, ou peut-être essaient-ils simplement de ne pas marcher sur les autres animaux déjà présents, araignées et autres ...

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

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

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

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

sub distance {
    return sqrt(&sq_dist)
}

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

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

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

while (1) {

    # Get input

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

    # Update my pack

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

    # Bury dead animals, retrieve their roles

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

    # See that everyone has a role and lives accordingly

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

    # And let the cruel world know about it

    print @out;
    print "\0"
}

__END__

5
Ceci doit être le script Perl le plus lisible que j'ai vu sur ce site à ce jour.
Geobits

Vous devez vous rendre exactement au coin de la rue pour les combattre efficacement, sinon vous ne ferez que participer à la boucherie de Netcats, haha
moitié le

@ juste la moitié, c'est comme je l'ai dit: le plan n'a pas fonctionné. Les parasites assis dans les coins ne chassèrent pas non plus la proie. Hm-m, peut-être deux ou plusieurs bêtes patrouillant dans un coin aideront.
user2846289

Vos chameaux sont vraiment bons! Heureusement (pour moi), j'ai amélioré mes Clairvoyants, donc la plupart du temps (pas toujours), mon groupe gagne contre le vôtre lors de la dernière bataille. Intéressant!
moitié

1
Si vous êtes à moins de 8 (20-2 * 6) unités de la proie, nous pouvons voir tout mouvement de toutes les autres proies se trouvant à moins de 30 unités de notre proie au tour actuel. Et la vecpropriété est fondamentalement juste le déplacement du tour précédent au tour actuel. Et comme je le disais, nous faisons la correspondance du tour précédent pour savoir quelle proie va dans quel sens, nous ne pouvons pas compter sur l'ordre des proies. Ceci est possible parce que les proies gardent généralement une distance suffisante (> 12 unités), de sorte que la plupart du temps, nous pouvons faire correspondre les proies du tour précédent au tour actuel.
moitié

4

AbleDogs - PHP

Ces gentils chiens ont appris à mordre les mollets d'une proie pour l'aiguiller le long des murs. Ils aiment aussi errer dans les pâturages à la recherche de nouvelles proies. Enfin, ils ont appris à s'abstenir de manger sauf s'ils ont réellement besoin de calories.

Mettez le code dans un AbleDogsfichier et exécutez-le avecphp 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();
}
?>

Considérations générales

  • C'est la finale qui compte. Vous pouvez avoir l'algorithme de chasse le plus intelligent de tous les temps, si vous ne repérez pas et ne capturez pas les dernières proies plus rapidement que l'opposition, vous perdez.

  • Si vos prédateurs ne peuvent attraper une proie seuls (ou au moins deux par deux), vous êtes grillé dès que la densité de leurs proies est suffisamment basse pour que vous puissiez compter soit sur la chance aveugle, soit sur le blocage des proies.

  • Un prédicteur du mouvement des proies est fondamentalement obligatoire. Je ne peux pas imaginer battre un programme basé sur un prédicteur sans avoir votre propre prédicteur.

Chasse à la queue

Le moyen le plus inefficace d'attraper une proie est de la chasser à sa poursuite. En supposant qu'un seul prédateur poursuive une seule proie et qu'aucune influence externe (murs, autres proies, etc.) ne soit exercé, une poursuite en queue pourrait durer éternellement. Dès que vous entrez dans le rayon de vision de la proie de 30 unités, la proie s'enfuit à la vitesse 6 de votre 6.1, vous gagnez donc une distance d'un tour: en ligne droite, vous avez besoin d'environ 300 tours pour l'obtenir.

En prenant en compte la taille de l'arène, une proie se déplacera au maximum dans la diagonale d'un carré de 500 unités avant de heurter un mur ou un coin, ce qui prendra au plus 117 tours.

La stratégie gagnante consiste évidemment à trouver un moyen de ralentir les proies, notamment en ayant soit un autre prédateur, soit un mur / un coin en face de celui-ci.

Prédicteur

Avec une vitesse de proie de 6, une proie peut se déplacer sur une zone de 36 * pi carrés. Avec un rayon de capture de 1, une estimation à l'aveugle de la prochaine destination de la proie a une chance de réussir de 1/36 * pi (environ 1%). Il est clair que quelque chose doit être fait pour améliorer cela!

En regardant le code du moteur de simulation, vous pouvez voir que les entrées sont:

  • proies visibles et positions des prédateurs
  • vitesses de proie précédentes

Bien que toutes les positions soient disponibles, les vitesses précédentes des proies ne le sont pas. Le seul moyen de calculer ces vitesses serait de suivre chaque proie d'un tour à l'autre, ce qui est presque impossible à faire (à moins de mettre en œuvre un algorithme de suivi du mouvement très intelligent). Ainsi, un prédicteur peut facilement reproduire tous les termes du calcul, à l'exception de la contribution de vitesse qu'il faut deviner.

Dans le cas d'une seule proie, la vitesse peut être suivie sans trop de problèmes, ce qui permet de créer un prédicteur "parfait" pour attraper une proie isolée du troupeau. Ce qui est fondamentalement tout ce dont vous avez besoin pour la phase finale, lorsque les proies sont trop peu nombreuses pour interagir les unes avec les autres. Lorsque les proies sont abondantes et que l'effet de troupeau est assez puissant pour tromper le prédicteur, sa densité même de proies compensera les erreurs (si vous n'attrapez pas celle que vous visiez, il est probable que vous obtiendrez l'un de ses meilleurs amis ).

Goading proies

Avec la connaissance exacte du calcul de la vitesse des proies, il est possible de "diriger" une proie donnée vers une direction souhaitée, en ajustant la position du prédateur.

Cela permet de coincer une proie contre un mur ou de la diriger vers un autre membre de la meute. J'ai essayé des stratégies raffinées, comme pincer une proie entre deux membres de la meute. Malheureusement, cela s’est avéré moins efficace que la routine actuelle consistant à «épingler et scanner», car garder deux prédateurs occupés à chasser une seule proie laisse l’opposition trop libérée pour explorer le pâturage.

Voler des proies

L'une des caractéristiques du comportement des proies est que l'influence d'un prédateur augmente proportionnellement à sa distance par rapport à la proie (à condition qu'il reste dans le rayon de vision de la proie). Plus un prédateur s'approche d'une proie, moins la proie s'en éloignera.

Cela signifie que lorsque deux prédateurs se font concurrence pour attraper une proie, le plus proche est obligé de l'attraper en premier. Même un concurrent extrêmement intelligent qui parviendrait à se positionner juste devant l'axe de chasse / proie effrayerait en fait la proie dans la gueule du concurrent.

Pour réussir à voler une proie, il faut au moins deux prédateurs. L'un ira chercher la mise à mort, et l'autre restera juste dans le rayon de vision de la proie, aussi loin que possible de la proie pour maximiser l'influence et guider la proie vers le chasseur.

En outre, chaque changement de direction permettra à la compétition de couper le coin de la proie, et rester derrière le concurrent n'est possible que si le "goader" était suffisamment proche de la proie au début de l'action.

Le vol de proie n'a donc qu'une chance de réussir si les positions initiales des "voleurs" sont favorables et que vous pouvez épargner au moins un deuxième prédateur. D'après mon expérience, cela ne vaut pas la complexité.

Changements suggérés

Pour permettre des stratégies plus complexes, déplacer le prédateur au-dessus de la vitesse de pointe de la proie pourrait avoir un coût en points de famine, proportionnel à l'excès de vitesse. Par exemple, par exemple, passer à la vitesse 6 est gratuit et chaque point de vitesse supérieur à 6 coûte 100 points de faim (aller à 6,3 coûte 30 points de faim par tour, brûler 1000 points de faim permettrait d'atteindre la vitesse 16 pour un tour attraper une proie le faisant!).

Au lieu de donner la mort à un prédateur au hasard lorsque plusieurs sont suffisamment proches pour manger une proie, je suggère de diviser le gain (par exemple, 3 prédateurs obtiendraient 333,33 points de faim chacun). Cela permettrait des stratégies de fin de partie plus intrigantes (par exemple, l'observation d'un prédateur ennemi deviendrait utile si vous pensez avoir plus de points à craindre).

La couleur spéciale pour le premier paquet est plutôt difficile à voir. Je suggère le cyan ou l'orange au lieu du bleu.


Enfin un autre concurrent! J'ai délibérément mis en œuvre tous les points que vous avez mentionnés, à l'exception du vol de proie, pour lequel je suis satisfait des effets secondaires actuels. D'après vos commentaires, il semble que vous gagniez contre mon Clairvoyant? C'est intéressant, je vérifierai demain. = D. En outre, vous pouvez essayer ma mise à jour graphique pour voir de meilleurs graphiques (du moins selon moi).
presque la moitié du

Je ne gagne pas tout le temps. Cela dépend de qui est le plus proche des dernières proies quand elles se reproduisent. Mes chiens capables pourraient avoir un avantage statistique, cependant. J'ai également peaufiné le moteur de simulation pour afficher chaque équipe dans une couleur différente, mais au final, elle s'est révélée trop colorée à mon goût. Je me suis donc tournée vers l'orange au lieu de bleu pour la 1ère équipe et de tous les autres rouges.

Wow, je viens de courir votre soumission. C'est fou, je ne savais pas que tu pouvais faire en sorte que la prière reste immobile comme ça. Et je me suis senti un peu trompé que lorsque la proie est précisément sur le bord, mes prédateurs ne pourront pas la détecter, c'est vraiment un gros inconvénient pour moi.
moitié

C'est de l'arithmétique vectorielle pour vous :). C’est ce que j’ai essayé d’expliquer dans mon message: pour une seule proie, vous connaissez tous les paramètres de déplacement (vitesse antérieure incluse), de sorte que vous pouvez calculer une position de prédateur qui produira la vitesse de proie appropriée pour la maintenir immobile près d’un mur.

1
Eh bien, la solution d'équation est codée en dur (aucune itération n'est nécessaire). En gros, vous calculez le vecteur que la proie devrait utiliser pour la vitesse de virage suivante si votre prédateur n’y était pas. Étant donné la direction dans laquelle vous voulez que la proie aille, vous en déduisez la différence de vecteur nécessaire pour que la vitesse de la proie pointe dans cette direction. Cette différence de vecteur vous donne la position du prédateur par rapport à la proie. Cela vous laisse un degré de liberté qui vous permet (dans certaines limites) de choisir une distance par rapport à la proie.

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

Vous aurez besoin de la plate-forme haskell pour exécuter cela. Ensuite, vous utilisez la runhaskellcommande pour l'exécuter. Ma meute attend que la proie vienne à eux.


+1 pour une solution squelette dans une nouvelle langue. Vous êtes probablement content que les gens construisent de nouvelles stratégies en plus de cela?
Trichoplax

Bien sûr, pourquoi pas. (Bien que cela ne fasse en quelque sorte rien d'autre que produire une sortie constante et quitter sur "dead \ 0", donc je ne suis pas sûr que ce serait très utile.)
PyRulez

Je conseillerais quand même à ceux qui courent d'utiliser cette -silentoption, cependant ...
Geobits

3

Ce n'est pas une entrée, je suis toujours intéressé à ajouter une couleur personnalisée pour chaque entrée participante dans ;)

De plus, le processus alimentaire ne se visualise pas en changeant de couleur, mais en modifiant sa taille afin que nous puissions voir plusieurs événements alimentaires en peu de temps.

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;
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.