空腹ゲーミング-食べるか死ぬ


60

空腹ゲーミング-食べるか死ぬ

食べなければ死ぬ。食べれば、死ぬまで生きます。あなた死ぬので、最後に死ぬようにしてください。

概要

獲物の群れが住む島があります。5つの捕食者のパックを制御します。あなたの目的は、パックを生かし続けることです。獲物を食べることでこれを行います。獲物は捕食者から逃げる傾向があり、そうでなければ群れにとどまるようにします。もちろん、あなたのパックは他のすべてのパックと同じフィールドにあるので、競争はあなたができる前にそれらを食べようとします。これであなたを落胆させないでください。

遊び方

パックを送信するコマンドラインプログラムを作成して送信します。STDINで制御プログラムから状態情報を受け取り、STDOUTでコマンドを出力します。この形式の詳細は以下のとおりです。各プログラムは1回だけ実行され、パックメンバが生きなくなるまで実行を続ける必要があります。入力があったときに入力を読み取り、すばやく応答する必要があります。各応答には200ミリ秒の厳密なタイムアウトがあります。それまでに応答しない場合、パックは現在のターンの新しい指示を受け取りません。

プログラムをコントローラーで実行できない場合、そのプログラムは有効とは見なされません。送信を実行するために使用する必要があるコマンドライン文字列を含めてください。特別な指示(コンパイラのセットアップなど)がある場合は、それらを含めてください。機能しない場合は、コメントをお願いします。あなたが応答しない場合、私はあなたの提出物を受け入れることができません。

トーナメントは64ビットLinuxシステムで開催されます。必要な指示を与えるときは、このことに留意してください。

詳細

  • 各クリーチャーの位置と方向は、それぞれと座標をdouble表す一対の倍精度浮動小数点数(例:)の形式です。xy

  • 各クリーチャーはポイントと見なされます。これは、それらが重複して同じスペースを占有できることを意味します。脇にぶつかることはなく、他のクリーチャーとの衝突という概念もありません。

  • 島は一辺が500単位の正方形です。これらの境界を越えてベンチャーしようとすると、エッジに固定されます。原点{0,0}は左上xにあり、右にy向かって増加し、下に向かって増加します。繰り返します、マップはラップしません

  • ゲームは1500 +(packCount * 50)の獲物から始まります。彼らは島の中心に集まりますが、すぐに動き始めることにします。

  • パックは、周囲に等間隔の円で配置されます。パックの順序はシャッフルされますので、特定の場所で開始することを期待しないでください。

  • 獲物の動物は、30単位の半径内の他のすべての動物を見ることができます。1ターンあたり最大6.0ユニットで移動できます。

  • 捕食者は、半径50ユニット内で他のすべての動物を見ることができます。彼らは1ターンあたり最大6.1ユニットで移動できます。これは、彼らが見られる前に獲物を見ることができ、(ほとんど)彼らを追い越すことができることを意味します。

  • 捕食者は自分に従って生き、死ぬ飢餓のレベル。1000から始まり、1ターンごとに減少します。移動後、捕食者が1ユニットの獲物内にいる場合、自動的にそれを食べます。これは獲物を取り除き、捕食者の空腹を1000に設定します。各捕食者は1ターンにつき1つの獲物しか食べることができません。範囲内に複数ある場合、ループが最初に到達したもの(必ずしも最も近いものではない)を食べます。捕食者は、空腹がゼロになると死にます。

  • パックはそれぞれ5人のメンバーで始まります。5000ターンごとに、まだゲーム内にあるすべてのパックが1人の新しいメンバーを生成します。仲間のパックメンバーの可視範囲内に配置されます。エントリが5人以上のメンバーを処理できることを確認してください。

  • 1000ターンごとに、より多くの獲物が発生します。新しい獲物の数は、生きている捕食者の数から1を引いたものになります。

  • 捕食者は他の捕食者を攻撃できません。彼らは獲物を捕まえるときに獲物を食べる。それでおしまい。

  • ターン内の順序は次のとおりです。

    • すべての獲物が決定を下す
    • すべての捕食者が決定を下す
    • すべての獲物の動き
    • すべての捕食者が移動/食べる
  • 各パックが決定/移動する順序は、各ターンでランダム化されます。

プロトコル(一般)

すべての通信は文字列形式で行われますUS-ASCII。数値は、Java Double.toString()またはを使用して文字列に変換されますInteger.toString()。出力は、Javaで読み取れるようにフォーマットする必要がありますDouble.valueOf(String)(整数は出力しません)。解析可能な形式の詳細については、のドキュメントをDouble参照してください。行のすべてのフィールドは標準\t文字で区切られ、改行は\nです。文字列全体がヌルバイトになります\0

以下の例では、<>読みやすくするためにフィールドにマークを付けています。これらは実際の文字列には存在しません。

プロトコル(入力)

入力文字列の長さは、パックに表示されるクリーチャーの数によって異なります。10万文字を超える可能性があるため、そのために準備してください。基本的なセットアップは次のとおりです。

  • 行0:ゲームに関する基本情報。turnは現在のターン番号で、カウントはフィールドに残っている獲物と捕食者の総数です。これらはinteger文字列形式です。

    <turn>\t<preyCount>\t<predatorCount>\n
    
  • 1行目:パックメンバーの一意のIDと飢hungレベル。これらは、すべての入力に対して同じ順序で与えられるわけではありません。一意のIDを使用して、入力に表示される順序ではなく、個々のメンバーを追跡します。繰り返しますが、これらはinteger文字列です。2つのパックの場合、これは次のようになります。

    <id[0]>\t<hunger[0]>\t<id[1]>\t<hunger[1]>\n
    
  • 2行目:パックメンバーの位置。1行目と同じ順序で。これらはdouble文字列です:

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

次の行は、各パックメンバーの可視性であり、1行目と同じ順序です。これらは、メンバーごとに2行で示されます。

それぞれの最初のものは、彼が見ることができる獲物の場所で構成されています。2つ目は、彼が見ることができる捕食動物の場所です。これらの場所は全体として一意ではありません。たとえば、2人のパックメンバーが同じ動物を見ることができる場合、両方のメンバーの文字列に含まれます。また、独自のパックメンバー含まれます。それらを除外する場合は、場所を自分のメンバーと比較することができます。すべての場所はdouble文字列形式です。

生きているメンバーごとに:

<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

最後に、最後の文字は\0、次の行の先頭でになります。

例外:入力を受け取った場合dead\0、パックは死んでいます。プログラムを正常に終了してください。コントローラーは、閉じられたときにすべての生きているプロセスシャットダウンする必要がありますが、私はゾンビプロセスがいたるところにあることを望みません。礼儀として、入力タイムアウトを含めることができます。たとえば、私の例のクラスは、15秒間入力を受け取らないと終了します。

プロトコル(出力)

出力は簡単です。doubleライブパックメンバーごとに値のペアを指定します。これらは、あなたが彼らにこのターンで取りたい動きを表します。たとえば、あなたのクリーチャーが現在にいて{100.0, 100.0}、あなたが彼らにコマンドを与えた場合{-1.0, 1.0}、彼らはに移動し{99.0, 101.0}ます。すべての番号は、タブで区切られた1行に表示されます。

たとえば、3人のパックメンバーが生存している場合、これは有効な応答になります。

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

これにより{1.0,-1.0}、クリーチャーは、、、{2.0,-2.0}およびで移動します{3.0,-3.0}。順序は、入力で受信した順序と同じです。エンディングを忘れないでください\0

無効な入力を与えると、悪い結果が続きます。単一の数値をa doubleに解析できない場合、ゼロになります。文字列全体を解析できない場合、新しい指示は与えられず、パック全体は前のターンからの指示を使用します。

すべての方向は、6.1単位の最大距離に固定されます。必要に応じて、これよりも遅く移動できます。たとえば、{1, 0}1ユニット移動します。{6,8}(距離10)は6.1ユニットのみ移動し、約に減少し{3.66, 4.88}ます。方向は一定のままです。

重要:制御プログラムは、STDOUT STDERRの両方を読み取ります。例外をスローしてSTDERRに出力する場合、そのメッセージが有効な応答の形式になることはほとんどありません。これを避けるようにしてください。

制御プログラム/テスト

コントローラーのソースは、bitbucket.orgにあります。実行する前にコンパイルする必要があります。メインクラスはGameで、すべてのクラスはデフォルトパッケージにあります。実行するには、各パックのコマンドを個別の引数として含めます。たとえば、Java ChaserPackとPython LazyPack.pyを実行する場合、次を使用できます。

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

マップでは、獲物は緑色で、捕食者は赤色で表示されます。ただし、引数として指定された最初のパックは、代わりに青色になります。これは、テスト目的でそれらをより簡単に区別するためのものです。捕食者は、食べると5フレームの間白く点滅します。

ゲームは最後の捕食者がstar死するまで進行し、飢eventsまたは絶滅のイベントが発生するとコンソールに書き込みます。ゲームが完了すると、各パックのスコアが与えられます。飢star /絶滅のイベントを見たくない場合は、-silent引数を使用できます。その後、最終スコアのみが出力されます。これを最初の引数として渡す必要があります

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

という名前のスケルトンJavaパックが含まれていGenericPackます。必要な基本的な入出力操作が含まれています。解析および返信の方法の明確な例を示すためにあります。別の言語でテンプレートを追加する場合は、お知らせください。

また、テンプレートに基づく捕食動物も含まれていChaserPackます。トーナメントには含まれず、テスト目的でのみ含まれます。意図的なターゲティングの欠陥のため、パフォーマンスが非常に悪くなります。あなたがそれを打ち負かすことができないならば、試み続けてください。

以下は、制御プログラムの実行例です(ビデオをクリックします)。ビデオの品質はそれほど高くありません(ごめんなさい)が、獲物がどのように動いているかを感じることができます。(注意:音声

スクリーンショット

得点

勝者はトーナメントによって決定され、各ラウンドでポイントを獲得します。

すべての捕食者が死ぬまで、各ラウンドが進行します。各パックは、最後のメンバーが飢vで亡くなったときに基づいてスコアが付けられます。次に、順序に基づいてポイントが割り当てられます。ポイントは10ラウンド貯まり、勝者は合計ポイントが最も高いパックになります。

各ラウンドの1位には100ポイントが与えられます。その後の各場所で、報酬は20%減ります(切り捨て)。これは、ポイントがゼロに達するまで続きます(17位以降)。プレイス18+はラウンドのポイントを獲得しません。同点のパックは同等のポイントを受け取ります。例えば:

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

トーナメントのコースで可能な最大ポイントは、1位から10回まで1000です。

複数のプログラムが1位のトーナメントを終了する場合、1位のエントリーのみが提出された状態で別の10ラウンドトーナメントが開催されます。これは、勝者が1人現れるまで続きます。

トーナメントはほぼ毎週、または新しいサブミッションが入るときに実行しようとします。

追加ルール(プレイフェア!)

  • 外部リソースを読み書きすることはできません。プログラムを複数回呼び出すことはないため、状態情報は内部に保存できます。

  • 他のプロセス/提出を妨げないでください。これがないではない獲物を盗もうとしません意味、それはプロセスの実行に干渉しないことを意味するなど、彼らを追い越します。これは私の判断です。

  • 出場者は最大3つのエントリに制限されています。さらに提出する場合、提出された最初の3つだけを採点します。取り消す場合は、削除します。

  • エントリは、他のエントリをサポートするためだけに存在することはできません。それぞれが独自のメリットで勝つためにプレーする必要があります。

  • プログラムは、一度に最大1つの子プロセスを生成できます(直接ではなく、子孫の合計)。どちらにしても、タイムアウトを超えないようにしてください。Gameいかなる方法でもクラス自体を呼び出すことはできません。

結果-2014年4月29日

最新の10ラウンドトーナメントの結果は次のとおりです。

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

2014年4月29日09:00 EDTより前に提出されたパックは、この実行に含まれています。

各ラウンド詳細を表示することもできます。なんらかの理由で、ラウンドを後方に番号付けすることにしたので、「ラウンド10」から始まります。

更新

2014/04/23: FGreg がタイムアウトに関連するバグを報告しました(ありがとう!)。修正プログラムが実装されているため、テスターは制御プログラムコードを更新する必要があります。


28
私はこれらの丘の王の質問が好きです!
ランチャー

2
@Manu Windows 7でボットの例を作成し、winとlinuxの両方でテストしました。それらにどんな問題がありますか?
ジオビット14

2
これらのキングオブザヒルの質問は非常に素晴らしく、これは間違いなく興味深いものです。現在、2つの異なるパックを制作中です!
マックホビット14

2
@githubphagocyte最初のタイムアウトでパックを強制終了したくありません。単純なプログラムでさえ、40k回以上のターンごとにタイムアウトするのを見たからです。コントローラーで名前の変更をコミットしました。ターンは、どこかで見逃していない限り、プログラム全体でターンとして知られています。
ジオビット14

2
@Geobitsええ、それは私でいいです。ご存知のように、これは私の物理学の教授の1人が行っている研究プロジェクトに非常によく似ており、夏に私が支援するかもしれません。可能であれば、後で少し説明します。
krs013

回答:


10

千里眼

AbleDogsに対応するために更新されたコード

うわー!最終的にそのNetcatsを打ち負かす!この将来の予測パックを作成するために、若干の修正を加えて既存のコードを拡張しました(Geobitsにクレジット!)。獲物がどこに移動するかを知っている捕食者に勝るものはありません!

私が行った2つのテストから、私のパックは常にNetcatに勝ちました。ただし、他のパックが存在しない場合は、近くに他の獲物が多すぎると予測が失敗するため、これはあまりよくありません。

たぶん、最初の数千ターンの間に獲物の数を大幅に減らすためにCivilizedBeastsのトリックを含めることができます。

5.21分で完了
千里眼(1):ターン9270:スコア100
EcoCamel.pl(3):ターン8118:スコア80
Netcats(0):ターン6111:スコア64
RubyVultures.rb(5):ターン4249:スコア51
RubySpiders.rb(4):ターン3495:スコア40
CivilizedBeasts(2):ターン3176:スコア32
ChaserPack(6):ターン2492:スコア25

私のパックの名前から、どの戦略を使用するかを知っているはずです= D

編集

  • 同じ獲物を追跡しないようにパック管理システムを更新しました(また、最適な一致を見つけようとしました!)
  • 獲物の数が少ないときのさまようプロセスを改善します(これは勝利のために重要です!)。
  • 以前のバージョンがちょうど角で立ち往生した特殊なケースを改善します。
  • 捕食者検出アルゴリズムのバグを修正しました(今では非常に正確です!)
  • 含まれる餌食flock[ALIGN]要因
  • 食物が不足している場合、ペットとして1つの獲物を保管してください
  • パックが獲物を集める場所に巣穴を作ります
  • 近くの捕食者を誘惑して獲物を追いかけます

各パックが何匹の獲物を食べるかを数えましたが、結果は次のとおりです。

Clairvoyant(1)は9270ターンで916の獲物を消費しました(0.099獲物/ターン)
EcoCamel.pl(3)は、8118ターンで73獲物を消費しました(0.009獲物/ターン)
Netcats(0)は6111ターンで563の獲物を消費しました(0.092獲物/ターン)
RubyVultures.rb(5)は4249ターンで77の獲物を消費しました(0.018獲物/ターン)
RubySpiders.rb(4)は3495ターンで293獲物を消費しました(0.084獲物/ターン)
CivilizedBeasts(2)は3176ターンで10獲物を消費しました(0.003獲物/ターン)
ChaserPack(6)は2492ターンで43獲物を消費しました(0.017獲物/ターン)

私のパックは非常に攻撃的であり、916カウントのほとんどは、RubySpidersのようにNetcatsから獲物を盗むことから得られると思います。

CivilizedBeastsは、残念ながらEcoCamelの中央のラクダのために負けています。

そして、EcoCamel(空腹クリティカル500)は非常に効率的で、最後まで生き残るのに十分なだけ食べます。

また、この更新されたClairvoyantでは、ゲームはかろうじて10,000ターンに達します。

コード:

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

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

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

    private XY abattoirCorner;
    private double abattoirRadius = 100;

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

    public class AnimalComparator implements Comparator<Animal>{

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    private XY limitSpeed(XY move) {
        if (move.x*move.x+move.y*move.y > MAX_SPEED*MAX_SPEED)
            move.unitize().multiply(MAX_SPEED);
        return move;
    }
}

1
私のゲームでは、ネットキャットよりも実際にあなたのほうが優れています。しかし、私の獣はあなたの統計で本当に悪い仕事をしているので、私は他の捕食者を実行できないことを嫌います(evilcamelはあまりにも良いことをしていますが)。たぶん、perlコンパイラなどをインストールする必要があります。
ヘルヤン14

はい、あなたの答えで説明したように、真ん中に捕食者がいる場合、あなたの方法は機能しないと思います。私はあなたと同じように振る舞う別のバージョンを実装しようとしました。それは利用可能な捕食者の数に応じてフォーメーションを変えることができるので、見るのはとても楽しいですが、あなたのものよりはそれほど良くありません。
ちょうど半分14

はい、私の獣は4匹未満の捕食者で運命づけられているため、私の戦略は他のフォーメーションと同様に多くの方法でアップグレードできます。または、ランダムに収集する場所(中央だけでなく)。しかし、私はそれを実装するのが面倒です(今)。獲物が少なくなると私の戦術がうまくいかないので、これほど良いものになることはありません。それはあなたがあなたのような獣を必要とするときです(あなたはすでに私の戦術から始めるために言及し、獲物がこの戦術を使用するために低くなるとき)。だから、あなたはすでにこれを熟考していると思います。
ヘルヤン14

私は今、別の課題に取り組んでいますが、GeoBitsはこの1つに興味を失っているようですので、結果が更新されない限り、しばらく放置します。他にも2、3の提案がありますので、この挑戦​​が生き続けることを願っています。もちろん、あなたのアップデートを見ていきます。

15

ネットキャッツ

皆さんが始められるようにするためのパックがあります。GenericPack制御プログラムに含まれるクラスを拡張します。元の投稿以来改善されており、まばらな群れに飢えていることはありません。

ネットキャットは、V字型のネットフォーメーションを使用して、獲物を隅に閉じ込め、余暇に食べることができます。ネットは、中央に1つの「ヘッド」メンバーで形成されます。頭が食べると、通常は頭が食べる機会を最初に得るため、空腹のパックメンバーと場所を入れ替えます。

ネットはかなり小さく始まりますが、群れが小さくなると広がり、畑をより効率的にトロールします。

獲物が見えない場合、フォーメーションは島のほとんどをカバーする素朴な探索パターンに広がります。

パックが2人のメンバーになると、ネットは機能しなくなります。その時点で、それぞれが独自の方法で進み、見つけられる最も近いものを貪欲に食べ、そうでない場合は半ランダムウォークを行います。

このバージョンは、質問でリンクされているビデオで見られる素朴なNetcatよりもはるかに優れています。

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

public class Netcats extends GenericPack {

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

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

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

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

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

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

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

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

        Collection<Animal> yummy = getFoods(head);

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

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

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

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

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

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

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

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

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

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

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

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

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

        if (target == null)
            return false;

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

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

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

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

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

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

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

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

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

11

ルビースパイダー

時には以下であり、より多くのソリューションは、おそらくとにかく獲物をコーナーにしようと...

私は自分のパックがただ分割され、他の人が仕事をするのを待つことができると思った。

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

警告:実際には実行されたままではなく、入力された入力を読み取ったり、すばやく応答したりすることもありません。それでも、コントローラーでうまく機能するので、さらに調整することなく適格であることを望みます。


4
+1最初の寄生虫ソリューション。私は答えのこのタイプは...徐々に抜け穴をなくすことにより、他の回答の質を押し上げるだろうと思います
センモウヒラムシ

@githubphagocyte私はよりスマートな寄生虫を念頭に置いていましたが、これはライブタイム/コード行の点でより効率的です。私はそれを実装する時間を見つけることを望みます。
レガ14

たぶん、@ Syntheticaが私のアイデアをコーディングしています。または、彼のアイデアがさらに別のアイデアである場合は、すぐにハンターよりも寄生虫が増える可能性があります;)
レガ14

1
@githubphagocyteでは3つのエントリを作成できます。準備ができたら別のパックを投稿します。それでも、このコードがその間にコード化されており、より効果的である可能性があることは興味深いと思います。Netcatを非常にうまく利用しており、実際に最初のハンターパックよりも長持ちします。
レガ

3
理由を理解するのに少し時間がかかったとしても、これはそのまま入力できます。追加するNetcatの数が多いほどうまくいくようです(これは理にかなっています)。私からの+1、どのようなハンターがコーナーを避けるために出てくるのか見てみましょう:)
Geobits 14

11

CivilizedBeasts

最後に、獣を自慢して見ましょう!

私の品種は、狩猟はやや原始的であると考えているため、4人のチームで協力し、5番目の味方を放棄します。彼らが基本的に行うことは、人間が行うことであり、彼らは獲物を捕まえ、彼らの牛の世話をします;)

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

ゲーム中に敵のNetcatのみがいる場合、+-12.000ターンで200未満の獲物で胸が生き残るのはかなり難しくなります。この種は他のどの種もできないほどの速さで大量の獲物を本当に食い尽くすので、この種に満足します(速くて大きな屠殺者が勝利を与えるわけではありませんが、ラウンド全体にかなりかかる(長い)時間に影響します)。


3
それらの世話をする」とは、「それらを真ん中に群がり、それらを屠殺/食べる」ことを意味する場合、はい、彼らはそれをうまく行います。+1
ジオビット14

面白いのは、変異していない(オリジナル)バージョンのEvil Camelsで、文明化された戦術は「センターラクダ」のために完全に非効率的です。
user2846289 14

1
@VadimR Crap、あなたのラクダを更新してくれてありがとう:PIはJavaではないのでテストできませんが、私の戦略は私の領土の真ん中にいる捕食者
にとって

5
またヘルジャンです!また、「が200未満の獲物で生き残ることは非常に困難になります」(強調を追加)。私は....あなたの胸の活力は、コンピュータシミュレーションで獲物の数に依存して実現しなかった
ジャスティン・

5

ルビーハゲタカ

ここに、より活発な寄生虫のパックが来ます。彼らは彼の獲物盗むことができるように、最も近い移動する捕食者取り囲んでいます。彼らは誰に従うかを選ぶ賢い方法を持っていないので少し運に依存していますが、彼らは通常追跡者と時にはクモを破っています。

私はテンポをプッシュするためにこれを投稿したように、彼らは完全に終わっていません:)

私は望んでいます:

  • 視野の外で捕食者を検索させる
  • 獲物を考慮してください -多くの場合、それらの1つは別のパックと獲物の間にあります!
  • 他のすべてが十分に給餌されているときに1つの飢えを避けるためにそれらを回転さ始めます

2014年4月22日:退屈を追加しました。これにより、ベタつきが少なくなり、自分で獲物探して捕食者探すことができます。

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

あなたは間違いなくミックスでより多くの「ハンター」を必要とします。現状では、これらは他の寄生虫に付着する傾向があります(それが野外での大半であるため)。しかし、私はそれらを見るのが好きで、競合他社のさまざまな組み合わせでそれらがどのように効果的であるかを見ることができます。
ジオビット14

そうそう、私のテスト環境には、他に2つのハンターパックがあります。ハゲワシがいなければ、ハゲタカはおそらくまったく無知です。特に、ネットキャットは真ん中から見られることなくコーナーをすばやく動作させることができます。
レガ14

私は、特に何が彼らを悩ませているのか知っています。邪悪なラクダの戦争ダンス。@Geobits Youtubeでの戦いはどうですか?10ラウンドは、見張りを維持するのに多すぎません。もちろん、本社が必要になります。観客の何百万人を期待したが、少し:)あなたのパックが実行方法を見ると、おそらく彼らのために応援して楽しまれることになるではない
Legat

1
完全なトーナメントは注意を引くために少し長いかもしれませんが(1ラウンドあたり約8分)、1つの「観客」ラウンドを記録することもできます。将来の実行のために考えてみます。
ジオビット14

@Geobits 8分間のラウンド中に速度は大きく変化しますか?計算量の多い部分で速度を落とすのではなく、一定の速度で再生できるように、1ターンごとにフレームを記録する価値があるかどうか疑問に思っています。YouTubeの目的のために、私は意味します。
センモウヒラムシ

5

邪悪なエコラクダ

編集:突然変異#2。ああ、いや、私は獲物の動きの予測の実装に遅れて、Netcatsを打ち負かす最初のものになりました。OK

この突然変異には$hunger_critical変数(定数)があります。1000を超える値に変更すると、Clairvoyantsのように、ラクダは常に狩りをします。次に:

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

場合は$hunger_critical500(下記のように)例えばに設定されている、(の恐怖見た後、その後の私のラクダの文明は)のみ空腹時に(それゆえ、彼らは彼らの品種の名前を変更している)、すなわち、それらは殺す環境に優しい方法で動作するようにしてみてください。お腹が空いていない場合は、他のハンターによる無意味な屠殺を防ぐために、重要な島のエリア-センターとコーナーをパトロールします。まあ、センターで、それは多かれ少なかれ動作します。角を旋回するという考えは、獲物を追い払って、猫と寄生虫の生活をより困難にすることでした。まあ、それは動作しません。とにかく愚かな獲物は隅に行きます。

また、興味深いのは、flock[ALIGN]捕食者のみがコンポーネントを推測できることであり、私の実装は単なる半分とは異なります。Geobitsのコードのぼったくりの実装には、CamelsとClairvoyantsの個々の狩猟を監視/比較する小さなバグがあるのではないかと心配しています

そして、プログラムは今やちょっと長いです、ごめんなさい。


編集:突然変異#1。島は非常に放射性であることがわかります(それは植生の欠如と「獲物」クリーチャーの説明できない性質を説明します)、ここに私のラクダの最初の突然変異があります。空腹の場合、または誰もが無料のコーナーがない場合、それらのいずれかがソロハンターになることができます。ハンターは、近くの獲物を積極的に追跡しようとします。存在しない場合は、島の中心の周りに広い円で巡回し、見つかったときに最も近いクリーチャーを追跡します。残念ながら、獲物の方向は群れに近づくと予測不可能になります(調査する価値があります...)。そのため、単独での追跡はあまり効率的ではありません。しかし、成功すると、ラクダは最寄りの無料コーナー(ある場合)にダイジェストします。飢levelのレベルが特定のレベルを下回ると、キャメルはその角を放棄します(おそらく、Netcatを呪います(「食べ物はどこですか?」))そして、単独で自由にローミングします。等々。


同じ冗談は二度言ったがおかしいとは言えないが、(1)どこかから始めなければならなかったし、これらのことに慣れていない、(2)正直、コーナー戦術について考えた(そして誰がそうしなかったのか)、Netcatを見て、Rubyの前に島にクモが現れました。

だから、肉食動物のラクダについて聞いたことがありますか?貧しい動物たちは、この神に見捨てられた島である日目を覚まし、草も木もまったく見つけませんでしたが、食用の速い動いている(かなり厄介な)小さなものはたくさんありますが、奇妙な緑をたくさん見つけました。狩猟の習慣がありませんが(すぐに変異することを願っています)、私のラクダは生き残るために非常に邪悪なスキームを開発しました:彼らは4つのコーナーのうちの1つに分かれて行き、5つ目は中央に行きます(最初にそこに死ぬため、判明)。彼らの目的地で彼らは忍耐強く待って、ラクダの一種のウォーダンスを行います。あるいは、彼らはすでにそこにいる他の動物、クモなどを踏まないようにしています...

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

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

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

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

sub distance {
    return sqrt(&sq_dist)
}

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

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

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

while (1) {

    # Get input

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

    # Update my pack

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

    # Bury dead animals, retrieve their roles

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

    # See that everyone has a role and lives accordingly

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

    # And let the cruel world know about it

    print @out;
    print "\0"
}

__END__

5
これは、これまでこのサイトで見た中で最も読みやすいperlスクリプトでなければなりません。
ジオビット14

あなたは効果的にそれらを撃つために正確にコーナーに行く必要があります、さもなければ、あなたは実際にNetcatsの屠殺に参加するでしょう、ハハ
ちょうど半分14

@justhalf、それは私が言ったようです:計画はうまくいきませんでした。角に座っている寄生虫も獲物を追い払うことはありませんでした。Hm-m、おそらく2つ以上の獣が1つのコーナーをパトロールするのに役立ちます。
user2846289 14

あなたのラクダは実際にはかなり良いです!ありがたいことに(私にとって)、千里眼を向上させたので、ほとんどの場合(常にではないが)、最後の戦いで私のパックがあなたのものに勝ちます。面白い!
ちょうど半分

1
獲物から8(20-2 * 6)ユニットより近い場合、現在のターンで獲物から30ユニット以内にある他のすべての獲物の動きを見ることができます。そして、vecプロパティは基本的に、前のターンから現在のターンまでの単なる変位です。そして、私が言ったように、私たちは前のターンからマッチングを行い、どの獲物がどの方向に進むかを見つけます。獲物の順序に頼ることはできません。これは、獲物が通常(典型的なシナリオでは)互いに十分な距離(> 12ユニット)を保つために可能であり、ほとんどの場合、前のターンの獲物を現在のターンに一致させることができます。
ちょうど半分14

4

AbleDogs-PHP

これらの素敵な犬は、獲物のふくらはぎを噛んで壁に沿って進む方法を学びました。彼らはまた、新しい餌を求めて牧草地を歩き回るのが大好きです。最後に、彼らは本当にカロリーが必要でない限り、食べることを控えるように教えられてきました。

コードをAbleDogsファイルに入れて実行しますphp 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();
}
?>

一般的な考慮事項

  • 重要なのはエンドゲームです。敵よりも早く最後の数匹の獲物を見つけて捕まえなければ、あなたは負けとなります。

  • 捕食者が単独で(または少なくともペアで)獲物を捕まえられない場合、獲物の密度が十分に低くなり、盲目的な運に頼るか、コーナーへの獲物をブロックすることで、すぐに乾杯します。

  • 獲物の動きの予測は基本的に必須です。独自の予測子を持たずに予測子ベースのプログラムに勝つことは想像できません。

テールチェイス

獲物を捕まえる最も非効率的な方法は、追いかけることです。単一の捕食者が単一の獲物を追いかけ、外部の影響(壁、他の獲物など)がないと仮定すると、尾の追跡は永遠に続く可能性があります。獲物の視界半径が30単位になると、獲物は6.1の速度6で逃げるので、1ターンあたり0.1の距離が得られます。直線では、それを得るには約300ターンが必要です。

アリーナのサイズを考慮に入れると、獲物は最大で500単位の正方形の対角線を移動してから、壁または角にぶつかります。

勝利戦略は明らかに、獲物を遅くする方法を見つけることです。つまり、他の捕食者またはその前に壁/角を置くことです。

予測子

獲物の速度が6の場合、獲物は36 * pi単位の面積に移動できます。キャッチ半径が1の場合、獲物が次にどこにいるかについての盲目的な推測では、成功する確率は1/36 * pi(約1%)です。明らかにそれを強化するために何かをしなければなりません!

シミュレーションエンジンのコードを見ると、入力が次のようになっていることがわかります。

  • 目に見える獲物と捕食者の位置
  • 以前の獲物の速度

すべてのポジションが利用可能ですが、以前の獲物の速度は利用できません。これらの速度を計算する唯一の方法は、1ターンから次のターンまですべての獲物を追跡することです。これはほとんど不可能です(非常にスマートなモーショントラッキングアルゴリズムを実装しない限り)。そのため、予測子は、推測する必要がある速度の寄与を除いて、計算のすべての項を簡単に再現できます。

単一の獲物の場合、速度はあまり問題なく追跡できます。これにより、群れから隔離された獲物を捕まえる「完璧な」予測子を構築できます。獲物が少なすぎて相互にやり取りできない場合、これが基本的にエンドゲームに必要なすべてです。獲物が豊富で、群れ効果が予測因子をだますのに十分強い場合、獲物の密度の高さがエラーを補います)。

餌食

獲物の速度計算の正確な知識があれば、捕食者の位置を調整することにより、与えられた獲物を望ましい方向に向けることができます。

これにより、獲物を壁に固定したり、別のパックメンバーに向けたりすることができます。2つのパックメンバー間で獲物をつまむなど、いくつかの洗練された戦略を試しました。残念ながら、これは現在の「ピンとスキャン」ルーチンよりも効率が低いことが判明しました。

獲物を盗む

捕食者の行動の特徴の1つは、捕食者からの距離に比例して捕食者の影響が増加することです(被捕食者の視界内に留まる場合)。捕食者が獲物に最も近づくと、獲物はそれから逃げることができなくなります。

これは、2人の捕食者が獲物を捕まえるために競う場合、最も近いものが最初に獲物を獲ることになります。チェイサー/獲物の軸の直前に自分自身を配置することができる超スマートな競争者でさえ、基本的には獲物を競争者の顎に怖がらせるでしょう。

獲物を盗むためには、少なくとも一対の捕食者が必要です。1つは殺害に行き、もう1つは獲物の視界の範囲内にとどまり、可能な限り獲物から離れて影響を最大化し、獲物をハンターに向けます。

それに加えて、方向を変えるたびに、競技者は獲物に向かって角を切ることができます。そして、競技者の後ろにいることは、アクションの開始時に「ゴーダー」が獲物に十分近かった場合にのみ可能です。

そのため、「スティーラー」の初期位置が有利であり、少なくとも2番目の捕食者をspareしまない場合、獲物を盗むことは成功するチャンスしかありません。私の経験では、これは複雑な価値はありません。

推奨される変更

より複雑な戦略を可能にするために、捕食者を獲物の最高速度より上に移動させると、速度超過に比例して空腹ポイントにコストがかかる可能性があります。たとえば、速度6までの移動は無料で、6を超える速度ポイントはすべて100ハンガーポイントかかります(6.3になると1ターンにつき30ハンガーポイント、1000ハンガーポイントを燃やすと1ターンで速度16に到達し、そうしないと死亡します)獲物を捕まえないでください!)。

獲物を食べるのに十分な距離にいる複数の捕食者に殺害を与える代わりに、利得を分割することをお勧めします(たとえば、3つの捕食者はそれぞれ333.33の空腹ポイントを獲得します)。これにより、より内密なエンドゲーム戦略が可能になります(たとえば、より多くの空腹ポイントがあると考えると、敵の捕食者をシャドウイングすることが有用になります)。

最初のパックの特別な色は見にくいです。青の代わりにシアンまたはオレンジをお勧めします。


最後に別の競合他社!獲物を盗むことを除いて、あなたが言及したすべてのポイントを意図的に実装しました。現在の副作用に満足しています。あなたのコメントから、あなたは私の千里眼に勝っていると思われますか?面白いです、明日チェックします。= D。また、私のGUIアップデートを試して、(少なくとも私によれば)より良いグラフィックを見ることができます。
ちょうど半分14

私はいつも勝っていません。産卵時に最後の獲物に最も近いのは誰かによって異なります。しかし、私の有能な犬には統計的な優位性があるかもしれません。また、各チームを異なる色で表示するようにシミュレーションエンジンを調整しましたが、最終的には自分の好みにはカラフルすぎることがわかったため、1番目のチームの青と他のすべての赤の代わりにオレンジに決めました。

うわー、私はちょうどあなたの提出を実行しました。それはクレイジーです、私はあなたがそのように祈りを静止させることができるとは知りませんでした。そして、獲物が正確に端にいるとき、私の捕食者がそれらを検出することができないので、それは私にとって間違いなく大きな不利益だと少しだまされました。
ちょうど半分14

それはあなたのためのベクトル演算です:)。それが私が私の投稿で説明しようとしたものです:単一の獲物について、あなたはすべての運動パラメーター(以前の速度を含む)を知っているので、適切な獲物の速度を生み出す捕食者の位置を計算して、それを壁の近くに静止させることができます。

1
方程式の解はハードコーディングされています(反復は不要です)。基本的に、捕食者がいない場合に次のターン速度で獲物が使用するベクトルを計算します。獲物が進む方向を考えると、獲物の速度をその方向に向けるのに必要なベクトルの差を推測します。このベクトルの差により、獲物に対する捕食者の位置がわかります。これにより、(特定の制限内で)獲物からの距離を選択できる自由度が得られます。

3

レイジーパック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

これを実行するにはhaskellプラットフォームが必要です。次に、runhaskellコマンドを使用して実行します。私のパックは獲物が彼らに来るのを待っています。


新しい言語のスケルトンソリューションの場合は+1。おそらくあなたは、人々がこれに基づいて新しい戦略を立てることに満足していますか?
センモウヒラムシ

もちろん。(ただし、一定の出力を生成し、「dead \ 0」で終了する以外は何も行いません。そのため、非常に役立つかどうかは
わかり

私はまだ使用してこれを実行します誰に助言したい-silent...しかし、オプションを
Geobits

3

エントリーではなく、私は常に、参加エントリーごとにカスタマイズされた色を追加することに興味があります;)

また、食べるプロセスは色を変えることで視覚化されるのではなく、サイズを変えることで視覚化されるため、複数の食事イベントを短時間で見ることができます。

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;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.