Голодні ігри - їжте чи вмирайте


60

Голодні ігри - їжте чи вмирайте

Якщо ви не їсте, ви помрете. Якщо ви їсте, ви живете (поки не помрете). Ви будете помирати, так що постарайтеся , щоб померти в минулому.

Огляд

Тут є острів, заселений стадом здобичних тварин. Ви керуєте зграєю з п'яти хижаків. Ваша мета - зберегти ваш пакет живим. Зробіть це, поїдаючи здобич. Здобич, як правило, перебігає від хижаків і намагається в іншому випадку триматися в зграї. Звичайно, ваш пакунок буде на тому ж полі, що і кожен інший пакет , тому змагання намагатимуться їх з'їсти, перш ніж зможете. Не дозволяйте цьому вас відштовхувати, інакше ви будете голодувати.

Як грати

Створіть та подайте програму командного рядка, щоб направити ваш пакет. Він буде отримувати інформацію про стан від програми управління на STDIN та виводити команди STDOUT. Формат детально викладений нижче. Кожна програма буде виконана лише один раз, і вона повинна працювати, доки в ній не буде більше членів пакету. Вам потрібно буде прочитати дані, як вони надходять, і швидко відповісти. Існує суворий час очікування в 200 мс для кожної відповіді. Якщо ви до цього часу не відповіли, ваш пакет не отримає нових інструкцій для поточної черги.

Якщо ваша програма не може бути запущена контролером, вона не вважатиметься дійсною. Будь ласка, включіть рядок командного рядка, який мені потрібно буде використовувати для запуску вашої роботи. Якщо є якісь спеціальні інструкції (до налаштування компіляторів тощо), будь ласка, включіть їх. Якщо я не можу працювати, я попрошу вас про допомогу в коментарях. Якщо ви не відповісте, я не зможу прийняти ваше подання.

Турнір відбудеться в 64-бітній системі Linux. Майте це на увазі, даючи будь-які необхідні вказівки.

Деталі

  • Положення та напрямок кожної істоти мають форму пари з двома точними числами з плаваючою точкою (наприклад double), що представляють їх xта yкоординати відповідно.

  • Кожна істота вважається пунктом. Це означає, що вони можуть перекриватися і займати однаковий простір. Вас не відштовхнуть, і немає поняття зіткнення з іншими істотами.

  • Острів - площа, 500 одиниць в бік. Якщо ви спробуєте здійснити за межі цих меж, ви будете затиснуті на край. Походження {0,0}знаходиться вгорі зліва, зі xзбільшенням праворуч і yзбільшенням вниз. Знову-таки карта не загортається .

  • Гра починається з 1500 здобич (packCount * 50) здобич тварин. Вони зберуться в центрі острова, але швидко вирішать почати рух.

  • Пачки будуть розташовані по рівномірно розташованому колу по периметру. Порядок упаковки перетасовується, тому не розраховуйте на запуск у певному місці.

  • Хижі тварини можуть бачити всіх інших тварин у радіусі 30 одиниць. Вони можуть рухатись максимум 6,0 одиниць за оборот.

  • Хижаки можуть бачити всіх інших тварин в радіусі 50 одиниць. Вони можуть рухатись максимум 6,1 одиниці за оборот. Це означає, що вони можуть побачити здобич, перш ніж їх побачать і (ледве) випередили їх.

  • Хижаки живуть і гинуть відповідно до рівня їх голоду . Він починається з 1000 , і зменшується на кожен поворот. Якщо після пересування хижак знаходиться в межах 1 одиниці здобичі, він автоматично з'їсть його. Це вилучає здобич і встановлює голод хижака до 1000. Кожен хижак може з'їдати лише одну здобич за оборот. Якщо в межах даного діапазону їх більше, він з'їсть той, до кого потрапить перша петля (не обов'язково найближча). Хижак гине, якщо його голод досягає нуля.

  • Пакети починаються з п'яти членів у кожному. Кожні 5000 оборотів, усі пакети, що залишаються в грі, породять одного нового учасника. Він буде розміщений у видимому діапазоні члена колеги. Переконайтеся, що ваші записи можуть обробляти більше п'яти членів.

  • Кожні 1000 оборотів породжуватиметься більше здобичі. Кількість нової здобичі буде кількістю живих хижаків мінус одна.

  • Хижаки не можуть атакувати інших хижаків. Вони їдять здобич, коли їх ловлять. Це воно.

  • Замовлення протягом черги:

    • Усі здобичі приймають рішення
    • Усі хижаки приймають рішення
    • Усі здобичі рухаються
    • Всі хижаки рухаються / їдять
  • Порядок, коли кожен пакет приймає свої рішення / кроки, буде рандомізований на кожному кроці.

Протокол (загальний)

Всі комунікації проводяться у рядковому форматі US-ASCII. Числа перетворюються на рядки за допомогою Java Double.toString()або Integer.toString(). Вихід повинен бути відформатований таким чином, щоб він міг прочитати Java Double.valueOf(String)(ви не будете виводити цілі числа). Детальніше про розбірливі формати див. У документації дляDouble . Усі поля в рядку розділені стандартним \tсимволом, а нові рядки - \n. Весь рядок буде завершений буде нульовим байтом \0.

У наведених нижче прикладах я використовую <>для позначення полів для читабельності. Їх немає у власних рядках.

Протокол (введення)

Рядок введення змінюється по довжині, в залежності від того, скільки істот видно у вашій упаковці. Він може перевищувати 100 тис. Символів, тому будьте готові до цього. Основна установка:

  • Рядок 0: Основна інформація про гру. turn- поточний номер черги, і рахунки - це загальна кількість здобичі та хижаків, залишених на полі. Вони є integerв рядковій формі.

    <turn>\t<preyCount>\t<predatorCount>\n
    
  • Рядок 1: унікальні ідентифікаційні показники та голод членів вашої упаковки. Вони не задаються в одному порядку для кожного введення. Використовуйте унікальні ідентифікатори для відстеження окремих членів, а не в тому порядку, в якому вони відображаються на вході. Знову ж таки, це integerяк струни. Для пачки з двох це:

    <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 . Вони будуть надані у вигляді двох рядків на кожного члена.

Перший для кожного складається з місць для здобичі, яку він може бачити. Друге - це місця для хижаків, які він може бачити. Ці місця не є унікальними в цілому. Наприклад, якщо двоє членів зграї можуть бачити одну тварину, це буде в рядку обох членів. Також будуть включені ваші власні члени пакету . Якщо ви хочете їх виключити, ви можете порівняти місця розташування з власними членами. Усі локації є у 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}. Усі числа будуть в одному рядку, розділеному вкладкою.

Наприклад, якщо у вас були 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}перемістить вам одну одиницю. {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"

На карті здобич з’являється зеленим кольором, а хижаки - червоним. Однак, який би пакет був першим пакетом, поданим в якості аргументу, замість цього буде кольоровим кольором. Це покликане легше їх розрізнити для тестування. Хижаки також спалахнуть білим протягом п’яти кадрів, коли вони їдять.

Гра триватиме до тих пір, поки останній хижак не голодує, записуючи до консолі, як трапляються голодування чи вимирання. Після того, як гра завершена, за кожний пакет буде додано рахунок. Якщо ви не хочете бачити події голоду / вимирання, можете скористатися -silentаргументом. Тоді він виведе лише остаточний рахунок. Ви повинні передати це як перший аргумент :

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

У комплекті є пакет скелета Java з назвою GenericPack. Він включає основні операції введення / виводу, необхідні. Саме там можна навести наочний приклад того, як розбирати та відповідати. Якщо ви хочете додати шаблон іншою мовою, дайте мені знати.

Також хижак на основі шаблону, ChaserPack. Він не буде включений у турнір, а включений лише для тестування. Це дуже погано, через умисний недолік націлювання. Якщо ви не можете його перемогти, продовжуйте намагатися.

Нижче наведено приклад запуску програми управління (натисніть на відео). Якість відео не чудова (вибачте), але ви можете відчути, як рухається здобич. ( обережність: аудіо )

скріншот

Оцінка балів

Переможець визначатиметься турніром, набираючи очки у кожному раунді.

Кожен раунд триває, поки всі хижаки не загинуть. Кожна пачка буде нараховуватися залежно від того, коли останній член помер від голоду. Потім їм будуть присвоєні бали на основі порядку. Бали накопичуватимуться за десять раундів, а переможець - пакет з найвищими загальними балами.

Перше місце за кожен тур отримає 100 балів. За кожне місце після цього винагорода буде зменшена на 20% (округлена вниз). Це триватиме доти, доки бали не досягне нуля (після 17-го місця). Місця 18+ не отримають балів за раунд. Пачки, які зв'язали, отримають рівні бали. Наприклад:

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

Максимально можливий бал за час турніру - 1000, з першого місця за всі десять разів.

Якщо декілька програм завершать турнір, зв'язаний за перше місце, відбудеться ще десять туровий турнір з поданням лише заявок на перше місце . Це триватиме до тих пір, поки не з’явиться один переможець.

Я намагатимусь проводити турнір приблизно щотижня, або в міру надходження нових заявок.

Додаткові правила (грайте справедливо!)

  • Ви не можете читати чи писати будь-які зовнішні ресурси. Оскільки ви не збираєтеся більше разів визивати свою програму, будь-яка інформація про стан може зберігатися всередині країни.

  • Не втручайтеся в інші процеси / подання. Це не означає, що не намагайтеся вкрасти свою здобич, випередити їх і т. Д. Це означає, що не заважайте виконувати процес. Це на мій розсуд.

  • Учасники конкурсу обмежені максимум трьома записами. Якщо ви подасте більше, я наберу лише перші три подані. Якщо ви хочете відкликати його, видаліть його.

  • Записи можуть не існувати виключно для підкріплення інших записів. Кожен повинен грати, щоб виграти за власними заслугами.

  • Ваша програма може породити максимум один дочірній процес одночасно ( загальний нащадок, а не прямий). У будь-якому випадку, переконайтеся, що ви не переходите на час очікування. Ви не можете жодним чином викликати сам Gameклас.

Результати - 29 квітня 2014 року

Ось результати останнього турніру з десяти турів:

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

Пакети, подані до 09:00 EDT 2014/04/29, включаються до цього циклу.

Ви також можете переглянути деталі для кожного раунду . Чомусь я вирішив нумерувати раунди назад, тому він починається з "раунду 10".

Оновлення

23.04.2014: FGreg повідомив про помилку, пов’язану з таймаутами (спасибі!). Виправлено виправлення, тому тестери хочуть оновити код програми управління.


28
Мені подобаються ці питання щодо короля пагорба!
Cruncher

2
@Manu Я написав приклади ботів на Windows 7 і перевірив і win, і linux. Які у вас проблеми з ними?
Геобіц

2
Ці питання щодо короля пагорба досить приголомшливі, і це, безумовно, цікаво. У мене зараз два різні пакети!
mackthehobbit

2
@githubphagocyte Я насправді не хочу вбивати пакет із першого тайм-ауту, просто тому, що я бачив навіть прості програмні тайм-аути один раз кожні 40 тис. + повороти чи подібні. Я здійснив зміну імені в контролері. Повороти зараз відомі як повороти у всій програмі, якщо я десь не пропустив.
Геобіць

2
@Geobits е, це добре мені. Ви знаєте, це схоже на дослідницький проект, який займається один з моїх професорів фізики, з яким я можу допомагати протягом літа. Я поясню трохи про це пізніше, якщо зможу.
krs013

Відповіді:


10

Ясновидник

Код оновлений для стикання з AbleDogs

Ву-ху! Нарешті б'є, що Netcats! Я розширив існуючий код (кредити до Geobits!) З деякою невеликою модифікацією, щоб створити цей майбутній пакет прогнозування. Ніщо не б’є хижаків, які знають, куди здобич рухатиметься!

З двох тестів, які я робив, мій пакет завжди вигравав проти Netcats. Але це не буде настільки добре, якщо немає інших пачок, оскільки прогнозування все-таки провалюється, якщо поблизу є занадто багато інших здобич.

Можливо, я можу включити трюк 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) спожив 916 здобич за 9270 оборотів (0,09 здобичі / оборот)
EcoCamel.pl (3) спожив 73 здобичі за 8118 оборотів (0,009 здобичі / оборот)
Netcats (0) спожив 563 здобичі за 6111 обертів (0,022 здобичі / обороту)
RubyVultures.rb (5) спожив 77 здобич за 4249 оборотів (0,018 здобич / оборот)
RubySpiders.rb (4) спожив 293 здобичі за 3495 оборотів (0,084 здобичі / оборот)
CivilizedBeasts (2) спожив 10 здобич за 3176 оборотів (0,003 здобичі / оборот)
ChaserPack (6) спожив 43 здобичі за 2492 обороти (0,017 здобич / оборот)

Моя упаковка дуже агресивна, і більшість з 916 підрахунків я думаю, що це отримується від крадіжки здобичі від Netcats, як і RubySpiders.

CivilizedBeasts, на жаль, програє завдяки центральному верблюду від EcoCamel.

А EcoCamel (з голодом критичних 500) досить ефективний, їсть його достатньо, щоб вижити до кінця.

Також із цим оновленим ясновидцем гра ледь досягає 10000 оборотів.

Код:

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
Виглядає дуже добре, ваш справді краще, ніж сітки в моїй грі. Але я ненавиджу, що я не можу запускати інших хижаків, оскільки мої звірі роблять по-справжньому погану роботу у вашій статистиці (в той час як злий камель робить занадто добре). Можливо, мені доведеться спробувати встановити компілятор perl чи так.
Герхан

Так, я думаю, що ваш метод не працює, якщо в середині є хижак, про що йдеться у вашій відповіді. Я спробував реалізувати іншу версію, яка поводиться так само, як і ваша. Він може змінювати формування залежно від кількості наявних хижаків, тому спостерігати за ним досить весело, хоча і не набагато краще, ніж ваше.
justhalf

Так, мою стратегію можна модернізувати багатьма способами, як і інші формування з іншою кількістю членів, тому що мої звірі приречені на <4 хижаки. Або випадкові місця для збору (замість лише середини), наприклад. Але я занадто ледачий, щоб реалізувати це (зараз). І це ніколи не буде так добре, як це, оскільки якщо здобич знизиться, моя тактика просто не працює. Ось тоді вам потрібен такий звір, як ваш (ви вже згадували, щоб почати з моєї тактики і коли здобич стає низькою, щоб використовувати цю тактику). Тож я гадаю, ви це вже продумали.
Герхан

Зараз я переживаю ще одне завдання, і GeoBits, схоже, втратив інтерес до цього, тому я дам йому просидіти деякий час, якщо результати не оновлюються. У мене є ідеї для кількох інших публікацій, тож сподіваюся, що цей виклик буде продовжений. Я, звичайно, перегляну ваше оновлення.

15

Мережі

Ось пакет, з якого ви почали працювати. Він розширює GenericPackклас, що входить до програми управління. Він покращився з моменту оригінальної публікації, і більше не голодує рідким стадом.

Сітчасті сітки використовують сітчасту форму у формі віє, щоб виловлювати здобич у кутку, де вони можуть їсти їх у дозвілля. Сітка формується з одним «головним» членом у центрі. Як тільки голова їсть, вона обмінюється місцями з найголоднішим членом пакета, оскільки голова, як правило, першою отримує можливість поїсти.

Сітка починається досить маленькою, але розширюється, коли стадо стає меншим, щоб ефективніше перетягувати поле.

Якщо здобич не видно, пластівництво розширюється до наївного пошуку, що охоплює більшу частину острова.

Як тільки пакет опускається до двох членів, мережа просто не працює. У цей момент кожен йде своїм шляхом, жадібно поїдаючи найближчу річ, яку може знайти, і в іншому випадку проходить напіввипадкову прогулянку.

Ця версія виживає набагато краще, ніж наївні Netcats, помічені у відео, пов'язаному з питанням.

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

Caveat: Він насправді не працює, ні він не читає вхід, коли він надходить, ні швидко реагує. Тим не менш, як це добре працює з контролером, я сподіваюся, що він може отримати без додаткових коригувань.


4
+1 перший розчин паразита. Я думаю, що такий тип відповідей підніме якість інших відповідей, поступово усуваючи лазівки ...
trichoplax

@githubphagocyte Я мав на увазі розумнішого паразита, але це більш ефективно з точки зору живого часу / рядків коду. Я сподіваюся, що знайду час для її втілення.
Легат

Можливо, @Synthetica кодує мою ідею прямо зараз. Або якщо його ідея ще одна, ми можемо скоро мати більше паразитів, ніж мисливців;)
Legat

1
@githubphagocyte нам дозволяється зробити три записи, тому я опублікую ще одну упаковку, коли вона буде готова. І все-таки мені цікаво, що цей час був закодований, і він може виявитись більш ефективним. Він користується Netcats дуже добре, і це фактично переживає мою першу зграю мисливців.
Легат

3
Це може ввести як є, навіть якщо мені знадобилося секунду, щоб зрозуміти, чому. Здається, що краще робити більше Netcats, які ви додаєте (що має сенс). +1 від мене, давайте подивимося, які мисливці виходять, щоб уникнути куточків :)
Geobits

11

Цивілізовані береги

Нарешті, час показати своїх звірів!

Моя порода вважає полювання дещо примітивним, тому вони працюють разом у команді 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();
    }
}

Моїм грудям стає досить важко вижити з меншою здобиччю 200 на черзі + -12.000, лише ворожі Netcats в грі. Ви будете щасливі з цією породою, оскільки вона дійсно пожирає величезну кількість здобичі зі швидкістю, як жодна інша порода ніколи не може (не те, що швидкі та великі забійники приносять перемогу, але це впливає на (тривалий час), коли цілий раунд забирає значно).


3
Якщо під " піклуватися про них " ви маєте на увазі " багаторазово ставити їх до середини і забивати / їсти їх ", то так, вони роблять це добре. +1
Геобіць

Це смішно, з не мутованою (оригінальною) версією Злих верблюдів цивілізована тактика абсолютно неефективна через «центрального верблюда».
користувач2846289

1
@VadimR Crack, дякую за оновлення верблюда: PI не можу перевірити його, оскільки це не Java, але я знаю, що моя стратегія є марною з хижаками посеред моєї території: P
Herjan

5
Знову Герхан! Також "Моїм грудям стає досить важко вижити з меншою здобиччю 200" (наголос додано). Я не усвідомлював, що життєздатність ваших грудей залежить від кількості здобичі в комп’ютерному моделюванні ....
Джастін,

5

Рубінні грифи

Тут виходить зграя більш активних паразитів . Вони намагаються оточити найближчого хижого хижака , щоб вони могли вкрасти його здобич . Вони трохи залежать від удачі, оскільки у них немає розумного способу вибору, кого слідкувати, але вони зазвичай б'ють переслідувачів, а іноді і павуків .

Вони не зовсім закінчені, оскільки я розмістив це, щоб натиснути темп :)

Я сподіваюся:

  • змусити їх шукати хижаків поза полем зору
  • враховуйте здобич - часто одна з них знаходиться між іншою пачкою і здобиччю!
  • починайте обертати їх, щоб уникнути одного голоду, коли всі інші добре годуються

22 квітня 2014 р .: Додана нудьга , яка робить їх менш менш клейкими та дозволяє їм полювати на здобич самостійно та шукати хижаків

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

Вам точно потрібно більше "мисливців" у суміші. Як це є, вони, як правило, приєднуються до інших паразитів (оскільки це більшість у цій галузі). Мені подобається спостерігати за ними, і я бачу, наскільки вони будуть ефективними при різних поєднаннях конкурентів.
Геобіц

О так, у моєму тестувальному середовищі я маю ще два зграї мисливця. Без них грифи, мабуть, досить незрозумілі. Тим більше, що сітки можуть швидко працювати по кутах, не видно з середини.
Легат

Я думаю, я знаю, що може їх особливо турбувати. Військовий танець злих верблюдів. @Geobits як щодо розміщення боїв на Youtube? 10 раундів - це не надто багато, щоб залишатися спостережливими. Звичайно, знадобиться штаб-квартира. Не сподіваючись на мільйони глядачів, але було б цікаво подивитися, як виконуються ваші пачки та, можливо, трохи повеселити їх :)
Legat

1
Повний турнір може бути трохи довгим (~ 8 хвилин за раунд зараз), щоб затримати увагу, але записати один "глядацький" раунд може спрацювати. Я дам йому думку на майбутні пробіжки.
Геобіц

@Geobits Чи сильно змінюється швидкість протягом 8-хвилинного раунду? Мені цікаво, чи варто записувати кадр за поворот, щоб вони могли відтворюватися з постійною швидкістю, а не сповільнюватися під час обчислювально інтенсивних частин. Я маю на увазі для цілей YouTube.
трихоплакс

5

Злі еко верблюди

Редагувати: Мутація №2. О, ні, я запізнююсь зі своїм реалізацією передбачення руху здобичі, щоб першим перемогти Netcats. Гаразд, так і буде.

Ця мутація має $hunger_criticalзмінну (постійну). Зміна його на значення вище 1000 змушує верблюдів завжди полювати, як і ясновидців. Тоді:

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

Якщо $hunger_criticalвстановлено, наприклад, 500 (як нижче), то мої верблюди (побачивши жах цивілізації ) намагаються вести себе екологічно (отже, вони змінили назву своєї породи), тобто вони вбивають лише тоді, коли голодні. Якщо вони не голодні, вони патрулюють критичні острівні райони - центр та куточки, щоб запобігти безглуздому вбивству з боку інших мисливців. Ну, з центром він більш-менш працює. Ідея кружляти по кутках полягала в тому, щоб вигнати здобич і ускладнити життя Котам і паразитам. Ну, це не працює. Дурна здобич все одно йде в куточки.

Цікаво також те, що flock[ALIGN]компонент може здогадуватися лише хижаками, а моя реалізація відрізняється від простоголовника. Я боюся , що якась - то незначна помилка в моєму плагіаті реалізації коду Geobits ', спостерігаючи / порівнюючи окремі полювання на Верблюди проти ясновидця.

А програма така вже давно, вибачте.


Редагувати: Мутація №1. Острів виявляється досить радіоактивним (це пояснює відсутність рослинності та незрозумілу природу «здобичних» істот), тому ось перша мутація моїх верблюдів. Будь-який з них може стати сольним мисливцем, якщо він голодний або якщо немає вільного куточка для всіх. Мисливець намагається активно переслідувати неподалік здобич. Якщо його немає, він патрулює широким колом навколо центру острова, тоді переслідує найближчу істоту, коли знаходить її. На жаль, напрям здобичі стає непередбачуваним, коли він знаходиться поруч із роєм (варто дослідити ...), тому сольний переслідування не дуже ефективно. Але якщо це вдасться, Верблюд відправляється переваритись до найближчого вільного кута (якщо такий є). Коли рівень голоду нижче певного рівня, будь-який верблюд покидає свій кут (напевно, проклинаючи Netcats ("де їжа?" )) і самостійно переходить до безкоштовного роумінгу. І так далі.


Один і той же жарт, розказаний двічі, не смішно, але (1) мені довелося десь починати, і я новачок у цих речах, (2) Чесно, я думав про кутову тактику (а хто ні?), Спостерігаючи за Netcats перед Рубі На острові з’явились павуки.

Отже, коли-небудь чули про м’ясоїдних верблюдів? Бідні тварини прокинулися одного разу на цьому покинутому богом острові, щоб не знайти ні трави, ні дерев взагалі, але безліч дивних зелених, хоча їстівних швидко рухаються (досить дратівливих) дрібниць. Не маючи звичок полювання (але, сподіваюся, незабаром вони будуть мутувати), мої верблюди розробили дуже злу схему виживання: вони розбиваються і йдуть кожен на 1 з 4 кутів, а 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, який я бачив на цьому сайті.
Геобіц,

Вам потрібно під'їхати саме до кута, щоб ефективно їх застрелити, інакше ви насправді будете брати участь у розпусці Netcats, ха-ха
justhalf

@justhalf, Як я вже сказав: план не спрацював. Паразити, що сидять у кутках, теж не відганяли здобич. Хм-м, може допоможуть 2 і більше звірів, які патрулюють один кут.
користувач2846289

Ваші верблюди дуже хороші насправді! На щастя (для мене) я покращив своїх ясновидців, тому більшу частину часу (не завжди) мій пакет вигравав проти ваших під час останнього бою. Цікаво!
justhalf

1
Якщо ви знаходитесь ближче, ніж 8 (20-2 * 6) одиниць від здобичі, ми можемо побачити будь-який рух усіх інших здобич, що знаходяться в межах 30 одиниць нашої здобичі. А vecвластивість - це лише переміщення від попереднього повороту до поточного повороту. І, як я вже сказав, ми робимо відповідність з попередньої черги, щоб з’ясувати, яка здобич іде в інший бік, ми не можемо розраховувати на порядок здобичі. Це можливо, тому що здобичі зазвичай (за типовим сценарієм) тримають достатньо відстані один від одного (> 12 одиниць), і тому більшу частину часу ми можемо співставити здобичі в попередньому руслі до поточного обороту.
justhalf

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 за ваші 6,1, тож ви отримуєте .1 відстань за оборот: по прямій лінії вам знадобиться близько 300 оборотів, щоб отримати її.

Враховуючи розмір арени, здобич буде подорожувати максимум по діагоналі площею 500 одиниць, перш ніж вдарити в стіну чи кут, що займе не більше 117 оборотів.

Стратегія виграшу очевидно - знайти спосіб уповільнити здобич, а саме мати іншого хижака або стіну / кут перед собою.

Прогноз

Зі швидкістю здобичі 6 здобич може переміститися до площі 36 * пі одиниць у квадраті. З радіусом лову 1, сліпи здогадки про те, де здобич буде наступною, мають шанс 1/36 * пі (близько 1%) досягти успіху. Очевидно, що потрібно зробити, щоб покращити це!

Переглядаючи код двигуна імітації, ви можете бачити, що входи:

  • видимі позиції здобичі та хижака
  • попередні швидкості здобичі

Хоча всі позиції доступні, попередні швидкості здобичі не є. Єдиним способом обчислити ці швидкості було б відслідковувати кожну здобич від одного повороту до наступного, що майже неможливо зробити (якщо ви не реалізуєте дуже розумний алгоритм відстеження руху). Таким чином, передбачувач може легко відтворити всі умови обчислення, за винятком внеску швидкості, про який потрібно здогадатися.

У випадку з однією здобиччю швидкість можна відслідковувати без особливих проблем, що дозволяє побудувати «ідеального» прогноктора для вилову здобичі, ізольованої від зграї. Що в основному все, що вам потрібно для закінчення гри, коли здобичі занадто мало, щоб взаємодіяти між собою. Коли здобич вдосталь і ефект стада досить сильний, щоб обдурити передбачувача, сама щільність здобичі компенсує помилки (якщо ви не зловите ту, на яку ви прагнули, швидше за все, ви отримаєте одного з найближчих приятелів ).

Головні здобичі

Точно знаючи обчислення швидкості здобичі, можна "спрямовувати" дану здобич у потрібний напрямок, регулюючи положення хижака.

Це дозволяє прибити здобич до стіни або направити її до іншого члена пакета. Я спробував кілька вдосконалених стратегій, таких як прищеплення здобичі між двома членами пачки. На жаль, це виявилося менш ефективним, ніж нинішній режим "шпигування та сканування", оскільки тримання двох хижаків, зайнятих переслідуванням однієї здобичі, залишає опозицію занадто багато хижаків для вільного розвідки пасовища.

Крадуть здобич

Однією з характеристик поведінки здобичі є те, що вплив хижака збільшується пропорційно його відстані від здобичі (за умови, що він залишається в радіусі зору здобичі). Найближчий хижак приходить до здобичі, найменше здобич відхилятиметься від нього.

Це означає, що коли два хижаки змагаються, щоб зловити здобич, найближчий зобов’язаний отримати її першим. Навіть супер розумний суперник, який зумів би розташуватися прямо перед віською гонитви / здобичі, в основному відлякує здобич у щелепах суперника.

Щоб встигнути викрасти здобич, потрібна хоча б пара хижаків. Один піде на вбивство, а інший залишиться просто в радіусі зору здобичі, наскільки це можливо здобич, щоб досягти максимального впливу та спрямувати здобич у бік мисливця.

Крім того, будь-яка зміна напрямку дозволить змаганням зрізати кут до здобичі, а тримати позаду суперника можливо лише в тому випадку, якщо «годер» був наближеним до здобичі на початку дії.

Тож викрадення здобичі має лише шанс на успіх, якщо початкові позиції "крадіжок" є сприятливими і ви можете пощадити хоча б другого хижака. На мій досвід, цього не варто складності.

Запропоновані зміни

Щоб дозволити більш складні стратегії, переміщення хижака над найвищою швидкістю здобичі може мати витрати в голодних точках, пропорційні перевищенню швидкості. Скажімо, наприклад, що швидкість руху до швидкості 6 є безкоштовною, і кожна швидкість вище 6 коштує 100 голодних пунктів (перехід до 6,3 коштує 30 голодних балів за оборот, спалювання 1000 голодних точок дозволить досягти швидкості 16 за один поворот - і помре, якщо ти не будеш ловити здобич, роблячи це!).

Замість того, щоб вбивати випадкового хижака, коли більше одного достатньо близько, щоб з'їсти здобич, я пропоную розділити виграш (наприклад, 3 хижаки отримують 333,33 голодні очки кожен). Це дозволило б отримати більше цікавих стратегій ендштею (затінення ворожих хижаків стане корисним, якщо, на думку, у вас є більше голоду, наприклад).

Особливий колір для першої упаковки досить важко помітити. Я пропоную блакитний або помаранчевий замість синього.


Нарешті ще один конкурент! Я навмисно реалізував кожен згаданий вами пункт, за винятком крадіжки здобичі, щодо якої я був задоволений поточним побічним ефектом. З ваших коментарів виходить, що ви виграєте проти мого ясновидця? Це цікаво, я перевірю завтра. = D. Також ви можете спробувати моє оновлення графічного інтерфейсу, щоб побачити кращу (принаймні на мою думку) графіку.
justhalf

Я не виграю весь час. Це залежить від того, хто найближчий до останньої здобичі, коли вони нерестуються. Хоча мої собаки можуть мати статистичні переваги. Я також налаштував двигун симуляції, щоб відобразити кожну команду в іншому кольорі, але врешті-решт він виявився занадто барвистим на мій смак, тому я влаштувався на оранжевий замість синього для 1-ї команди, та на всі інші червоні.

Нічого собі, я просто запустив ваше подання. Це божевільно, я не знав, що ти можеш зробити так, щоб молитва стояла так. І я відчув трохи обману, що коли здобич знаходиться точно на краю, мої хижаки не зможуть їх виявити, це, безумовно, великий недолік для мене.
justhalf

Це для вас векторна арифметика :). Це я намагався пояснити у своєму дописі: для однієї здобичі ви знаєте всі параметри руху (включена попередня швидкість), тому ви можете обчислити позицію хижака, яка виробить відповідну швидкість здобичі, щоб вона стояла нерухомо біля стіни.

1
Добре рішення рівняння є жорстким кодом (ніяких ітерацій не потрібно). В основному ви обчислюєте вектор, який здобич повинен використовувати для наступної швидкості повороту, якщо вашого хижака там не було. З огляду на напрямок, яким ви хочете здобич рухатися, ви робите різницю векторів, необхідну для того, щоб зробити точку швидкості здобичі в цьому напрямку. Ця векторна різниця дає вам положення хижака відносно здобичі. Це залишає вам ступінь свободи, яка дозволяє (в певних межах) вибрати відстань від здобичі.

3

Ледачий пакет Хаскелл

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 для скелетного рішення новою мовою. Імовірно, ви щасливі, що люди будують нові стратегії на цьому?
трихоплакс

Звісно, ​​чому б ні. (Хоча, він нічого не робить, окрім того, що створює постійний вихід і виходить із "мертвих \ 0", тому я не впевнений, чи було б це дуже корисно.)
PyRulez

Я б все-таки радив усім, хто це запускає, скористатися цим -silentваріантом, хоча ...
Geobits

3

Це не запис, мені завжди цікаво додавати індивідуальні кольори для кожної участі в програмі ;)

А також процес їжі не візуалізується шляхом зміни кольору, а замість цього розміру, щоб ми могли за короткий час побачити кілька подій їжі.

Гра.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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.