Jogos Vorazes - Eat or Die

60

Jogos Vorazes - Eat or Die

Se você não comer, você morre. Se você come, você vive (até morrer). Você vai morrer, então tente morrer por último.

visão global

Há uma ilha povoada com um rebanho de presas. Você controla um pacote de cinco predadores. Seu objetivo é manter sua mochila viva. Faça isso comendo presas. As presas tendem a fugir de predadores e tentam permanecer em um rebanho de outra maneira. Naturalmente, seu bando estará no mesmo campo que todos os outros bando , então a competição tentará comê-los antes que você possa. Não deixe isso desanimar você, ou você vai morrer de fome.

Como jogar

Crie e envie um programa de linha de comando para direcionar seu pacote. Ele receberá informações de estado do programa de controle no STDIN e comandos de saída no STDOUT. O formato é descrito em detalhes abaixo. Cada programa será executado apenas uma vez e deve permanecer em execução até que não haja mais membros do pacote ativos. Você precisará ler as entradas assim que chegar e responder rapidamente. Há um tempo limite estrito de 200ms para cada resposta. Se você ainda não respondeu, seu pacote não receberá novas instruções para o turno atual.

Se o seu programa não puder ser executado pelo controlador, ele não será considerado válido. Inclua a string da linha de comando que precisarei usar para executar seu envio. Se houver instruções especiais (para configurar compiladores, etc.), inclua-as. Se eu não conseguir fazê-lo funcionar, solicitarei ajuda nos comentários. Se você não responder, não poderei aceitar seu envio.

O torneio será realizado em um sistema Linux de 64 bits. Lembre-se disso ao fornecer as instruções necessárias.

Detalhes

  • A posição e direção de cada criatura estão na forma de um par de números de ponto flutuante de precisão dupla (por exemplo double) representando suas xe ycoordenadas, respectivamente.

  • Cada criatura é considerada um ponto. Isso significa que eles podem se sobrepor e ocupar o mesmo espaço. Você não será deixado de lado, e não há conceito de colisão com outras criaturas.

  • A ilha é uma praça, 500 unidades ao lado. Se você tentar se aventurar além desses limites, ficará preso na borda. A origem {0,0}está no canto superior esquerdo, xaumentando para a direita e yaumentando para baixo. Novamente, o mapa não quebra .

  • O jogo começa com 1500 + (packCount * 50) presas. Eles estarão reunidos no centro da ilha, mas rapidamente decidem começar a se mover.

  • Os pacotes serão organizados em um círculo uniformemente espaçado ao redor do perímetro. A ordem dos pacotes é embaralhada; portanto, não conte com a partida em um local específico.

  • Presas podem ver todos os outros animais em um raio de 30 unidades. Eles podem se mover no máximo 6,0 unidades por turno.

  • Os predadores podem ver todos os outros animais em um raio de 50 unidades. Eles podem se mover a um máximo de 6,1 unidades por turno. Isso significa que eles podem ver presas antes de serem vistos e (apenas) fugir deles.

  • Os predadores vivem e morrem de acordo com o nível de fome . Começa às 1000 e diminui um a cada turno. Se, após o movimento, um predador estiver dentro de 1 unidade de presa, ele o comerá automaticamente. Isso remove a presa e define a fome do predador para 1000. Cada predador pode comer apenas uma presa por turno. Se houver mais de um dentro do alcance, ele comerá o que o loop chegar primeiro (não necessariamente o mais próximo). Um predador morre se sua fome chegar a zero.

  • Os pacotes começam com cinco membros cada. A cada 5000 turnos, todos os pacotes ainda no jogo geram um novo membro. Será colocado dentro do alcance visível de um membro do bando. Verifique se suas entradas podem lidar com mais de cinco membros.

  • A cada 1000 turnos, mais presas aparecem. O número de novas presas será o número de predadores vivos menos um.

  • Predadores não podem atacar outros predadores. Eles comem presas quando pegam. É isso aí.

  • A ordem dentro de um turno é:

    • Todas as presas tomam decisões
    • Todos os predadores tomam decisões
    • Todas as presas
    • Todos os predadores se movem / comem
  • A ordem em que cada pacote toma suas decisões / movimentos será aleatória a cada turno.

Protocolo (Geral)

Todas as comunicações são feitas no formato de sequência US-ASCII. Os números são convertidos em strings usando Java Double.toString()ou Integer.toString(). Sua saída deve ser formatada para que possa ser lida pelo Java Double.valueOf(String)(você não estará produzindo números inteiros). Para detalhes sobre formatos analisáveis, consulte a documentação paraDouble . Todos os campos em uma linha são separados pelo \tcaractere padrão e as novas linhas \n. A cadeia inteira será encerrada com um byte nulo \0.

Nos exemplos abaixo, estou usando <>para marcar os campos por questões de legibilidade. Eles não estão presentes nas strings reais.

Protocolo (entrada)

A sequência de entrada varia em comprimento, dependendo de quantas criaturas são visíveis para o seu pacote. Pode exceder 100k caracteres, esteja preparado para isso. A configuração básica é:

  • Linha 0: informações básicas sobre o jogo. turné o número do turno atual e as contagens são o número total de presas e predadores restantes no campo. Estes estão integerem forma de string.

    <turn>\t<preyCount>\t<predatorCount>\n
    
  • Linha 1: IDs e níveis de fome exclusivos dos membros do seu grupo. Estes não são fornecidos na mesma ordem para todas as entradas. Use os IDs exclusivos para rastrear membros individuais, não a ordem em que eles aparecem na entrada. Novamente, estes são integercomo strings. Para um pacote de dois, isso seria:

    <id[0]>\t<hunger[0]>\t<id[1]>\t<hunger[1]>\n
    
  • Linha 2: As posições dos membros do seu bando, na mesma ordem que a linha 1 . Estes são doublecomo string:

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

As seguintes linhas são a visibilidade de cada membro do pacote, na mesma ordem que a linha 1 . Estes serão dados em duas linhas por membro.

O primeiro para cada um consiste em locais para a presa que ele pode ver. O segundo são os locais para os predadores que ele pode ver. Esses locais não são únicos como um todo. Por exemplo, se dois membros da matilha puderem ver o mesmo animal, ele estará na sequência de ambos os membros. Além disso, seus próprios membros do bando serão incluídos. Se você quiser excluí-los, compare os locais com seus próprios membros. Todos os locais estão no doubleformato de sequência.

Para cada membro vivo:

<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

Finalmente, o último caractere será \0, no início da próxima linha.

Exceção: Se você receber a entrada dead\0, seu pacote está morto. Termine seu programa normalmente, por favor. O controlador deve desligar todos os processos vivos quando fechado, mas eu prefiro não ter processos zumbis por todo o lado. Como cortesia, você pode incluir um tempo limite de entrada. Por exemplo, minha classe de exemplo termina se não receber entrada por 15 segundos.

Protocolo (Saída)

A saída é simples. Você fornecerá um par de doublevalores para cada membro do live pack. Eles representam o movimento que você gostaria que eles realizassem neste turno. Por exemplo, se sua criatura estiver atualmente em {100.0, 100.0}e você der um comando {-1.0, 1.0}, eles irão para {99.0, 101.0}. Todos os números estarão em uma única linha, separados por tabulação.

Por exemplo, se você tivesse 3 membros da matilha vivos, isso seria uma resposta válida:

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

Este seria mover suas criaturas por {1.0,-1.0}, {2.0,-2.0}, e {3.0,-3.0}. O pedido é o mesmo que o recebido na entrada. Não esqueça o final \0!

Se você fornecer informações inválidas, ocorrerão maus resultados. Se um número único não puder ser analisado como a double, ele se tornará zero. Se a string como um todo não puder ser analisada, nenhuma nova instrução será dada e todo o seu pacote seguirá as instruções da curva anterior.

Todas as direções serão fixadas a uma distância máxima de 6,1 unidades. Você pode se mover mais devagar que isso, se quiser. Por exemplo, {1, 0}você moverá uma unidade. {6,8}(distância 10) moverá você apenas 6,1 unidades e reduzirá para cerca de {3.66, 4.88}. A direção permanece constante.

Importante: O programa de controle lê seu STDOUT e STDERR. Se você lançar uma exceção e imprimir no STDERR, é muito improvável que a mensagem esteja na forma de uma resposta válida. Tente evitar fazer isso.

Programa de Controle / Teste

A fonte do controlador pode ser encontrada aqui em bitbucket.org . Você precisará compilá-lo antes de executar. A classe principal é Gamee todas as classes estão no pacote padrão. Para executar, inclua o comando de cada pacote como um argumento separado. Por exemplo, se você deseja executar um Java ChaserPack e um Python LazyPack.py, você pode usar:

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

No mapa, as presas aparecem em verde e os predadores em vermelho. No entanto, o pacote que for o primeiro pacote fornecido como argumento será colorido em azul. O objetivo é distingui-los mais facilmente para fins de teste. Os predadores também piscarão em branco por cinco quadros quando comerem.

O jogo continuará até o último predador morrer de fome, gravando no console quando ocorrerem eventos de fome ou extinção. Quando o jogo terminar, a pontuação será dada para cada pacote. Se você não quiser ver os eventos de inanição / extinção, use o -silentargumento Então ele produzirá apenas a pontuação final. Você deve passar isso como o primeiro argumento :

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

Incluído é um pacote Java esqueleto chamado GenericPack. Inclui as operações básicas de entrada / saída necessárias. Está aqui para dar um exemplo claro de como analisar e responder. Se você deseja adicionar um modelo em outro idioma, entre em contato.

Também está incluído um predador baseado no modelo ChaserPack,. Não será incluído no torneio e é incluído apenas para fins de teste. Ele tem um desempenho muito ruim, devido a uma falha de segmentação intencional. Se você não pode vencê-lo, continue tentando.

Abaixo está um exemplo de execução do programa de controle (clique para o vídeo). A qualidade do vídeo não é ótima (desculpe), mas você pode ter uma ideia de como a presa se move. ( cuidado: áudio )

captura de tela

Pontuação

O vencedor será determinado pelo torneio, ganhando pontos em cada rodada.

Cada rodada prossegue até que todos os predadores estejam mortos. Cada pacote será pontuado com base em quando seu último membro morreu de fome. Eles receberão pontos com base no pedido. Os pontos serão acumulados por dez rodadas, e o vencedor é o pacote com o maior número total de pontos.

O primeiro lugar de cada rodada receberá 100 pontos. Para cada lugar depois disso, a recompensa será reduzida em 20% (arredondado para baixo). Isso continuará até que os pontos cheguem a zero (após o 17º lugar). Os lugares com mais de 18 anos não receberão pontos pela rodada. Os pacotes que empatarem receberão pontos iguais. Por exemplo:

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

O máximo de pontos possíveis ao longo do torneio é de 1000, desde o primeiro lugar todas as dez vezes.

Se vários programas terminarem o torneio empatado em primeiro lugar, outro torneio de dez rounds será realizado apenas com as entradas do primeiro colocado . Isso continuará até que um vencedor apareça.

Vou tentar executar um torneio aproximadamente semanalmente, ou à medida que novas inscrições chegarem.

Regras adicionais (jogar limpo!)

  • Você não pode ler ou gravar em nenhum recurso externo. Como você não invocará o programa várias vezes, qualquer informação de estado pode ser armazenada internamente.

  • Não interfira com outros processos / envios. Isso não significa não tentar roubar suas presas, fugir delas, etc. Isso significa não interferir na execução do processo. Isso é a meu critério.

  • Os participantes estão limitados a um máximo de três entradas. Se você enviar mais, pontuarei apenas os três primeiros enviados. Se você deseja revogar um, exclua-o.

  • As entradas podem não existir apenas para apoiar outras entradas. Cada um deve jogar para ganhar por seu próprio mérito.

  • Seu programa pode gerar no máximo um processo filho por vez ( total de descendentes, não diretos). De qualquer forma, verifique se você não excedeu o tempo limite. Você não pode invocar a Gameprópria classe de forma alguma.

Resultados - 29 abril 2014

Aqui estão os resultados do último torneio de dez rounds:

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

Pacotes enviados antes das 09:00 EDT 2014/04/29 estão incluídos nesta execução.

Você também pode ver os detalhes de cada rodada . Por alguma razão, decidi numerar as rodadas para trás, para que comece com a "rodada 10".

Atualizações

23/04/2014: FGreg relatou um bug relacionado a tempos limite (obrigado!). Uma correção foi implementada, para que os testadores desejem atualizar o código do programa de controle.

Geobits
fonte
28
Estou gostando dessas perguntas sobre o rei da colina!
Cruncher
2
O @Manu escrevi os exemplos de bots no Windows 7 e testei no win e no linux. Que problemas você está tendo com eles?
Geobits
2
Essas perguntas sobre o rei da colina são impressionantes, e essa é definitivamente interessante. Eu tenho dois pacotes diferentes em andamento agora!
precisa saber é o seguinte
2
@githubphagocyte Eu realmente não quero acabar com um pacote no primeiro tempo limite, simplesmente porque eu já vi programas simples expirarem uma vez a cada 40k + turnos ou similares. Eu cometi a alteração de nomenclatura no controlador. As curvas agora são conhecidas como curvas ao longo do programa, a menos que eu tenha perdido uma em algum lugar.
Geobits
2
@ Geobits eh, por mim tudo bem. Sabe, isso é muito parecido com um projeto de pesquisa que um dos meus professores de física está fazendo, com o qual eu poderia estar ajudando durante o verão. Vou explicar um pouco mais tarde, se puder.
krs013

Respostas:

10

Clarividente

Código atualizado para enfrentar o AbleDogs

Woo hoo! Finalmente supera esse Netcats! Expandi o código existente (créditos para o Geobits!) Com algumas pequenas modificações para criar este pacote de previsão futura. Nada supera os predadores que sabem onde a presa se moverá!

Dos dois testes que fiz, meu grupo sempre venceu o Netcats. Mas isso não será tão bom se não houver outros pacotes, pois a previsão ainda falha se houver muitas outras presas nas proximidades.

Provavelmente, posso incluir o truque do CivilizedBeasts para reduzir substancialmente o número de presas durante os primeiros milhares de turnos.

Feito em 5,21 minutos
Clarividente (1): Turno 9270: Pontuação 100
EcoCamel.pl (3): Turno 8118: Pontuação 80
Netcats (0): Turno 6111: Pontuação 64
RubyVultures.rb (5): Turn 4249: Pontuação 51
RubySpiders.rb (4): Turn 3495: Pontuação 40
CivilizedBeasts (2): Turn 3176: Pontuação 32
ChaserPack (6): Turno 2492: Pontuação 25

Do nome da minha mochila, você deve saber qual estratégia eu uso = D

Editar :

  • Sistema de gerenciamento de pacotes atualizado para não perseguir a mesma presa (e também tentar encontrar a melhor correspondência!)
  • Melhore o processo de errância quando o número de presas for pequeno (isso é crucial para uma vitória!).
  • Melhore os casos especiais quando a versão anterior ficou parada na esquina.
  • Corrigido um erro no algoritmo de detecção de predadores (agora é bastante preciso!)
  • flock[ALIGN]Fator de presa incluído
  • Mantenha uma presa como animal de estimação se a comida for escassa
  • Crie um covil onde o bando agrupe suas presas em
  • Atrair predador próximo a perseguir nossas presas, que elas não vencerão

Contei quantas presas cada pacote come, e aqui está o resultado:

Clarividente (1) consumiu 916 presas em 9270 turnos (0,099 presas / turno)
EcoCamel.pl (3) consumiu 73 presas em 8118 turnos (0,009 presas / turno)
Netcats (0) consumiu 563 presas em 6111 turnos (0,092 presas / turno)
RubyVultures.rb (5) consumiu 77 presas em 4249 turnos (0,018 presas / turno)
RubySpiders.rb (4) consumiu 293 presas em 3495 turnos (0,084 presas / turno)
CivilizedBeasts (2) consumiu 10 presas em 3176 turnos (0,003 presas / turno)
ChaserPack (6) consumiu 43 presas em 2492 turnos (0,017 presas / turno)

Meu bando é muito agressivo, e acho que a maioria das 916 conta com o roubo de presas do Netcats, assim como o RubySpiders.

Infelizmente, o CivilizedBeasts está perdendo devido ao camelo central da EcoCamel.

E o EcoCamel (com fome de 500) é bastante eficiente, come o suficiente para sobreviver até o fim.

Também com este clarividente atualizado, o jogo mal chega a 10.000 turnos.

O código:

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

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

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

    private XY abattoirCorner;
    private double abattoirRadius = 100;

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

    public class AnimalComparator implements Comparator<Animal>{

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    private XY limitSpeed(XY move) {
        if (move.x*move.x+move.y*move.y > MAX_SPEED*MAX_SPEED)
            move.unitize().multiply(MAX_SPEED);
        return move;
    }
}
justhalf
fonte
11
Parece muito bom, o seu realmente é melhor do que os netcats no meu jogo. Mas eu odeio que eu não possa correr contra os outros predadores, já que meus animais fazem um trabalho muito ruim em suas estatísticas (enquanto evilcamel é bom demais). Talvez eu tenha que tentar instalar um compilador perl ou algo assim.
Herjan
Sim, acho que seu método não funciona se houver um predador no meio, conforme discutido na sua resposta. Eu tentei implementar outra versão que se comporta como a sua. Pode mudar a formação dependendo do número de predadores disponíveis, por isso é muito divertido de assistir, embora não seja muito melhor que o seu.
justhalf
Sim, minha estratégia pode ser atualizada de várias maneiras, como outras formações com outra quantia com membros, porque meus animais estão condenados a <4 predadores. Ou lugares aleatórios para se reunir (em vez de apenas o meio), por exemplo. Mas estou com preguiça de implementar isso (agora). E nunca será tão bom quanto este, pois se a presa se abaixar, minha tática simplesmente não funcionará. É quando você precisa de uma fera como a sua (você já mencionou para começar com minha tática e quando a presa fica baixa para usar essa tática). Então eu acho que você já pensou nisso.
Herjan
Estou em outro desafio agora, e o GeoBits parece ter perdido o interesse nesse, então deixarei descansar por um tempo, a menos que os resultados sejam atualizados. Tenho idéias para outras submissões, então espero que esse desafio seja mantido vivo. Vou dar uma olhada na sua atualização, é claro.
15

Netcats

Aqui está um pacote para você começar. Estende a GenericPackclasse incluída no programa de controle. Ele foi aprimorado desde a postagem original e não passa mais fome com um rebanho escasso.

Os Netcats usam uma rede em forma de V para prender presas no canto, onde podem comê-las à vontade. A rede é formada com um membro "principal" no centro. Uma vez que a cabeça come, ela troca de lugar com o membro da matilha mais faminto, já que a cabeça é normalmente a primeira a ter a oportunidade de comer.

A rede começa um pouco pequena, mas aumenta quando o rebanho fica menor, a fim de arrastar o campo com mais eficiência.

Se nenhuma presa é visível, a formação se amplia para um padrão de busca ingênuo, cobrindo a maior parte da ilha.

Quando o pacote chega a dois membros, a rede simplesmente não funciona. Nesse ponto, cada um segue seu próprio caminho, avidamente comendo a coisa mais próxima que pode encontrar e dando um passeio semi-aleatório.

Esta versão sobrevive muito melhor do que os ingênuos Netcats vistos no vídeo vinculado na pergunta.

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

public class Netcats extends GenericPack {

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

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

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

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

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

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

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

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

        Collection<Animal> yummy = getFoods(head);

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

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

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

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

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

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

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

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

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

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

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

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

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

        if (target == null)
            return false;

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

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

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

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

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

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

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

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

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

Aranhas Rubi

Como às vezes menos é mais e muitas soluções provavelmente tentariam encurralar a presa de qualquer maneira ...

Eu pensei que minha mochila pudesse apenas se separar e esperar que outros fizessem o trabalho.

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

Advertência: Na verdade, ele não fica em execução, nem lê as entradas quando chega nem responde rapidamente. Ainda assim, como funciona bem com o controlador, espero que se qualifique sem mais ajustes.

Legat
fonte
4
+1 primeira solução parasita. Eu acho que esse tipo de resposta vai elevar a qualidade de outras respostas por brechas eliminando gradualmente ...
Trichoplax
@githubphagocyte Eu tinha um parasita mais inteligente em mente, mas isso é mais eficiente em termos de tempo ao vivo / linhas de código. Espero encontrar tempo para implementá-lo.
Legat
Talvez a @Synthetica esteja codificando minha ideia agora. Ou se a sua ideia é ainda um outro, poderíamos ter em breve mais parasitas então caçadores;)
Legat
11
@githubphagocyte, temos permissão para fazer três entradas, por isso postarei outro pacote assim que estiver pronto. Ainda assim, acho interessante que este tenha sido codificado nesse meio tempo e possa se mostrar mais eficaz. Ele tira proveito do Netcats muito bem e realmente sobrevive ao meu primeiro pacote de caçadores.
Legat
3
Isso pode entrar como está, mesmo que demorei um segundo para descobrir o porquê. Parece fazer melhor quanto mais Netcats você adicionar (o que faz sentido). +1 de mim, vamos ver que tipo de caçadores saem para evitar cantos :)
Geobits
11

CivilizedBeasts

Finalmente, hora de mostrar minhas bestas!

Minha raça acha que a caça é um tanto primitiva, então eles trabalham juntos em uma equipe de 4 pessoas e abandonam seu quinto aliado, porque: menos predadores = mais presas para si mesmos. O que eles basicamente fazem é o que os humanos fazem, pegam presas e cuidam bem do gado;)

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

Torna-se bastante difícil para meus seios sobreviverem com menos de 200 presas no turn + -12.000, apenas com Netcats inimigos no jogo. Você ficará feliz com esta raça, pois ela realmente devora enormes quantidades de presas com velocidade, como nenhuma outra raça consegue (não que abates rápidos e grandes garantam a vitória, mas influencia o (longo) tempo que uma rodada inteira leva consideravelmente).

Herjan
fonte
3
Se, por " cuidar bem deles ", você quer dizer " agrupá-los repetidamente no meio e matá-los / comê-los ", então sim, eles fazem isso bem. +1
Geobits
É engraçado, com a versão não alterada (original) do Evil Camels, as táticas civilizadas são totalmente ineficientes por causa do 'camelo central'.
precisa saber é o seguinte
11
@VadimR Merda, obrigado por atualizar seu camelo: PI não pode testá-lo já que não é Java, mas eu sei que minha estratégia é tipo de inútil com predadores no meio do meu território: P
Herjan
5
É Herjan de novo! Também "fica muito difícil para meus seios sobreviver com menos de 200 presas" (grifo nosso). Eu não sabia que a vitalidade de seus seios depende do número de rapina em uma simulação de computador ....
Justin
5

Abutres de rubi

Aí vem um pacote de parasitas mais ativos . Eles estão tentando cercar o predador em movimento mais próximo , para que possam roubar sua presa . Eles são um pouco dependentes da sorte, pois não têm uma maneira inteligente de escolher quem seguir, mas geralmente estão atacando caçadores e às vezes aranhas .

Eles não estão completamente terminados, como eu postei isso para aumentar o tempo :)

Espero:

  • fazê-los procurar predadores fora do campo de visão
  • leve em consideração a presa - muitas vezes uma delas está entre outro bando e a presa!
  • comece a girá- los para evitar que um morra de fome quando todos os outros estiverem bem alimentados

22 de abril de 2014: Adicionado tédio , que os torna menos pegajosos e permite que caçam presas por conta própria e procurem predadores

class Animal
  attr_accessor :x, :y
end

class Hunter < Animal
  attr_accessor :id, :bored

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

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

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

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

  def member_by_order index
    member_by_id @order[index]
  end

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

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

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

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

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

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

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

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

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

Pack.new
Legat
fonte
Você definitivamente precisa de mais "caçadores" na mistura. Como é, eles tendem a se apegar a outros parasitas (já que essa é a maioria em campo). Eu gosto de vê-los, no entanto, e posso ver como eles seriam eficazes com uma mistura diferente de concorrentes.
Geobits
Ah, sim, no meu ambiente de teste eu tenho dois outros pacotes de caçadores. Sem eles, os abutres provavelmente são completamente ignorantes. Especialmente, os netcats podem trabalhar rapidamente nos cantos sem serem vistos do meio.
Legat
Eu acho que sei o que pode estar incomodando eles em particular. Dança de guerra dos camelos do mal. @ Geobits, que tal colocar as lutas no Youtube? 10 rodadas não é demais para permanecer assistível. Obviamente, seria necessário um QG. Não esperando milhões de espectadores, mas seria divertido para ver como seus pacotes de realizar e talvez torcer por eles um pouco :)
Legat
11
O torneio completo pode ser um pouco longo (~ 8 minutos por rodada agora) para prender a atenção, mas gravar uma rodada de "espectador" pode funcionar. Pensarei em execuções futuras.
precisa saber é o seguinte
@ Geobits A velocidade varia muito durante uma rodada de 8 minutos? Gostaria de saber se vale a pena gravar um quadro por turno para que eles possam ser reproduzidos a uma taxa constante, em vez de diminuir a velocidade durante partes intensamente computacionais. Para fins do YouTube, quero dizer.
Trichoplax
5

Evil Eco Camels

Edit: Mutação # 2. Ah, não, estou atrasado com a implementação da previsão do movimento das presas, para ser o primeiro a vencer os Netcats. OK, que assim seja.

Esta mutação tem $hunger_criticalvariável (constante). Mudá-lo para um valor acima de 1000 faz com que Camels sempre caçam, como Clarividentes. Então:

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

Se $hunger_criticalestiver definido como, por exemplo, 500 (como abaixo), então meus Camelos (depois de ver os horrores da civilização ) tentam se comportar de maneira ecológica (por isso eles mudaram o nome de sua raça), ou seja, matam apenas quando estão com fome. Se não estiverem com fome, eles patrulham áreas críticas da ilha - centro e cantos, para evitar massacres sem sentido por alguns outros caçadores. Bem, com o centro, mais ou menos funciona. A idéia de circular nos cantos era afastar a presa e dificultar a vida dos gatos e parasitas. Bem, isso não funciona. Presas estúpidas entram nos cantos de qualquer maneira.

É interessante, também, que o flock[ALIGN]componente só possa ser adivinhado por predadores, e minha implementação seja diferente da justhalf. Eu tenho medo há algum pequeno bug no meu rip-off implementação de código Geobits', observando / comparando caça individual de Camelos vs clarividentes.

E o programa está meio longo agora, desculpe.


Edit: Mutação # 1. A ilha acaba sendo bastante radioativa (o que explica a falta de vegetação e a natureza inexplicável das criaturas 'presas'), então aqui está a primeira mutação dos meus camelos. Qualquer um deles pode se tornar um caçador solo, se estiver com fome ou se não houver espaço livre para todos. Hunter tenta perseguir ativamente presas próximas. Se não houver, ele patrulha em um amplo círculo ao redor do centro da ilha e persegue a criatura mais próxima quando a encontra. Infelizmente, a direção da presa se torna imprevisível quando está perto de seu enxame (vale a pena investigar ...), então a perseguição a solo não é muito eficiente. Mas, se for bem-sucedido, o Camel vai digerir no canto livre mais próximo (se houver). Quando o nível de fome está abaixo de certo nível, qualquer camelo abandona seu canto (provavelmente xingando Netcats ('onde está a comida?' )) e fica livre em roaming por conta própria. E assim por diante.


A mesma piada contada duas vezes não é engraçada, mas (1) eu tive que começar de algum lugar e sou novo nessas coisas, (2) Honesto, pensei em táticas de canto (e quem não o fez?), Assistindo Netcats, antes de Ruby Aranhas apareceram na ilha.

Então, já ouviu falar em camelos carnívoros? Um dia, animais pobres acordaram nesta ilha abandonada por Deus e não encontraram grama nem árvores, mas muitas coisas estranhas, verdes, embora comestíveis, que se movem rapidamente (muito irritantes). Não tendo hábitos de caça (mas eles sofrerão mutações em breve, espero), meus Camelos desenvolveram um esquema muito maligno para sobreviver: eles se dividem e se dividem em um dos quatro cantos, e o quinto deles vai para o centro (para morrer primeiro, como acontece). Em seus destinos, eles esperam pacientemente, realizando uma espécie de dança de guerra de camelo, ou talvez apenas tentem não pisar em outros animais já lá, aranhas e tudo ...

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

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

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

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

sub distance {
    return sqrt(&sq_dist)
}

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

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

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

while (1) {

    # Get input

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

    # Update my pack

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

    # Bury dead animals, retrieve their roles

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

    # See that everyone has a role and lives accordingly

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

    # And let the cruel world know about it

    print @out;
    print "\0"
}

__END__
user2846289
fonte
5
Esse deve ser o script perl mais legível que eu já vi neste site até hoje.
Geobits
Você precisa ir para exatamente o canto para espantar-los eficazmente, caso contrário, você só vai realmente participar na chacina de Netcats, haha
justhalf
@ justhalf, é como eu disse: o plano não funcionou. Parasitas sentados nos cantos também não afastaram a presa. Hm-m, talvez 2 ou mais animais patrulhando um canto ajudem.
user2846289
Seus camelos são muito bons, na verdade! Felizmente (para mim) eu melhorei meus clarividentes, então na maioria das vezes (nem sempre), minha matilha vence a sua durante a última batalha. Interessante!
justhalf
11
Se você estiver a menos de 8 (20-2 * 6) unidades da presa, podemos ver qualquer movimento de todas as outras presas que estejam dentro de 30 unidades da presa no turno atual. E a vecpropriedade é basicamente o deslocamento da curva anterior para a curva atual. E, como eu disse, combinamos na curva anterior para descobrir qual presa segue por qual caminho, não podemos confiar na ordem das presas. Isso é possível porque as presas geralmente (no cenário típico) mantêm distância suficiente uma da outra (> 12 unidades) e, portanto, na maioria das vezes podemos combinar as presas no turno anterior ao turno atual.
precisa saber é
4

AbleDogs - PHP

Esses cães simpáticos aprenderam a morder os bezerros de uma presa para cutucá-la pelas paredes. Eles também gostam de vagar pelo pasto em busca de novas presas. Por fim, eles foram ensinados a não comer, a menos que realmente precisem das calorias.

Coloque o código em um AbleDogsarquivo e execute-o comphp 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();
}
?>

Considerações gerais

  • É o fim do jogo que conta. Você pode ter o algoritmo de caça mais inteligente de todos os tempos, se não detectar e capturar as últimas presas mais rapidamente que a oposição, você perde.

  • Se seus predadores não puderem capturar uma presa sozinha (ou pelo menos em pares), você estará brindando assim que a densidade da presa cair baixa o suficiente para depender da sorte cega ou do bloqueio das presas nas esquinas.

  • Um preditor de movimento de presas é basicamente obrigatório. Não consigo imaginar vencer um programa baseado em preditores sem ter seu próprio preditor.

Caça à cauda

A maneira mais ineficiente de capturar uma presa é persegui-la. Supondo que um único predador persiga uma única presa e sem influências externas (paredes, outras presas, etc.), uma perseguição pela cauda pode durar para sempre. Assim que você entra no raio de visão da presa de 30 unidades, a presa foge na velocidade 6 para o seu 6,1, para ganhar 0,1 distância por turno: em uma linha reta, você precisará de cerca de 300 voltas para obtê-lo.

Levando em conta o tamanho da arena, uma presa viajará no máximo na diagonal de 500 unidades quadradas antes de atingir uma parede ou um canto, o que levará no máximo 117 voltas.

A estratégia vencedora é, obviamente, encontrar uma maneira de desacelerar as presas, ou seja, tendo outro predador ou uma parede / canto à sua frente.

Preditor

Com uma velocidade de presa de 6, uma presa pode se mover para uma área de 36 * pi unidades ao quadrado. Com um raio de captura de 1, uma estimativa cega de onde a presa será a próxima tem uma chance de 1/36 * pi (cerca de 1%) de sucesso. Claramente, algo precisa ser feito para melhorar isso!

Observando o código do mecanismo de simulação, você pode ver que as entradas são:

  • presas visíveis e posições predadoras
  • velocidades de presas anteriores

Embora todas as posições estejam disponíveis, as velocidades anteriores de presa não. A única maneira de calcular essas velocidades seria rastrear todas as presas de uma volta para a outra, o que é quase impossível de fazer (a menos que você implemente um algoritmo de rastreamento de movimento muito inteligente). Portanto, um preditor pode reproduzir facilmente todos os termos da computação, exceto a contribuição de velocidade que deve ser adivinhada.

No caso de uma única presa, a velocidade pode ser rastreada sem muitos problemas, o que permite criar um preditor "perfeito" para capturar uma presa isolada do rebanho. O que é basicamente tudo o que você precisa para o final de jogo, quando as presas são muito poucas para interagir umas com as outras. Quando as presas são abundantes e o efeito do rebanho é forte o suficiente para enganar o preditor, a enorme densidade de presas compensará os erros (se você não pegar a que estava almejando, é provável que encontre um de seus amigos mais próximos )

Goading presas

Com o conhecimento exato do cálculo da velocidade da presa, é possível "orientar" uma presa em direção à direção desejada, ajustando a posição do predador.

Isso permite prender uma presa contra uma parede ou direcioná-la para outro membro da matilha. Tentei algumas estratégias refinadas, como beliscar uma presa entre dois membros da matilha. Infelizmente, isso se mostrou menos eficiente do que a rotina atual de "prender e escanear", pois manter dois predadores ocupados para perseguir uma única presa deixa a oposição com muitos predadores livres para explorar o pasto.

Roubar presas

Uma característica do comportamento das presas é que a influência de um predador aumenta proporcionalmente à sua distância da presa (desde que permaneça dentro do raio de visão da presa). Quanto mais próximo o predador chegar de uma presa, menor será a presa.

Isso significa que quando dois predadores competem para capturar uma presa, o mais próximo é obrigado a obtê-la primeiro. Mesmo um competidor super inteligente que conseguisse se posicionar bem na frente do eixo caçador / presa basicamente assustaria a presa nas mandíbulas do competidor.

Para conseguir roubar uma presa, são necessários pelo menos um par de predadores. Um irá para a matança, e o (s) outro (s) permanecerá (m) dentro do raio de visão da presa, tanto quanto for possível, para maximizar a influência e incitar a presa em direção ao caçador.

Além disso, toda mudança de direção permitirá que a competição diminua a curva em direção à presa, e permanecer atrás do competidor só é possível se o "aguilhão" estiver perto o suficiente da presa no início da ação.

Portanto, o roubo de presas tem apenas uma chance de sucesso se as posições iniciais dos "ladrões" forem favoráveis ​​e você puder poupar pelo menos um segundo predador. Na minha experiência, isso não vale a complexidade.

Alterações sugeridas

Para permitir estratégias mais complexas, mover o predador acima da velocidade máxima da presa pode ter um custo em pontos de fome, proporcional ao excesso de velocidade. Digamos, por exemplo, subir a velocidade 6 é gratuito e todo ponto de velocidade acima de 6 custa 100 pontos de fome (ir para 6,3 custa 30 pontos de fome por turno, queimar 1000 pontos de fome permitiria atingir a velocidade 16 por uma volta - e morrer se você não pegue uma presa fazendo isso!).

Em vez de dar a morte a um predador aleatório quando mais de um está perto o suficiente para comer uma presa, sugiro dividir o ganho (por exemplo, 3 predadores receberiam 333,33 pontos de fome cada). Isso permitiria estratégias mais intrigantes para o final de jogo (sombrear um predador inimigo se tornaria útil se você considerar que tem mais pontos de fome, por exemplo).

A cor especial para o primeiro pacote é bastante difícil de ver. Sugiro ciano ou laranja em vez de azul.


fonte
Finalmente outro concorrente! Eu propositalmente implementei todos os pontos que você mencionou, exceto o roubo de presas, pelo qual fiquei satisfeito com o efeito colateral atual. Pelos seus comentários, parece que você está ganhando contra o meu Clarividente? Isso é interessante, vou verificar amanhã. = D. Além disso, você pode tentar minha atualização da GUI para ver melhores gráficos (pelo menos de acordo comigo).
justhalf
Eu não estou ganhando o tempo todo. Depende de quem está mais próximo das últimas presas quando elas aparecem. Meus cães capazes podem ter uma vantagem estatística, no entanto. Também modifiquei o mecanismo de simulação para exibir cada equipe em uma cor diferente, mas no final ficou colorido demais para o meu gosto, então decidi usar laranja em vez de azul no 1º time e todos os outros vermelhos.
Uau, acabei de enviar sua inscrição. Isso é loucura, eu não sabia que você pode fazer a oração parar assim. E me senti um pouco enganado que, quando a presa está exatamente no limite, meus predadores não serão capazes de detectá-las, isso é definitivamente uma grande desvantagem para mim.
justhalf 12/09
Isso é aritmética vetorial para você :). Isso é o que tentei explicar no meu post: para uma única presa, você conhece todos os parâmetros de movimento (velocidade anterior incluída), para poder calcular uma posição de predador que produzirá a velocidade apropriada da presa para que ela fique parada perto de uma parede.
11
Bem, a solução da equação é codificada (não são necessárias iterações). Basicamente, você calcula o vetor que a presa deve usar para a velocidade do próximo turno, se o seu predador não estiver lá. Dada a direção que você deseja que a presa siga, você deduz a diferença de vetor necessária para fazer com que a velocidade da presa aponte nessa direção. Essa diferença de vetor fornece a posição do predador em relação à presa. Isso deixa você com um grau de liberdade que permite (dentro de certos limites) selecionar uma distância da presa.
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

Você precisará da plataforma haskell para executar isso. Então você usa o runhaskellcomando para executá-lo. Minha matilha espera a presa chegar até eles.

PyRulez
fonte
+1 para uma solução de esqueleto em um novo idioma. Presumivelmente, você está feliz que as pessoas criem novas estratégias além disso?
Trichoplax
Claro, por que não. (Embora, ele tipo de não fazer nada além de produzir uma saída constante e sair em "morto \ 0", então eu não tenho certeza se ele seria muito útil.)
PyRulez
Eu ainda aconselho a qualquer um que administra este para usar a -silentopção, embora ...
Geobits
3

Não é uma inscrição, estou sempre interessado em adicionar cores personalizadas para cada inscrição participante no ;)

E também o processo alimentar não é visualizado alterando a cor, mas alterando o tamanho, para que possamos ver vários eventos alimentares em pouco tempo.

Game.java

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

import javax.swing.JFrame;

public class Game {

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

    JFrame frame;
    BufferedImage img;
    Color[] colors;

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

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

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

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

        System.exit(0);
    }

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

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


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

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

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

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

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

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

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

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

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

        frame.repaint();
    }

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

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

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

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

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

        map.rebuildLists(preys, predators);
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Predator.java

import java.awt.Graphics2D;


public class Predator extends Creature {

    static int count = 0;

    int id;
    int hunger;
    Pack pack;

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

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

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

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

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