Битва за стипендії Кот


45

У цьому виклику ви збираєтесь створити Стипендію з метою перемогти всі інші товариства в бою.

Товариство (команда) складається з 3 персонажів . Кожен персонаж рухається незалежно від решти своєї команди, але їм потрібно буде працювати разом, бореться з вашим ворогом. Команди будуть обертатися один до одного головою до голови. Виграші коштують 3 бали, краватки - 1 бал, а втрати - 0 балів.

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

Персонажі мають бали здоров'я (HP), і коли їх HP потрапляє (або опускається нижче) 0, вони гинуть . Якщо всі персонажі в команді опонента загинуть, то ви виграєте!

Персонажі мають Мана. Більшість дій вимагає виконання Маною , а якщо вас недостатньо, ця дія недоступна для вас.

Персонажі мають затримку повороту . Це визначає кількість кліщів між кожним витком (починається з 100). Нижній краще.

Персонажі мають атрибути . Кожен символ має 5 базів у кожному атрибуті, і вам надається 20 додаткових атрибутивних балів, які потрібно розділити. Після призначення точок атрибутів ваш основний атрибут встановлюється як ваш найвищий атрибут.

Доступними атрибутами є:

  • Міцність: дає 10 макс. HP і .5 HP за оборот
  • Інтелект: дає 7 Макс Мана та .1 Мана за оборот
  • Спритність: зменшує затримку повороту на 1

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

    444
   43334
  4322234
 432111234
 432101234
 432111234
  4322234
   43334
    444

Персонажі мають початкове бачення 2. Бачення між гравцями того ж товариства є спільним.

Як грати

Будівельні
гравці будуватимуть товариство. Вам потрібно виконати наступні дії :

  1. Дайте кожному атрибуту символів очки . Кожен символ починається з 5 у кожній статистиці, з додатковими 20 для розподілу між 3.

  2. Дайте кожному здібності персонажа . Кожен символ починається з 4-х слотів для здібностей, а здібності за замовчуванням займають 1 слот. Деякі здібності є повторюваними , і їх можна надати персонажу кілька разів. Використання набору можливостей для іншого подання без дозволу власника не дозволяється.

  3. Напишіть код для своїх ботів. Код повинен бути на Java і буде використовуватися для боротьби (наступний крок)

Дії

Усі символи починаються з 3 стандартних дій:

  1. Крок : перемістіть свого персонажа в кардинальному діапазоні 1
  2. Фрагмент : атакуйте ворога для PrimaryAttribute в кардинальному діапазоні 1
  3. Посмішка : Не робіть нічого

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

Здібності
Кожен персонаж має 4 слоти для здібностей. Якщо здатність є курсивом, це дія.

Здібності

Ім'я Опис Мобільність відключення Mana 
                                                                                                         

Моргання         Перемістити в квадрат, діапазон 4 2 + 2 
 Змінний          своп місця з цільовими 5 5 
 Телепорт      Переміщення в будь-якому місці 20 5

Тире Збільшити діапазон кроків на 1. Повторюваний                                               
Мобільний крок може рухатися в будь-якому з 8 напрямків                                                  
                                                    Напади                                                    

Швидкий         зріз двічі 3 0 
 Плетіть         Зріжте всіх видимих ​​ворогів раз 15 10

Поглинайте кожен шматочок вкраде 1 основний атрибут вашої цілі. Триває 20 витків                    
Cleave Every Slice завдає 1/2 ураження суміжним ворогам                                           
Critital Додає 30% шансу Slice завдати 200% шкоди. Повторний                               
Святковий кожен шматочок збільшує ваш HP на 3. Повторюваний                                             
Гнучка банка може нарізати будь-який із 8 напрямків                                                      
Мана вкраде Сріз краде 2 мани. Повторний                                                           
Рефлексивний зріз при нарізанні 0 3 
Діапазон Додає 1 до діапазону "Зріз"                                                              
Проведіть пальцем Кожен наступний фрагмент на одній цілі наносить на 3 більше шкоди, ніж останній               
                                                    Статуси                                                     

Dispel        Видаляє всі статуси з цілі. Діапазон 2. 20 10 
 Поєдинок          Заморожує вас і вашу ціль, поки хтось із вас не загине. Діапазон 1 25 0 
 Нокаут      Ви та ціль приголомшені наступними 1000 кліщами 10 10 
 Метеор        Усі вороги приголомшені наступними 100 кліщами 25 10 
 Поворотна         ціль заморожена на 2 наступних витках 4 6 
 Отрута        отрує ворога за 1 HP на 5 витків 5 0 
 Мовчання      Ціль замовчується на 5 витків 5 7 
 Повільна          ціль сповільнюється на 40 кліщів протягом наступних 3 оборотів 10 5 
 Оглушена          ціль приголомшена наступними 300 кліщами 10 10

Холодний. Усі інші символи в межах 2 діапазону сповільнені на 10 кліщів                                
Імунний статус Жоден статус не може бути застосований до вас                                                           
                                                    Оборонний                                                    

Силовий   блок блоку наступних 5 джерел шкоди. Не         стоїть 15 5 
 Привид За поворот усі пошкодження заживають 10 10 
 Заціліть          цільову ціль на 20 HP 10 3 
 Відновіть       Усі одужання відновлені до повного здоров’я 20 40 
 Щит        Ви не можете нарізати до наступної черги 3 0

Ухиляється від 25% шансу, щоб шматочок не вдарив вас. Повторний                                         
Лише стовп можна нарізати один раз на обороті                                                            
Воскрешаючи Коли будете вбиті, поверніться до життя з повним HP (і без статусів) 0 40 
Шипи При нанесенні шкоди поверніть половину збитку назад                                           
                                                     Зір                                                      

        Команда плащів стає невидимою на 5 оборотів 20 20 
 Сховати          Ви невидимі на 5 оборотів 4 7 
 Фаза         Станьте невидимою за 1 виток 0 3 
         Ціль треку не може залишитися невидимою і завдає 10% більше шкоди. Триває 10 витків. 5 5

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

Злив         Наносить 5 ушкоджень Цілі та лікує себе на 5 HP, поки вони залишаються в 1 діапазоні 10 5 
 Блискавка     завдає 15 збитків усім ворогам 20 10 
 K / O           Вбиває ціль, якщо ціль нижче 20% HP 20 0 
 Пастка          Помістіть невидиму пастку. Пастка завдає 15 збитків при наступі. Стеки. 10 2 
 Зап.           Нанесіть 30 ушкоджень, щоб нанести ціль 30 5

Статичні угоди 5 завдають шкоди кожній черзі всім ворогам у межах 1 діапазону. Повторний                       
                                                      Статистика                                                      

Перевертень      Додайте 10 до всіх статистичних даних за 5 витків 30 25

Buff Подвойте свій пул HP. Повторний                                                           
Розумні дії мають 20% коротше вимкнення. Повторний                                             
Цілеспрямовано збільшує ваш рівень регенерації Мани на Int / 10. Повторний                                  
Відновити Збільшує ваш рівень реєстрації на силу / 2. Повторний                                 
Розумні дії коштують на 2 менше мани. Повторний                                                      
Сильний Ви отримуєте 10 балів за атрибути. Повторний                                                  
Слабкий Ви втрачаєте 15 атрибутивних балів. Ви отримуєте 2 слоти для здібностей (для цього потрібен один)                  
                                                      Інший                                                      

Ведмідь          Може викликати ведмідь , який має 5 в кожному стате-10 
 Клон         Клона себе. Займає два слоти для здібностей. 100 100 
 Вкрасти         Замініть цю дію на використану ціль противника останньої дії. Триває 10 витків 5 0 
 Стіна          Створіть непрохідну стіну на націленому порожньому квадраті, діапазон 6 10 10 

Статуси:

  • Оглушення дозволяє вашому персонажу виконувати тільки дію "Усмішка", і триває X тиків .
  • Заморожування не дозволяє вашому персонажу рухатися, і триває X поворотів.
  • Мовчання заважає вашому персонажу виконувати що-небудь, крім усмішки, кроку чи фрагмента, і триває Х поворотів.
  • Отрута шкодить вашому персонажу за X пошкодження на Y поворотах. Якщо ви застосуєте іншу отруту, шкода додається, а тривалість оновиться.
  • Повільний додає X до кількості кліщів між вашими поворотами. Це не впливає на вашу майбутню чергу, лише повертається після.
  • Невидимий робить це, щоб ваш опонент не міг бачити чи пошкоджувати. Якщо ви виконаєте будь-яку дію, окрім кроку або посмішки, вона буде видалена. Якщо ваш опонент має здатність, яка дає їм бачення вас, невидимість видаляється.

Усі статуси (крім Отрути) діють незалежно один від одного.

Бічні примітки:

  • Якщо є основний атрибут, він вирішується як STR> AGI> INT.
  • Ви граєте на сітці 10х10. Команди будуть розміщені на протилежних сторонах.
  • Відсотки складаються мультиплікаційно, за винятком розумних.

Правила подання

Вам потрібно реалізувати 2 функції:

// Create *exactly* 3 Character templates.  You must return the same templates every time
public List<CharacterTemplate> createCharacters();

// Choose an action for a character.  If the action requires a target or location, it must be set.
public ReadonlyAction choose(Set<ReadonlyAction> actions, ReadonlyCharacter character);

Ви також отримаєте доступ до трьох змінних (змінних членів):

Set<ReadonlyCharacter> team;
Set<EnemyCharacter> enemies;
Map<Point2D, EnemyCharacter> visibleEnemies;

Це воно. Нижче ви можете знайти повний API в алфавітному порядку:

class Ability and ReadonlyAbility
    int getNumSlots() returns the number of slots it takes up
    boolean repeatable() returns true if the ability can be repeated
    String name()
class Action and ReadonlyAction
    Set<Point2D> availableLocations()
    Set<ReadonlyCharacter> availableTargets()
    boolean basicAction() returns true if the action is Smile, Step, or Slice
    boolean breaksInvisibiliby()      
    int getCooldown() returns the cooldown cost (not the cooldown remaining)
    int getManaCost()
    String getName()
    int getRemainingCooldown()
    boolean isAvailable() returns true if the action can be performed
    boolean movementAction() returns true if the action is prevented when Frozen
    boolean needsLocation()
    boolean needsTarget()
    void setTarget(ReadonlyCharacter target)
    void setLocation(Point2D location)
class CharacterTemplate
    void addAbility(Ability)
    boolean canAddAbility(Ability)
    List<Ability> currentAbilities()
    Map<Stat, Integer> currentAttributes()
    int getRemainingPoints() returns the total number of ability points you have left to assign
    int getRemainingSlots() returns the total number of slots you have to assign
    int getStat(Stat stat)
    boolean isValid() returns true if your character template is complete and valid
class Point2D
    getX()
    getY()
class Range
    boolean isCardinal() returns true if the range only extends in the 4 cardinal directions
    int getRange() returns the distance of the range
class ReadonlyCharacter and EnemyCharacter
    Class characterClass()
    int cleverness()
    List<ReadonlyAbility> getAbilities()
    Point2D getLocation()   Not on EnemyCharacter
    double getHealth()
    double getMana()
    int getMaxHealth()
    int getMaxMana()
    Range getSightRange()
    Range getSliceRange()
    int getStat(Stat stat)
    Range getStepRange()
    ReadonlyAction getLastAction()
    boolean isFrozen()
    boolean isStunned()
    boolean isPoisoned()
    int getPoisonAmount()
    boolean isSilenced()
    boolean isInvisible()
    boolean isDead()
    Stat primaryStat()
    int smartness()
enum Stat
    INT, STR, AGI

Вище описано всі функції, які, можливо, знадобляться для подання. Рефлексія заборонена. Якщо подання з будь-якої причини недійсне, видаліть його або додайте до заголовка "Недійсне". У вашому поданні не повинно бути декларації про пакет. Ваша заявка повинна міститись у першому багаторядковому кодовому блоці, а перший рядок повинен мати ім'я файлу.

Як запустити проект:

Існує кілька способів:

  1. Завантажте файл JAR і запустіть java -jar Fellowship.jar. Якщо ви хочете завантажити інші матеріали, перейдіть -q 99744. java повинні вказувати на JDK, а не на JRE.
  2. Клоніруйте git repo і біжіть gradle run. Потрібно встановити gradle, і якщо ви хочете передати аргументи, використовуйте-PappArgs="['arg1', 'args2']"
  3. Клоніруйте git repo і складіть його самостійно. Вам знадобляться наступні бібліотеки: org.eclipse.collections:eclipse-collections-api:8.0.0, org.eclipse.collections:eclipse-collections:8.0.0, com.beust:jcommander:1.48, com.google.code.gson:gson:2.7,org.jsoup:jsoup:1.9.2

Якщо ви клонуєтесь, ви повинні використовувати --recursiveпрапор, а коли ви отримуєте оновлення, включайте.--recurse-submodules Для будь-якого з перерахованих вище ваш клас повинен перейти в submissions/javaпапку. Якщо ви використовуєте gradle або збираєте його самостійно, ви можете помістити клас у сам проект. Вам потрібно буде коментувати деякі рядки в основній функції та оновлювати їх, щоб вказати на ваш клас.

Табло:

+------+-------------------+-------+
| Rank | Name              | Score |
+------+-------------------+-------+
|    1 | TheWalkingDead    | 738.0 |
|    2 | RogueSquad        | 686.0 |
|    3 | Spiky             | 641.0 |
|    4 | Invulnerables     | 609.0 |
|    5 | Noob              | 581.0 |
|    6 | Railbender        | 561.0 |
|    7 | Vampire           | 524.0 |
|    8 | LongSword         | 508.0 |
|    9 | SniperSquad       | 456.0 |
|   10 | BearCavalry       | 430.0 |
|   11 | StaticCloud       | 429.0 |
|   12 | PlayerWerewolf    | 388.0 |
|   13 | LongSwordv2       | 347.0 |
|   14 | Derailer          | 304.0 |
|   15 | Sorcerer          | 266.0 |
|   16 | CowardlySniperMk2 | 262.0 |
|   17 | TemplatePlayer    |  59.0 |
+------+-------------------+-------+

Якщо у вас є якісь питання або вам потрібна допомога, прокоментуйте нижче або приєднайтеся до чату ! Хай щастить


1
Коментарі не для розширеного обговорення; ця розмова переміщена до чату .
Денніс

Блискавка вказана як Deal 15 damage to all enemies, але блискавки на невидимих ​​ворогів не впливають. Це помилка? Інакше невидимість здається мені досить сильною ...
CommonGuy

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

2
Так, не знаю, чи варіанти -g, однак, коли я розробляв свого бота, у мене він був ще не в придатному стані, тому я почав робити альтернативу. На даний момент він дуже рудиментарний, але має видимий радіус зору. Ось захоплення гравця Bear Cavalry vs Template! захоплення .
Moogie

1
Чи можете ви протестувати нових символів та оновити рахунок?
Зруйнований лимон

Відповіді:


10

StaticCloud

Хмара, яка постійно зростає, яка завдає статичної шкоди кожному, хто наближається. Він складається з:

  • 1/3 Невидима частина
    • STR: 5; AGI: 5; INT: 25
    • Клон , невидимий , статичний
  • 2/3 Видима частина
    • STR: 5; AGI: 5; INT: 25
    • Клон , статичний , статичний

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

StaticCloud.java
import java.util.Arrays;
import java.util.List;

import org.eclipse.collections.api.set.ImmutableSet;
import org.eclipse.collections.impl.factory.Sets;

import com.nmerrill.kothcomm.game.maps.Point2D;

import fellowship.abilities.ActionAbility;
import fellowship.abilities.damage.Static;
import fellowship.abilities.vision.Invisible;
import fellowship.actions.ReadonlyAction;
import fellowship.actions.other.Clone;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;

public class StaticCloud extends SleafarPlayer {
    private CharacterTemplate invisibleTemplate() {
        return new CharacterTemplate(0, 0, 20,
                new ActionAbility(Clone::new), new Invisible(), new Static());
    }

    private CharacterTemplate visibleTemplate() {
        return new CharacterTemplate(0, 0, 20,
                new ActionAbility(Clone::new), new Static(), new Static());
    }

    @Override
    public List<CharacterTemplate> createCharacters() {
        return Arrays.asList(visibleTemplate(), invisibleTemplate(), visibleTemplate());
    }

    private class InvisibleCloud extends Character {
        protected InvisibleCloud(ReadonlyCharacter delegate) {
            super(delegate);
        }

        @Override
        protected ReadonlyAction choose() {
            ReadonlyAction clone = getAction(Clone.class);
            if (clone != null && (isVisible() || !isInEnemySightRange())) {
                int invisibleCount = countCharacters(InvisibleCloud.class);
                if (invisibleCount > 8 && setClosestSafeLocation(clone, getStaticLocations())) {
                    return clone;
                } else if (setCloneLocation(clone, invisibleCount < 3 ? 3 : 1)) {
                    return clone;
                }
            }
            if (step != null && isVisible() && isInEnemySliceRange() &&
                    setClosestSafeLocation(step, getStaticLocations())) {
                return step;
            }
            if (slice != null && isVisible() && setSliceTarget(slice, 0.01)) {
                return slice;
            }
            if (step != null) {
                ImmutableSet<Point2D> avoidLocations = !isVisible() || isInEnemySliceRange() ?
                        Sets.immutable.empty() : getEnemySliceLocations();
                if ((isVisible() || clone != null) && !getEnemyHiddenLocations().isEmpty() &&
                        setClosestLocation(step, avoidLocations, getEnemyHiddenLocations())) {
                    return step;
                }
                if (!getStaticLocations().contains(getLocation()) &&
                        setClosestLocation(step, avoidLocations, getStaticLocations())) {
                    return step;
                }
            }
            return smile;
        }
    }

    private class VisibleCloud extends Character {
        protected VisibleCloud(ReadonlyCharacter delegate) {
            super(delegate);
        }

        @Override
        protected ReadonlyAction choose() {
            ReadonlyAction clone = getAction(Clone.class);
            if (clone != null) {
                int visibleCount = countCharacters(VisibleCloud.class);
                if (visibleCount > 5 && setClosestSafeLocation(clone, getStaticLocations())) {
                    return clone;
                } else if (setCloneLocation(clone, visibleCount < 3 ? 2 : 1)) {
                    return clone;
                }
            }
            if (step != null && isInEnemySliceRange() && setClosestSafeLocation(step, getStaticLocations())) {
                return step;
            }
            if (slice != null && setSliceTarget(slice, 0.01)) {
                return slice;
            }
            if (step != null && !getStaticLocations().contains(getLocation())) {
                if (isInEnemySliceRange() ? setClosestLocation(step, getStaticLocations()) :
                        setClosestSafeLocation(step, getStaticLocations())) {
                    return step;
                }
            }
            return smile;
        }
    }

    @Override
    protected Character createCharacter(ReadonlyCharacter delegate) {
        if (hasAbility(delegate, Invisible.class)) {
            return new InvisibleCloud(delegate);
        } else {
            return new VisibleCloud(delegate);
        }
    }
}

3
Цей цікавий тим, що статичні не порушують невидимість.
Draco18s

7

Програвач шаблонів

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

Не соромтеся використовувати цей бот як шаблон для створення власного.

Пам'ятайте, що вам потрібно змінити ім'я файлу в першому рядку, а також вибрати власний набір можливостей.

TemplatePlayer.java
import com.nmerrill.kothcomm.game.maps.Point2D;
import fellowship.abilities.ActionAbility;
import fellowship.abilities.attacking.Flexible;
import fellowship.abilities.attacking.Ranged;
import fellowship.actions.ReadonlyAction;
import fellowship.actions.damage.KO;
import fellowship.actions.damage.Zap;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;
import fellowship.Player;
import org.eclipse.collections.api.set.MutableSet;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class TemplatePlayer extends Player{
    private final double CRITICAL_HEALTH = 20;
    @Override
    public List<CharacterTemplate> createCharacters() {
        List<CharacterTemplate> templates = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            templates.add(new CharacterTemplate(10, 5, 5,
                    new Ranged(),
                    new Flexible(),
                    new ActionAbility(KO::new),
                    new ActionAbility(Zap::new)));
        }
        return templates;
    }

    @Override
    public ReadonlyAction choose(Set<ReadonlyAction> actions, ReadonlyCharacter character) {
        int minPriority = Integer.MAX_VALUE;
        ReadonlyAction chosen = null;
        for (ReadonlyAction action: actions){
            int priority = getPriorityFor(action, character);
            if (priority < minPriority){
                chosen = action;
                minPriority = priority;
            }
        }
        if (chosen == null){
            throw new RuntimeException("No valid actions");
        }
        if (chosen.needsLocation()){
            chosen.setLocation(chooseLocationFor(chosen, character));
        } else if (chosen.needsTarget()){
            chosen.setTarget(chooseTargetFor(chosen));
        }
        return chosen;
    }

    private Point2D chooseLocationFor(ReadonlyAction action, ReadonlyCharacter character){
        if (action.movementAction()){
            if (character.getHealth() < CRITICAL_HEALTH){
                return fromEnemy(action.availableLocations());
            } else {
                return toEnemy(action.availableLocations());
            }
        }
        return toTeam(action.availableLocations());
    }

    private Point2D toEnemy(MutableSet<Point2D> availableLocations){
        if (visibleEnemies.isEmpty()){
            return availableLocations.iterator().next();
        }
        return availableLocations.minBy(p1 ->
                p1.cartesianDistance(visibleEnemies.keysView().minBy(p1::cartesianDistance))
        );
    }

    private Point2D fromEnemy(MutableSet<Point2D> availableLocations){
        if (visibleEnemies.isEmpty()){
            return availableLocations.iterator().next();
        }
        return availableLocations.maxBy(p1 ->
                p1.cartesianDistance(visibleEnemies.keysView().minBy(p1::cartesianDistance))
        );
    }

    private Point2D toTeam(MutableSet<Point2D> availableLocations){
        if (team.isEmpty()){
            return availableLocations.iterator().next();
        }
        return availableLocations.minBy(p1 ->
                p1.cartesianDistance(team.collect(ReadonlyCharacter::getLocation).minBy(p1::cartesianDistance))
        );
    }

    private ReadonlyCharacter chooseTargetFor(ReadonlyAction action){
        return action.availableTargets().minBy(ReadonlyCharacter::getHealth);
    }

    private int getPriorityFor(ReadonlyAction action, ReadonlyCharacter character){
        if (character.isInvisible() && action.breaksInvisibility()){
            return 1000;
        }
        if (action.getName().equals("Smile")){
            return 999;
        }
        if (action.movementAction()){
            if (character.getHealth() < 20){
                return 0;
            }
            return 998;
        }
        if (action.needsTarget()) {
            return ((int) action.availableTargets().minBy(ReadonlyCharacter::getHealth).getHealth());
        }
        return 997;
    }
}

7

ТрусливийSniperMk2

Використовує Zap , FarSight * 2 та Hide .

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

У режимі відстеження та достатнього скидання мани та виходу з ладу вимкнеться "Зап" найслабкіший видимий ворог.

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

Зверніть увагу, оскільки 'Zap' нескінченний діапазон, члени команди при націлюванні націляться на одного бота.

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

CowardlySniperMk2.java

import com.nmerrill.kothcomm.game.maps.Point2D;
import fellowship.abilities.ActionAbility;
import fellowship.abilities.vision.FarSight;
import fellowship.actions.ReadonlyAction;
import fellowship.actions.damage.Zap;
import fellowship.actions.vision.Hide;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import fellowship.*;

public class CowardlySniperMk2 extends Player{

    private final static boolean DEBUG=false; 
    private static Point2D lastAttackedEnemyLocation = null;
    private static HashMap<ReadonlyCharacter, Boolean> rechargingManaMap = new HashMap<>();
    private final double STANDARD_VISION_MOVEMENT_BUFFER = 3;
    private final double MIN_VISION_DISTANCE = 2;

    @Override
    public List<CharacterTemplate> createCharacters() {
        List<CharacterTemplate> templates = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            templates.add(new CharacterTemplate(8, 8, 4,
                    new ActionAbility(Zap::new),
                    new FarSight(),
                    new FarSight(),
                    new ActionAbility(Hide::new)));
        }
        return templates;
    }

    @Override
    public ReadonlyAction choose(Set<ReadonlyAction> actions, ReadonlyCharacter character) {

        // get last flag for recharging mana
        Boolean rechargingMana = rechargingManaMap.get(character);
        if (rechargingMana == null || rechargingMana)
        {
            rechargingMana = !(character.getMana()>0.90*character.getMaxMana());
        }
        else
        {
            rechargingMana = character.getMana()<0.10*character.getMaxMana();
        }

        rechargingManaMap.put(character,rechargingMana);

        HashMap<Integer, ReadonlyAction> validActions = new HashMap<>();
        HashMap<Integer, String> actionString = new HashMap<>();

        // see if we have arrived at the last attack location of the enemy
        if (character.getLocation().equals(lastAttackedEnemyLocation))
        {
            lastAttackedEnemyLocation = null;
        }

        double closestEnemyVisionDistance = Double.MAX_VALUE;
        for ( Point2D enemyLocation : visibleEnemies.keySet())
        {
            final int enemyVisibiltyRange = visibleEnemies.get(enemyLocation).getSightRange().getRange();
            double visionDistanceDiff = character.getLocation().diagonalDistance(enemyLocation)-enemyVisibiltyRange;
            if (visionDistanceDiff< closestEnemyVisionDistance)
            {
                closestEnemyVisionDistance = visionDistanceDiff;
            }
        }

        for (ReadonlyAction action: actions){

            int priority=-1;
            String message = "";
            switch (action.getName())
            {
                case "Hide":
                    // are we, or will we be within sight range of an enemy
                    if (closestEnemyVisionDistance < STANDARD_VISION_MOVEMENT_BUFFER )
                    {
                        if (!character.isInvisible())
                        {
                            message = ""+closestEnemyVisionDistance;
                            priority = 1000;
                        }
                    }
                    break;

                case "Step":

                    Point2D chosenLocation = null;

                    // are we within sight range of an enemy or are we recharging mana?
                    if (closestEnemyVisionDistance < MIN_VISION_DISTANCE || rechargingMana)
                    {
                        message = "Fleeing (Seen) "+ closestEnemyVisionDistance;
                        priority = 800;

                        if (character.isInvisible())
                        {
                            message = "Fleeing (UnSeen) "+ closestEnemyVisionDistance;
                            priority = 500;
                        }

                        // simple enemy avoidance... chose location that is farthest away from closest enemy
                        double furthestDistance = 0;

                        for ( Point2D enemyLocation : visibleEnemies.keySet())
                        {
                            for (Point2D location : action.availableLocations())
                            {
                                if (location.diagonalDistance(enemyLocation) > furthestDistance)
                                {
                                    furthestDistance = location.diagonalDistance(enemyLocation);
                                    chosenLocation = location;
                                }
                            }
                        }

                        if (chosenLocation == null)
                        {
                            // no moves are better than staying in current location
                            priority = -1;
                            break;
                        }
                    }
                    // are we "tracking" an enemy?
                    else if (lastAttackedEnemyLocation !=null)
                    {
                        priority = 20;
                        message = "Tracking "+ closestEnemyVisionDistance;

                        // head toward last attacked enemy location
                        double distance = Integer.MAX_VALUE;
                        for (Point2D location : action.availableLocations())
                        {
                            if (location.diagonalDistance(lastAttackedEnemyLocation) < distance)
                            {
                                distance = location.diagonalDistance(lastAttackedEnemyLocation);
                                chosenLocation = location;
                            }
                        }
                    }
                    // are we outside the sight range of all enemies?
                    else if (closestEnemyVisionDistance > STANDARD_VISION_MOVEMENT_BUFFER)
                    {
                        // scout for an enemy

                        priority = 10;
                        message = "Scouting "+ closestEnemyVisionDistance;

                        // dumb random location selection... not optimal but is sufficent.
                        int index = getRandom().nextInt(action.availableLocations().size());
                        for (Point2D location : action.availableLocations())
                        {
                            chosenLocation= location;
                            if (--index == 0)
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                        // we are in the sweet zone... just out of enemy sight range but within our sight range
                        break;
                    }

                    action.setLocation(chosenLocation);
                    break;

                case "Zap":
                    message = ""+closestEnemyVisionDistance;
                    ReadonlyCharacter chosenTarget = null;
                    double chosenTargetHealth = Double.MAX_VALUE;

                    // target the weakest enemy
                    for (ReadonlyCharacter target : action.availableTargets())
                    {
                        if (target.getHealth() < chosenTargetHealth)
                        {
                            chosenTargetHealth = target.getHealth();
                            chosenTarget = target;
                        }
                    }

                    if (chosenTarget != null)
                    {
                        priority = 100;
                        action.setTarget(chosenTarget);
                        lastAttackedEnemyLocation = chosenTarget.getLocation();
                    }
                    else
                    {
                        // nothing to target
                    }

                    break;

                case "Smile":
                    priority = 0;
                    break;
            }

            // add the action to the collection of valid actions to perform
            if (priority >-1)
            {
                validActions.put(priority, action);
                actionString.put(priority, message);
            }

        }


        int highestPriority = -1;
        ReadonlyAction chosen = null;

        // choose the highest priority action
        for (Integer priority : validActions.keySet())
        {
            if (priority > highestPriority)
            {
                highestPriority = priority;
                chosen = validActions.get(priority);
            }
        }
        String message = actionString.get(highestPriority);

        if (chosen == null){
            throw new RuntimeException("No valid actions");
        }

        if (DEBUG) System.out.println(this+"("+System.identityHashCode(character)+"): "+chosen.getName()+ (rechargingMana?" Mana_charge":" Mana_usable")+" H: "+character.getHealth()+" M: "+character.getMana() +(character.isInvisible()?" InVis":" Vis") +" x: "+character.getLocation().getX()+" y: "+character.getLocation().getY()+" "+message);
        return chosen;
    }
}

Ім'я файлу має бути CowardlySniperMk2 :)
Натан Мерріл

oops: P вирізати та вставити змушує мене виглядати дурнем!
Moogie

Нагадує мені про Dasher MK2 MarioKart Wii :)
Kritixi Lithos

@KritixiLithos нагадує мені Bamboozler Splatoon 14 Mk II. ;)
ТНТ

7

Ходячі мерці

Зомбі, їх усі знають. Вони залишаються в групі, не роблячи нічого, поки хтось не з’явиться. Їх важко вбити, і скільки б ви не вбили, завжди більше. І вони, як правило, з'являються з нізвідки просто за вашою спиною.

  • 1 х зомбі №1 (найсильніший, а отже, альфа-зомбі)
    • STR: 25; AGI: 5; INT: 15
    • Клон , воскрес , сильний
  • 2 х зомбі №2 (ніхто не хотів бути зомбі №3 у завершальних кредитах, тому обидва отримали однакову кількість)
    • STR: 15; AGI: 5; INT: 15
    • Клон , воскресіння , поглинання

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

TheWalkingDead.java
import java.util.Arrays;
import java.util.List;

import fellowship.abilities.ActionAbility;
import fellowship.abilities.attacking.Absorb;
import fellowship.abilities.defensive.Resurrect;
import fellowship.abilities.stats.Strong;
import fellowship.actions.ReadonlyAction;
import fellowship.actions.other.Clone;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;

public class TheWalkingDead extends SleafarPlayer {
    private CharacterTemplate zombie1Template() {
        return new CharacterTemplate(20, 0, 10, new ActionAbility(Clone::new), new Resurrect(), new Strong());
    }

    private CharacterTemplate zombie2Template() {
        return new CharacterTemplate(10, 0, 10, new ActionAbility(Clone::new), new Resurrect(), new Absorb());
    }

    @Override
    public List<CharacterTemplate> createCharacters() {
        return Arrays.asList(zombie1Template(), zombie2Template(), zombie2Template());
    }

    private class Zombie extends Character {
        private int resurrectCountdown = 0;
        private double oldHealth;

        protected Zombie(ReadonlyCharacter delegate) {
            super(delegate);
            this.oldHealth = getHealth();
        }

        @Override
        protected ReadonlyAction choose() {
            if (getHealth() > oldHealth + getHealthRegen() + 0.1) {
                resurrectCountdown = 40;
            }
            if (resurrectCountdown > 0) {
                --resurrectCountdown;
            }
            oldHealth = getHealth();

            ReadonlyAction clone = getAction(Clone.class);
            if (resurrectCountdown > 0) {
                if (step != null && isInEnemySliceRange() && setAvoidEnemiesLocation(step)) {
                    return step;
                }
                if (clone != null && !getSliceLocations().isEmpty() && setClosestLocation(clone, getSliceLocations())) {
                    return clone;
                }
                if (clone != null && setCloneLocation(clone, 1)) {
                    return clone;
                }
                if (slice != null && setSliceTarget(slice, 0.01)) {
                    return slice;
                }
                if (step != null && setAvoidEnemiesLocation(step)) {
                    return step;
                }
            } else {
                if (clone != null && !getSliceLocations().isEmpty() && setClosestLocation(clone, getSliceLocations())) {
                    return clone;
                }
                if (clone != null && setCloneLocation(clone, 1)) {
                    return clone;
                }
                if (slice != null && setSliceTarget(slice, 0.01)) {
                    return slice;
                }
                if (step != null && !getSliceLocations().isEmpty() && setClosestLocation(step, getSliceLocations())) {
                    return step;
                }
            }
            return smile;
        }
    }

    @Override
    protected Character createCharacter(ReadonlyCharacter delegate) {
        return new Zombie(delegate);
    }
}

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

@Moogie Nathan вирішив проблему .
Sleafar

7

Невразливі

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

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

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

Волхв у шкідливій сфері

  • STR : 5; AGI : 5; INT : 25

Маг захищає себе за допомогою комбінації стовпа та поля сили ; будучи зосередженим і володіючи високим інтелектом, він відновлює достатню кількість мани, щоб кожен раз кидати Силове поле на перезарядку. Якщо припустити, що опоненти не мають прискореної спритності, вони можуть вражати її максимум п’ятьма зрізами за п’ять оборотів, а п’ять джерел ураження будуть заблоковані протягом цього періоду. Іншими словами, Маг абсолютно вимагає заклинань, щоб перемогти; Нарізка не працює сама по собі, незалежно від того, наскільки ви хороші в цьому.

Чарівник може напасти через "Срізання" на 25 у надзвичайних ситуаціях, але в основному кидає отруту повторно, що дуже спамує з високою регенерацією МП. Оскільки отрута має нескінченний діапазон, обмежений лише баченням команди, то Маг - це спосіб, яким ця команда б'є дуже ворогів, важких чи регенеративних HP; решта команди підтримують зір на них, тоді як Маг завдає квадратичну кількість збитку. Отрута неминуче в кінцевому підсумку випереджає їх регенерацію HP, і мені не доведеться турбуватися про статичні пошкодження, такі як Спайкс у процесі.

Гігантський скелетонний сокіл

  • STR : 35; AGI : 5; INT : 5

Основна робота Falconer полягає в тому, щоб отримати решту команди бачення цілей, щоб їх можна було атакувати, розвівши карту очима сокола. Далекий приціл дає достатньо зору, що вороги, які намагаються сховатися чи втекти, зазвичай можуть бути захоплені зоною зору у куті карти; не дуже легко дати Сокольнику відсіч. True Sight - головний призив цієї команди проти невидимих ​​ворогів, яких інакше було б неможливо пошкодити. Будучи великим і, таким чином, Сильним , робить Сокольник дуже стійким до пошкоджень та здатним до важких зрізів при необхідності. Нарешті, Сокіллер може випустити соколів на ворожий натовп внизу, наказавши їм плести серед ворога і завдаючи великої (35) шкоди площі.

На додаток до роботи Соколера по полюванню ухиляються від ворогів та збереженню зору ворогів, щоб Маг міг продовжувати їх отруєння, можливість періодично плести до 35 є ключовим для боротьби з ворожими командами рою; таким чином можна вразити багатьох ворогів і залишити їх досить низькими, щоб решта команди закінчилася (в ідеалі з іншим Weave). Роїння - це майже переможена стратегія за цими правилами, і Weave - один з небагатьох реальних лічильників, які він має. Навіть тоді це не дуже добре саме по собі.

Trollbone Skullrender

  • STR : 25; AGI : 5; INT : 5

Завдання Trollbone - тримати придушені орди ворога, тоді як інші підрозділи можуть виконувати свою роботу. Так само, як і Маг, Trollbone має заклинання нескінченного діапазону в нокауті . Це дуже добре поєднується з отрутою Мага; якщо коли-небудь вдасться зіткнутися з противником один на один (і проти багатьох команд, так і є), Сокольниця здобуде зір, Троллбон приголомшить їх, тоді Маг покладе на них отруту, і вони закінчаться мертвими без можливості робити що-небудь (нокаут триває 1000 кліщів у цілі, і Trollbone регенерує відключення для нього трохи швидше, ніж це). Це також дуже добре захищає Trollbone від одиноких сильних ворогів; вони нічого не можуть зробити йому, якщо вони не свідомі. Звичайно, розбиті черепи з ворогом можуть залишати обох зірваних,оглушити і отруїти (і купу інших статусів ніхто не хвилює). Як персонаж, орієнтований на заклинання, який, тим не менше, не дуже магічно схильний, Trollbone відновлює магію не за допомогою інтелекту, а за допомогою пиття крові своїх ворогів, роблячи крадіжку Мани з кожним ударом; це дає досить гарну швидкість регенерації МП (а приголомшені вороги роблять легкі цілі для викрадення МП у). Нарешті, Троллбон періодично вирує на сказ і буде переплести ворожі ряди, в той час як забиває голову і п'є їх кров. Проти досить великого рою ворогів це фактично відновлює ману, і вона може закінчити рій, який ослабив Сокольник (25 + 35 - 60, тому він працює, навіть якщо вороги регенерували певною мірою між ними).

Стратегія

На відміну від багатьох команд, я приділяв багато уваги AI, а не лише командному будівництву. Основним правилом є те, що команда завжди намагатиметься згрупуватися, якщо вони не зайняті чимось іншим, що ускладнить їх оточення та можливість захищати один одного. Якщо їх роять, вони спробують сховатися в кутку. З іншого боку, якщо противник намагається втекти або втекти, вони бродять по карті, підбираючи і прямуючи до випадкових куточків або центру; це більш-менш гарантує, що Сокольник врешті-решт виявить ціль. Рух розроблений так, щоб ніколи не дозволяти супротивнику отримати перший удар, якщо це можливо; ворогу доведеться самостійно проникати в зону зрізу. Маг завжди будезалиште МП для Силового поля, запобігаючи втраті виснаження МП (єдиний спосіб цього може провалитись - з "Поглинати", який може потрапити через Силове поле, навіть якщо шкоди не буде). Зазвичай це не проблема; зазвичай Маг може спамувати отруту на кожному кроці без проблем. Якщо це не заважає, команда вважає за краще переслідувати ворогів один за одним, приголомшуючи їх, коли вони потрапляють у поле зору, а потім повторно отруюючи їх, поки вони не загинуть. З іншими ворогами навколо, команда намагатиметься їх збити, якщо це можливо, бігаючи по колах і змушуючи більшість ворогів переслідувати, при цьому приголомшуючи і отруюючи одного з них. Основна проблема - із роями, саме тому тут є так багато ткацьких виробів, але навіть тоді, здається, важко реально обіграти стратегію.

Invulnerables.java
import com.nmerrill.kothcomm.game.maps.Point2D;
import fellowship.abilities.*;
import fellowship.abilities.attacking.*;
import fellowship.abilities.defensive.*;
import fellowship.abilities.vision.*;
import fellowship.abilities.stats.*;
import fellowship.abilities.statuses.*;
import fellowship.actions.*;
import fellowship.actions.attacking.*;
import fellowship.actions.damage.*;
import fellowship.actions.defensive.*;
import fellowship.actions.statuses.*;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;
import fellowship.characters.EnemyCharacter;
import fellowship.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class Invulnerables extends Player {
  @Override
  public List<CharacterTemplate> createCharacters() {
    List<CharacterTemplate> templates = new ArrayList<>();

    templates.add(new CharacterTemplate(0, 0, 20,
                                        new ActionAbility(Poison::new),
                                        new ActionAbility(ForceField::new),
                                        new Focused(),
                                        new Pillar()));

    templates.add(new CharacterTemplate(30, 0, 0,
                                        new ActionAbility(Weave::new),
                                        new Strong(),
                                        new FarSight(),
                                        new TrueSight()));

    templates.add(new CharacterTemplate(20, 0, 0,
                                        new ActionAbility(Weave::new),
                                        new ActionAbility(Knockout::new),
                                        new ManaSteal(),
                                        new Immune()));

    return templates;
  }

  private String lastIdentifier(String s) {
    String[] split = s.split("\\W");
    return split[split.length - 1];
  }

  private boolean hasAbility(ReadonlyCharacter character, String abilityName) {
    for (ReadonlyAbility ability : character.getAbilities()) {
      if (lastIdentifier(ability.name()).equals(abilityName))
        return true;
    }
    return false;
  }

  private boolean hasAbility(EnemyCharacter character, String abilityName) {
    for (ReadonlyAbility ability : character.getAbilities()) {
      if (lastIdentifier(ability.name()).equals(abilityName))
        return true;
    }
    return false;
  }

  private int getSquareDanger(ReadonlyCharacter character, Point2D square) {
    /* A square's danger is basically equal to the number of hits we'd
       expect to take when standing there. Each hit is worth 1; a hit of
       25 damage or more is worth 2. */
    int sliceDanger = 0;
    int otherDanger = 0;
    int cx = square.getX();
    int cy = square.getY();
    for (Point2D enemyLocation : visibleEnemies.keysView()) {
      EnemyCharacter enemy = visibleEnemies.get(enemyLocation);
      if (enemy.isStunned())
        continue; /* approaching stunned enemies is a good thing */
      int dx = enemyLocation.getX() - cx;
      int dy = enemyLocation.getY() - cy;
      if (dx < 0)
        dx = -dx;
      if (dy < 0)
        dy = -dy;
      if (dx + dy <= 1) {
        /* We're in Static range. */
        if (hasAbility(enemy, "Static"))
          otherDanger++;
      }
      if (dx + dy <= enemy.getSliceRange().getRange() &&
          (dx * dy == 0 || !enemy.getSliceRange().isCardinal())) {
        int sliceMultiplier = 1;
        if (hasAbility(enemy, "Quick") && !hasAbility(character, "Pillar"))
          sliceMultiplier *= 2;
        if (enemy.getStat(enemy.primaryStat()) >= 25)
          sliceMultiplier *= 2;
        if (hasAbility(character, "Pillar")) {
          if (sliceDanger >= sliceMultiplier)
            continue;
          sliceDanger = 0;
        }
        sliceDanger += sliceMultiplier;
      }
    }
    return sliceDanger + otherDanger;
  }

  private ReadonlyAction[] forceFieldAction = new ReadonlyAction[3];
  private int goalX = 5;
  private int goalY = 5;

  @Override
  public ReadonlyAction choose(Set<ReadonlyAction> actions, ReadonlyCharacter character) {

    /* Which character are we? */
    int characterNumber;
    if (hasAbility(character, "Focused"))
      characterNumber = 0;
    else if (hasAbility(character, "Immune"))
      characterNumber = 1;
    else if (hasAbility(character, "TrueSight"))
      characterNumber = 2;
    else
      throw new RuntimeException("Unrecognised character!");

    /* If we're at the goal square, pick a new one. */
    if (goalX == character.getLocation().getX() &&
        goalY == character.getLocation().getY()) {
      int i = getRandom().nextInt(5);
      goalX = i < 2 ? 1 : i > 2 ? 9 : 5;
      goalY = i == 2 ? 5 : (i % 2) == 1 ? 1 : 9;
    }

    /* If there are a lot of visible enemies, try to group up in a corner in order
       to prevent being surrounded. */
    if (visibleEnemies.size() > 3) {
      int xVotes = 0;
      int yVotes = 0;
      for (ReadonlyCharacter ally : team) {
        xVotes += ally.getLocation().getX() >= 5 ? 1 : -1;
        yVotes += ally.getLocation().getY() >= 5 ? 1 : -1;
      }
      goalX = xVotes > 0 ? 9 : 0;
      goalY = yVotes > 0 ? 9 : 0;
    }

    /* We need to know our Force Field cooldowns even between turns, so store the
       actions in a private field for later use (they aren't visible via the API) */
    for (ReadonlyAction action : actions) {
      if (action.getName().equals("ForceField"))
        forceFieldAction[characterNumber] = action;
    }

    /* If we know Force Field, ensure we always hang on to enough mana to cast it, and
       never allow our mana to dip low enough that it wouldn't regenerate in time. */
    double mpFloor = 0.0;
    if (forceFieldAction[characterNumber] != null) {
      double mpRegen = character.getStat(Stat.INT) / 10.0;
      if (hasAbility(character, "Focused"))
        mpRegen *= 2;
      mpFloor = forceFieldAction[characterNumber].getManaCost();
      mpFloor -= forceFieldAction[characterNumber].getRemainingCooldown() * mpRegen;
    }
    if (mpFloor > character.getMana())
      mpFloor = character.getMana();

    /* We use a priority rule for actions. */
    int bestPriority = -2;
    ReadonlyAction bestAction = null;
    for (ReadonlyAction action : actions) {
      int priority = 0;
      if (lastIdentifier(action.getName()).equals("ForceField"))
        priority = 20; /* top priority */
      else if (character.getMana() - action.getManaCost() < mpFloor) {
        continue; /* never spend mana if it'd block a force field */
      } else if (lastIdentifier(action.getName()).equals("Quick") ||
                 lastIdentifier(action.getName()).equals("Slice")) {
        int damagePotential =
          lastIdentifier(action.getName()).equals("Quick") ? 50 : 25;
        /* We use these abilities with very high priority to /kill/ an enemy
           who's weak enough to die from the damage. If they wouldn't die,
           we're much more wary about attacking; we do it only if we have
           nothing better to do and it's safe. */
        ReadonlyCharacter chosenTarget = null;
        for (ReadonlyCharacter target : action.availableTargets()) {
          if (!isEnemy(target))
            continue;
          if (target.getHealth() <= damagePotential) {
            chosenTarget = target;
            priority = (damagePotential == 25 ? 19 : 18);
            break; /* can't do beter than this */
          }
          if (hasAbility(target, "Spikes") ||
              hasAbility(target, "Reflexive"))
            /*  (target.getLastAction() != null &&
                target.getLastAction().getName().equals("Ghost")) */
            continue; /* veto the target */
          priority = (damagePotential == 25 ? 3 : 4);
          chosenTarget = target;
        }
        if (chosenTarget == null)
          continue;
        action.setTarget(chosenTarget);
      } else if (lastIdentifier(action.getName()).equals("Weave")) {
        priority = visibleEnemies.size() >= 3 ? 14 :
          visibleEnemies.size() >= 1 ? 6 : -1;
      } else if (lastIdentifier(action.getName()).equals("Smile")) {
        /* If there's a stunned or poisoned enemy in view, we favour Smile
           as the idle action, rather than exploring, so that we don't
           move it out of view. Exception: if they're the only enemy;
           in that case, hunt them down. Another exception: if we're
           running into a corner. */
        for (EnemyCharacter enemy : visibleEnemies) {
          if (enemy.isStunned() || enemy.isPoisoned())
            if (visibleEnemies.size() > 1 && visibleEnemies.size() < 4)
              priority = 2;
        }
        /* otherwise we leave it as 0, and Smile only as a last resort */
      } else if (lastIdentifier(action.getName()).equals("Knockout")) {
        /* Use this only on targets who have more than 50 HP. It doesn't
           matter where they are: if we can see them now, knocking them
           out will guarantee we can continue to see them. Of course, if
           they're already knocked out, don't use it (although this case
           should never come up). If there's only one enemy target in
           view, knocking it out has slightly higher priority, because
           we don't need to fear enemy attacks if all the enemies are
           knocked out.

           Mildly favour stunning poisoned enemies; this reduces the
           chance that they'll run out of sight and reset the poison. */
        ReadonlyCharacter chosenTarget = null;
        for (ReadonlyCharacter target : action.availableTargets())
          if ((target.getHealth() > 50 || target.isPoisoned()) &&
              !target.isStunned() && isEnemy(target)) {
            chosenTarget = target;
            if (target.isPoisoned())
              break;
          }
        if (chosenTarget == null)
          continue;
        action.setTarget(chosenTarget);
        priority = visibleEnemies.size() == 1 ? 17 : 15;
      } else if (lastIdentifier(action.getName()).equals("Poison")) {
        /* Use this preferentially on stronger enemies, and preferentially
           on enemies who are more poisoned. We're willing to poison
           almost anyone, although weak enemies who aren't poisoned
           are faster to kill via slicing. The cutoff is at 49, not 50,
           so that in the case of evasive enemies who we can't hit any
           other way, we can wear them one at a time using poison. */
        ReadonlyCharacter chosenTarget = null;
        int chosenTargetPoisonLevel = -1;
        for (ReadonlyCharacter target : action.availableTargets()) {
          int poisonLevel = 0;

          if (!isEnemy(target))
            continue;
          if (target.isPoisoned())
            poisonLevel = target.getPoisonAmount() + 1;
          if (poisonLevel < chosenTargetPoisonLevel)
            continue;
          if (poisonLevel == 0 && target.getHealth() <= 49)
            continue; /* prefer stronger targets */
          if (poisonLevel == 0 && target.getHealth() == 50 &&
              chosenTarget != null)
            continue; /* we poison at 50, but not with other options */
          chosenTarget = target;
          chosenTargetPoisonLevel = poisonLevel;
          priority = 12;
        }
        if (chosenTarget == null)
          continue;
        action.setTarget(chosenTarget);
      } else if (action.movementAction()) {
        /* A move to a significantly safer square is worth 16.
           A move to a mildly safer square is worth 8.
           Otherwise, move to group, either with the enemy,
           the team, or the goal, at priority 1, if we
           safely can; that's our "idle" action. */
        int currentSquareDanger =
          getSquareDanger(character, character.getLocation());
        int bestSquareDanger = currentSquareDanger;
        int bestGroupiness = 0;
        Point2D bestLocation = null;
        priority = 1;
        for (Point2D location :
               action.availableLocations().toList().shuffleThis(getRandom())) {
          int danger = getSquareDanger(character, location);
          if (danger > bestSquareDanger)
            continue;
          else if (danger < bestSquareDanger) {
            priority = (currentSquareDanger - danger > 2)
              ? 16 : 8;
            bestSquareDanger = danger;
            bestLocation = location;
            bestGroupiness = 0; /* reset the tiebreak */
          }

          int cx = character.getLocation().getX();
          int xDelta = location.getX() - cx;
          int cy = character.getLocation().getY();
          int yDelta = location.getY() - cy;
          int groupiness = 0;
          /* Always hunt down a visible enemy when they're the only
             remaining enemy and doing so is safe. Otherwise, still
             favour hunting them down, but in that situation also
             consider factors like grouping and exploration. */
          for (Point2D enemyLocation : visibleEnemies.keysView())
            if (xDelta * (enemyLocation.getX() - cx) > 0 ||
                yDelta * (enemyLocation.getY() - cy) > 0)
              groupiness += (visibleEnemies.size() == 1 ? 99 : 5);
          /* If there are 4 or more visible enemies, then grouping is
             vitally important (so as to not get surrounded).
             Otherwise, it's more minor. */
          for (ReadonlyCharacter ally : team)
            if (xDelta * (ally.getLocation().getX() - cx) > 0 ||
                yDelta * (ally.getLocation().getY() - cy) > 0)
              groupiness += (visibleEnemies.size() > 3 ? 99 : 3);
          /* When exploring, we bias towards random map locations,
             changing location when we reach them. This helps us beat
             enemies that hide in the corners. When there are a lot
             of visible enemies, this changes to a bias to hide in a
             corner. */
          if (xDelta * (goalX - cx) > 0 ||
              yDelta * (goalY - cy) > 0)
            groupiness += (visibleEnemies.size() > 3 ? 99 : 4);
          if (groupiness >= bestGroupiness) {
            bestLocation = location;
            bestGroupiness = groupiness;
            /* leave priority, safety untouched */
          }
        }
        if (bestLocation == null)
          continue;
        action.setLocation(bestLocation);
      } else
        throw new RuntimeException("unknown action" + action.getName());

      if (priority > bestPriority) {
        bestPriority = priority;
        bestAction = action;
      }
    }
    if (bestAction == null)
      throw new RuntimeException("no action?");

    return bestAction;
  }
}

дуже ефективно :) приголомшливий виглядає як ефективна стратегія!
Moogie

Твій персонаж із ForceField не може перемогти моїх Noobs, навіть якщо він має лише 50 здоров'я!
Kritixi Lithos

Виправлена ​​помилка, яку вказав @Sleafar.

я думаю , що у Вас є кілька дуже багато на ваші очок майстерності сокольничим і trollbone
Eumel

Виправлено також. Це був просто друкарський опис, код був правильний.

7

RogueSquad

Загін шахраїв складається з:

  • 1 Розвідник (залишається в тіні, вивчаючи карту)
    • STR: 5; AGI: 5; INT: 25
    • Клон , невидимий , далекогляд
  • 2 Assasins (атакуйте ворогів смертельною отрутою)
    • STR: 5; AGI: 5; INT: 25
    • Клон , отрута , зосереджений

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

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

RogueSquad.java
import java.util.Arrays;
import java.util.List;

import fellowship.abilities.ActionAbility;
import fellowship.abilities.stats.Focused;
import fellowship.abilities.vision.FarSight;
import fellowship.abilities.vision.Invisible;
import fellowship.actions.ReadonlyAction;
import fellowship.actions.other.Clone;
import fellowship.actions.statuses.Poison;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;

public class RogueSquad extends SleafarPlayer {
    private CharacterTemplate scoutTemplate() {
        return new CharacterTemplate(0, 0, 20,
                new ActionAbility(Clone::new), new Invisible(), new FarSight());
    }

    private CharacterTemplate assasinTemplate() {
        return new CharacterTemplate(0, 0, 20,
                new ActionAbility(Clone::new), new ActionAbility(Poison::new), new Focused());
    }

    @Override
    public List<CharacterTemplate> createCharacters() {
        return Arrays.asList(assasinTemplate(), scoutTemplate(), assasinTemplate());
    }

    private class Scout extends Character {
        protected Scout(ReadonlyCharacter delegate) {
            super(delegate);
        }

        @Override
        protected ReadonlyAction choose() {
            ReadonlyAction clone = getAction(Clone.class);
            if (clone != null && (isVisible() || !isInEnemySightRange()) && setCloneLocation(clone, 3)) {
                return clone;
            }
            if (step != null && isVisible() && isInEnemySliceRange() && setAvoidEnemiesLocation(step)) {
                return step;
            }
            if (slice != null && isVisible() && setSliceTarget(slice, 0.01)) {
                return slice;
            }
            if (step != null && isVisible() && setAvoidEnemiesLocation(step)) {
                return step;
            }
            if (step != null && !isVisible() && setExploreLocation(step)) {
                return step;
            }
            return smile;
        }
    }

    private class Assasin extends Character {
        protected Assasin(ReadonlyCharacter delegate) {
            super(delegate);
        }

        @Override
        protected ReadonlyAction choose() {
            ReadonlyAction clone = getAction(Clone.class);
            ReadonlyAction poison = getAction(Poison.class);
            if (clone != null && setCloneLocation(clone, 1)) {
                return clone;
            }
            if (step != null && isInEnemySliceRange() && setAvoidEnemiesLocation(step)) {
                return step;
            }
            if (slice != null && setSliceTarget(slice, 0.01)) {
                return slice;
            }
            if (poison != null && setPoisonTarget(poison)) {
                return poison;
            }
            if (step != null && setAvoidEnemiesLocation(step)) {
                return step;
            }
            return smile;
        }
    }

    @Override
    protected Character createCharacter(ReadonlyCharacter delegate) {
        if (hasAbility(delegate, Invisible.class)) {
            return new Scout(delegate);
        } else if (hasAbility(delegate, Poison.class)) {
            return new Assasin(delegate);
        } else {
            throw new IllegalArgumentException();
        }
    }
}

Базовий клас для всіх моїх ботів

SleafarPlayer.java
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.collections.api.RichIterable;
import org.eclipse.collections.api.map.ImmutableMap;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.api.set.ImmutableSet;
import org.eclipse.collections.api.set.MutableSet;
import org.eclipse.collections.api.tuple.Pair;
import org.eclipse.collections.impl.factory.Maps;
import org.eclipse.collections.impl.factory.Sets;
import org.eclipse.collections.impl.list.primitive.IntInterval;
import org.eclipse.collections.impl.tuple.Tuples;

import com.nmerrill.kothcomm.game.maps.Point2D;

import fellowship.Player;
import fellowship.Range;
import fellowship.abilities.ReadonlyAbility;
import fellowship.abilities.attacking.Critical;
import fellowship.abilities.attacking.Reflexive;
import fellowship.abilities.defensive.Spikes;
import fellowship.abilities.statuses.Immune;
import fellowship.actions.ReadonlyAction;
import fellowship.actions.attacking.Quick;
import fellowship.actions.attacking.Slice;
import fellowship.actions.mobility.Step;
import fellowship.actions.other.Smile;
import fellowship.characters.CharacterInterface;
import fellowship.characters.EnemyCharacter;
import fellowship.characters.ReadonlyCharacter;

public abstract class SleafarPlayer extends Player {
    private static final ImmutableSet<Point2D> MAP_LOCATIONS = IntInterval.fromTo(0, 9)
            .collect(x -> IntInterval.fromTo(0, 9).collect(y -> new Point2D(x, y))).flatCollect(t -> t).toSet()
            .toImmutable();
    protected static final Comparator<CharacterInterface> HEALTH_COMPARATOR = (o1, o2) ->
            Double.compare(o1.getHealth(), o2.getHealth());
    private static final Range BLOCKING_RANGE = new Range(1, true);
    private static final Range STATIC_RANGE = new Range(1);

    protected static boolean hasAbility(CharacterInterface character, Class<?> ability) {
        return character.getAbilities().anySatisfy(a -> a.abilityClass().equals(ability));
    }

    protected static boolean isBear(CharacterInterface character) {
        return character.getAbilities().isEmpty();
    }

    protected static double calcSliceDamage(CharacterInterface character) {
        return character.getStat(character.primaryStat()) * (hasAbility(character, Quick.class) ? 2.0 : 1.0);
    }

    protected static boolean setLocation(ReadonlyAction action, Point2D location) {
        if (location != null) {
            action.setLocation(location);
        }
        return location != null;
    }

    protected static boolean setTarget(ReadonlyAction action, ReadonlyCharacter target) {
        if (target != null) {
            action.setTarget(target);
        }
        return target != null;
    }

    protected abstract class Character {
        protected final ReadonlyCharacter delegate;

        protected Character(ReadonlyCharacter delegate) {
            super();
            this.delegate = delegate;
        }

        protected abstract ReadonlyAction choose();

        protected double getHealth() {
            return delegate.getHealth();
        }

        protected double getHealthRegen() {
            return delegate.getHealthRegen();
        }

        protected double getMana() {
            return delegate.getMana();
        }

        protected double getManaRegen() {
            return delegate.getManaRegen();
        }

        protected Point2D getLocation() {
            return delegate.getLocation();
        }

        protected boolean isVisible() {
            return !delegate.isInvisible();
        }

        protected double getSliceDamage() {
            return delegate.getStat(delegate.primaryStat());
        }

        protected boolean isInEnemySliceRange() {
            return getEnemySliceLocations().contains(delegate.getLocation());
        }

        protected boolean isInEnemySightRange() {
            return getEnemySightLocations().contains(delegate.getLocation());
        }

        protected boolean isInEnemyStepSightRange() {
            return getEnemyStepSightLocations().contains(delegate.getLocation());
        }

        protected double calcSliceRetaliationDamage(CharacterInterface character) {
            double result = 0.0;
            double ownDamage = getSliceDamage();
            for (ReadonlyAbility ability : character.getAbilities()) {
                if (ability.abilityClass().equals(Critical.class)) {
                    ownDamage = ownDamage * 2;
                }
            }
            for (ReadonlyAbility ability : character.getAbilities()) {
                if (ability.abilityClass().equals(Spikes.class)) {
                    result += ownDamage / 2.0;
                } else if (ability.abilityClass().equals(Reflexive.class)) {
                    result += character.getStat(character.primaryStat());
                }
            }
            return result;
        }

        protected double calcSpellRetaliationDamage(CharacterInterface character, double ownDamage) {
            double result = 0.0;
            for (ReadonlyAbility ability : character.getAbilities()) {
                if (ability.abilityClass().equals(Spikes.class)) {
                    result += ownDamage / 2.0;
                }
            }
            return result;
        }

        protected boolean setRandomLocation(ReadonlyAction action) {
            return setLocation(action, chooseRandom(action.availableLocations()));
        }

        protected boolean setRandomLocation(ReadonlyAction action, ImmutableSet<Point2D> avoidLocations) {
            return setLocation(action, chooseRandom(action.availableLocations().difference(avoidLocations)));
        }

        protected boolean setClosestLocation(ReadonlyAction action, ImmutableSet<Point2D> targetLocations) {
            return setLocation(action, chooseClosest(action.availableLocations(), targetLocations));
        }

        protected boolean setClosestLocation(ReadonlyAction action, ImmutableSet<Point2D> avoidLocations,
                ImmutableSet<Point2D> targetLocations) {
            return setLocation(action, chooseClosest(action.availableLocations().difference(avoidLocations),
                    targetLocations));
        }

        protected boolean setClosestHiddenLocation(ReadonlyAction action, ImmutableSet<Point2D> preferredLocations) {
            return setClosestLocation(action, getEnemySightLocations(), preferredLocations);
        }

        protected boolean setClosestSafeLocation(ReadonlyAction action, ImmutableSet<Point2D> preferredLocations) {
            return setClosestLocation(action, getEnemySliceLocations(), preferredLocations);
        }

        protected boolean setFarthestLocation(ReadonlyAction action, ImmutableSet<Point2D> targetLocations) {
            return setLocation(action, chooseFarthest(action.availableLocations(), targetLocations));
        }

        protected boolean setFarthestLocation(ReadonlyAction action, ImmutableSet<Point2D> avoidLocations,
                ImmutableSet<Point2D> targetLocations) {
            return setLocation(action, chooseFarthest(action.availableLocations().difference(avoidLocations),
                    targetLocations));
        }

        public boolean setCloneLocation(ReadonlyAction action, int distance) {
            ImmutableSet<Point2D> cloneLocations = distance < 2 ? team.collect(t -> t.getLocation()).toImmutable() :
                team.flatCollect(t -> t.rangeAround(new Range(distance))).difference(
                team.flatCollect(t -> t.rangeAround(new Range(distance - 1)))).toImmutable();
            if (cloneLocations.isEmpty()) {
                return setRandomLocation(action, getEnemySightLocations()) ||
                        setRandomLocation(action, getEnemySliceLocations()) ||
                        setRandomLocation(action);
            } else {
                return setClosestLocation(action, getEnemySightLocations(), cloneLocations) ||
                        setClosestLocation(action, getEnemySliceLocations(), cloneLocations) ||
                        setClosestLocation(action, cloneLocations);
            }
        }

        protected boolean setAvoidEnemiesLocation(ReadonlyAction action) {
            Point2D location = chooseFarthest(Sets.mutable.ofAll(action.availableLocations())
                    .with(delegate.getLocation()).difference(getEnemySliceLocations()), getEnemyLocations());
            if (location == null || location.equals(delegate.getLocation())) {
                return false;
            } else {
                return setLocation(action, location);
            }
        }

        protected boolean setBlockEnemiesLocation(ReadonlyAction action) {
            return setLocation(action, chooseRandom(action.availableLocations().intersect(getEnemyBlockingLocations())));
        }

        protected boolean setExploreLocation(ReadonlyAction action) {
            return visibleEnemies.size() < enemies.size() && getTeamHiddenLocations().notEmpty() &&
                    setClosestLocation(action, getEnemyStepSightLocations(), getTeamHiddenLocations());
        }

        protected boolean setSliceTarget(ReadonlyAction action, double minHealthReserve) {
            MutableSet<Pair<ReadonlyCharacter, Double>> pairs = action.availableTargets()
                    .collect(t -> Tuples.pair(t, calcSliceRetaliationDamage(t)));
            Pair<ReadonlyCharacter, Double> smallest = chooseSmallest(pairs, (o1, o2) -> {
                int c = Double.compare(o1.getTwo(), o2.getTwo());
                return c == 0 ? Double.compare(o1.getOne().getHealth(), o2.getOne().getHealth()) : c;
            });
            if (smallest == null || smallest.getTwo() > delegate.getHealth() - minHealthReserve) {
                return false;
            } else {
                return setTarget(action, smallest.getOne());
            }
        }

        protected boolean setPoisonTarget(ReadonlyAction action) {
            return setTarget(action, chooseSmallest(action.availableTargets().reject(c -> hasAbility(c, Immune.class)),
                    HEALTH_COMPARATOR));
        }

        protected final ImmutableSet<Point2D> getEnemyLocations() {
            if (enemyLocations == null) {
                enemyLocations = visibleEnemies.keysView().toSet().toImmutable();
            }
            return enemyLocations;
        }

        protected final ImmutableSet<Point2D> getEnemySliceLocations() {
            if (enemySliceLocations == null) {
                enemySliceLocations = visibleEnemies.keyValuesView()
                        .flatCollect(c -> c.getTwo().rangeAround(c.getTwo().getSliceRange(), c.getOne())).toSet()
                        .toImmutable();
            }
            return enemySliceLocations;
        }

        protected final ImmutableSet<Point2D> getEnemySightLocations() {
            if (enemySightLocations == null) {
                enemySightLocations = visibleEnemies.keyValuesView()
                        .flatCollect(c -> c.getTwo().rangeAround(c.getTwo().getSightRange(), c.getOne())).toSet()
                        .toImmutable();
            }
            return enemySightLocations;
        }

        protected final ImmutableSet<Point2D> getEnemyStepSightLocations() {
            if (enemyStepSightLocations == null) {
                enemyStepSightLocations = visibleEnemies.keyValuesView()
                        .flatCollect(c -> Sets.mutable.ofAll(c.getTwo().rangeAround(c.getTwo().getStepRange(), c.getOne()))
                                .with(c.getOne()).flatCollect(r -> c.getTwo().rangeAround(c.getTwo().getSightRange(), r)))
                        .toSet().toImmutable();
            }
            return enemyStepSightLocations;
        }

        protected final ImmutableSet<Point2D> getEnemyHiddenLocations() {
            if (enemyHiddenLocations == null) {
                enemyHiddenLocations = MAP_LOCATIONS.difference(getEnemySightLocations());
            }
            return enemyHiddenLocations;
        }

        protected final ImmutableSet<Point2D> getEnemyBlockingLocations() {
            if (enemyBlockingLocations == null) {
                enemyBlockingLocations = visibleEnemies.keyValuesView()
                        .flatCollect(c -> c.getTwo().rangeAround(BLOCKING_RANGE, c.getOne())).toSet().toImmutable();
            }
            return enemyBlockingLocations;
        }

        protected final ImmutableSet<Point2D> getTeamHiddenLocations() {
            if (teamHiddenLocations == null) {
                teamHiddenLocations = MAP_LOCATIONS.difference(team.flatCollect(c -> c.rangeAround(c.getSightRange())));
            }
            return teamHiddenLocations;
        }

        protected final ImmutableSet<Point2D> getTeamBlockingLocations() {
            if (teamBlockingLocations == null) {
                teamBlockingLocations = team.flatCollect(c -> c.rangeAround(BLOCKING_RANGE)).toImmutable();
            }
            return teamBlockingLocations;
        }

        protected final ImmutableSet<Point2D> getSliceLocations() {
            if (sliceLocations == null) {
                sliceLocations = visibleEnemies.keyValuesView()
                        .flatCollect(c -> c.getTwo().rangeAround(delegate.getSliceRange(), c.getOne())).toSet().toImmutable();
            }
            return sliceLocations;
        }

        protected final ImmutableSet<Point2D> getStaticLocations() {
            if (staticLocations == null) {
                staticLocations = visibleEnemies.keyValuesView()
                        .flatCollect(c -> c.getTwo().rangeAround(STATIC_RANGE, c.getOne())).toSet().toImmutable();
            }
            return staticLocations;
        }

        protected final ImmutableMap<Point2D, Double> getEnemySliceDamage() {
            if (enemySliceDamage == null) {
                MutableMap<Point2D, Double> tmp = MAP_LOCATIONS.toMap(l -> l, l -> 0.0);
                for (Pair<Point2D, EnemyCharacter> p : visibleEnemies.keyValuesView()) {
                    double damage = calcSliceDamage(p.getTwo());
                    for (Point2D l : p.getTwo().rangeAround(p.getTwo().getSliceRange(), p.getOne())) {
                        tmp.put(l, tmp.get(l) + damage);
                    }
                }
                enemySliceDamage = tmp.toImmutable();
            }
            return enemySliceDamage;
        }
    }

    protected ImmutableMap<ReadonlyCharacter, Character> characters = Maps.immutable.empty();

    private ImmutableMap<Class<?>, ReadonlyAction> actions = null;
    protected ReadonlyAction step = null;
    protected ReadonlyAction slice = null;
    protected ReadonlyAction smile = null;

    private ImmutableSet<Point2D> enemyLocations = null;
    private ImmutableSet<Point2D> enemySliceLocations = null;
    private ImmutableSet<Point2D> enemySightLocations = null;
    private ImmutableSet<Point2D> enemyStepSightLocations = null;
    private ImmutableSet<Point2D> enemyHiddenLocations = null;
    private ImmutableSet<Point2D> enemyBlockingLocations = null;
    private ImmutableSet<Point2D> teamHiddenLocations = null;
    private ImmutableSet<Point2D> teamBlockingLocations = null;
    private ImmutableSet<Point2D> sliceLocations = null;
    private ImmutableSet<Point2D> staticLocations = null;
    private ImmutableMap<Point2D, Double> enemySliceDamage = null;

    protected final <T> T chooseRandom(Collection<T> collection) {
        if (!collection.isEmpty()) {
            int i = getRandom().nextInt(collection.size());
            for (T t : collection) {
                if (i == 0) {
                    return t;
                }
                --i;
            }
        }
        return null;
    }

    protected final <T> T chooseSmallest(Collection<T> collection, Comparator<? super T> comparator) {
        if (!collection.isEmpty()) {
            List<T> list = new ArrayList<>();
            for (T t : collection) {
                if (list.isEmpty()) {
                    list.add(t);
                } else {
                    int c = comparator.compare(t, list.get(0));
                    if (c < 0) {
                        list.clear();
                    }
                    if (c <= 0) {
                        list.add(t);
                    }
                }
            }
            return list.get(getRandom().nextInt(list.size()));
        }
        return null;
    }

    protected final Point2D chooseClosest(Collection<Point2D> available, RichIterable<Point2D> targets) {
        if (targets.isEmpty()) {
            return chooseRandom(available);
        } else {
            Map<Point2D, Integer> map = new HashMap<>();
            for (Point2D a : available) {
                map.put(a, targets.collect(t -> t.cartesianDistance(a)).min());
            }
            return chooseSmallest(available, (o1, o2) -> Integer.compare(map.get(o1), map.get(o2)));
        }
    }

    protected final Point2D chooseFarthest(Collection<Point2D> available, RichIterable<Point2D> targets) {
        if (targets.isEmpty()) {
            return chooseRandom(available);
        } else {
            Map<Point2D, Integer> map = new HashMap<>();
            for (Point2D a : available) {
                map.put(a, targets.collect(t -> t.cartesianDistance(a)).min());
            }
            return chooseSmallest(available, (o1, o2) -> Integer.compare(map.get(o2), map.get(o1)));
        }
    }

    protected int countCharacters(Class<?> clazz) {
        return characters.count(c -> c.getClass().equals(clazz));
    }

    protected ReadonlyAction getAction(Class<?> clazz) {
        return actions.get(clazz);
    }

    protected abstract Character createCharacter(ReadonlyCharacter delegate);

    @Override
    public final ReadonlyAction choose(Set<ReadonlyAction> actions, ReadonlyCharacter character) {
        characters = team.collect(c -> characters.getIfAbsentWith(c, this::createCharacter, c))
                .groupByUniqueKey(c -> c.delegate).toImmutable();

        this.actions = Sets.immutable.ofAll(actions).groupByUniqueKey(ReadonlyAction::actionClass);
        step = getAction(Step.class);
        slice = getAction(Slice.class);
        smile = getAction(Smile.class);

        enemyLocations = null;
        enemySliceLocations = null;
        enemySightLocations = null;
        enemyStepSightLocations = null;
        enemyHiddenLocations = null;
        enemyBlockingLocations = null;
        teamHiddenLocations = null;
        teamBlockingLocations = null;
        sliceLocations = null;
        staticLocations = null;
        enemySliceDamage = null;

        return characters.get(character).choose();
    }
}

Чудово зроблено.
Трудна

6

Вампір

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

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

Використовуючи Absorb , Feast , Regenerate , strong з усім, що знаходиться в STR

Vampire.java

import com.nmerrill.kothcomm.game.maps.Point2D;
import fellowship.abilities.ActionAbility;
import fellowship.abilities.attacking.Absorb;
import fellowship.abilities.attacking.Feast;
import fellowship.abilities.stats.Strong;
import fellowship.abilities.stats.Regenerate;
import fellowship.actions.ReadonlyAction;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;
import fellowship.Player;
import org.eclipse.collections.api.set.MutableSet;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class Vampire extends Player{
    private final double CRITICAL_HEALTH = 5;
    @Override
    public List<CharacterTemplate> createCharacters() {
        List<CharacterTemplate> templates = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            templates.add(new CharacterTemplate(30, 0, 0,
                    new Absorb(),
                    new Feast(),
                    new Regenerate(),
                    new Strong()));
        }
        return templates;
    }

    @Override
    public ReadonlyAction choose(Set<ReadonlyAction> actions, ReadonlyCharacter character) {
        int minPriority = Integer.MAX_VALUE;
        ReadonlyAction chosen = null;
        for (ReadonlyAction action: actions){
            int priority = getPriorityFor(action, character);
            if (priority < minPriority){
                chosen = action;
                minPriority = priority;
            }
        }
        if (chosen == null){
            throw new RuntimeException("No valid actions");
        }
        if (chosen.needsLocation()){
            chosen.setLocation(chooseLocationFor(chosen, character));
        } else if (chosen.needsTarget()){
            chosen.setTarget(chooseTargetFor(chosen));
        }
        return chosen;
    }

    private Point2D chooseLocationFor(ReadonlyAction action, ReadonlyCharacter character){
        if (action.movementAction()){
            if (character.getHealth() <= CRITICAL_HEALTH){
                return fromEnemy(action.availableLocations());
            } else {
                return toEnemy(action.availableLocations());
            }
        }
        return toTeam(action.availableLocations());
    }

    private Point2D toEnemy(MutableSet<Point2D> availableLocations){
        if (visibleEnemies.isEmpty()){
            return availableLocations.iterator().next();
        }
        return availableLocations.minBy(p1 ->
                p1.cartesianDistance(visibleEnemies.keysView().minBy(p1::cartesianDistance))
        );
    }

    private Point2D fromEnemy(MutableSet<Point2D> availableLocations){
        if (visibleEnemies.isEmpty()){
            return availableLocations.iterator().next();
        }
        return availableLocations.maxBy(p1 ->
                p1.cartesianDistance(visibleEnemies.keysView().minBy(p1::cartesianDistance))
        );
    }

    private Point2D toTeam(MutableSet<Point2D> availableLocations){
        if (team.isEmpty()){
            return availableLocations.iterator().next();
        }
        return availableLocations.minBy(p1 ->
                p1.cartesianDistance(team.collect(ReadonlyCharacter::getLocation).minBy(p1::cartesianDistance))
        );
    }

    private ReadonlyCharacter chooseTargetFor(ReadonlyAction action){
        return action.availableTargets().minBy(ReadonlyCharacter::getHealth);
    }

    private int getPriorityFor(ReadonlyAction action, ReadonlyCharacter character){
        if (action.getName().equals("Smile")){
            return 1000;
        }
        if (action.movementAction()){
            if (character.getHealth() <= CRITICAL_HEALTH){
                return 0;
            }
            return 999;
        }
        if (action.needsTarget()) {
            return ((int) action.availableTargets().minBy(ReadonlyCharacter::getHealth).getHealth());
        }
        return 998;
    }
}

Мені подобається використання всіх пасивних дій! Хороший.
Moogie

6

Ведмежа кіннота

Використовує поглинання , клонування та ведмедя ; статистика (+9, +0, +11) .

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

Код - безлад, але він, здається, працює. Я скопіював його частини з програвача Template Player.

Ви можете використовувати персонажів цієї команди будь-яким способом, який вам подобається.

BearCavalry.java

import com.nmerrill.kothcomm.game.maps.Point2D;
import fellowship.abilities.ActionAbility;
import fellowship.abilities.attacking.Absorb;
import fellowship.actions.ReadonlyAction;
import fellowship.actions.other.Clone;
import fellowship.actions.other.Bear;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;
import fellowship.Player;
import org.eclipse.collections.api.set.MutableSet;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class BearCavalry extends Player{
    @Override
    public List<CharacterTemplate> createCharacters() {
        List<CharacterTemplate> templates = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            templates.add(new CharacterTemplate(9, 0, 11,
                        new Absorb(),
                        new ActionAbility(Clone::new),
                        new ActionAbility(Bear::new)));
        }
        return templates;
    }

    @Override
    public ReadonlyAction choose(Set<ReadonlyAction> actions, ReadonlyCharacter character) {
    for(ReadonlyAction action: actions){
        if (action.getName().equals("Clone") && action.isAvailable()){
        action.setLocation(toTeam(action.availableLocations(), character));
        return action;
        }
    }
    for(ReadonlyAction action: actions){
        if (action.getName().equals("Bear") && action.isAvailable()){
        action.setLocation(toEnemy(action.availableLocations(), character));
        return action;
        }
    }
    for(ReadonlyAction action: actions){
        if (action.getName().equals("Slice") && action.isAvailable()){
        action.setTarget(action.availableTargets().minBy(ReadonlyCharacter::getHealth));
        return action;
        }
    }
    for(ReadonlyAction action: actions){
        if (action.getName().equals("Step") && action.isAvailable()){
        action.setLocation(toEnemy(action.availableLocations(), character));
        return action;
        }
    }
    for(ReadonlyAction action: actions){
        if (action.getName().equals("Smile")){
        return action;
        }
    }
    return null;
    }

    private Point2D toTeam(MutableSet<Point2D> availableLocations, ReadonlyCharacter character){
        if (team.isEmpty()){
            return availableLocations.minBy(p1 ->
                        p1.diagonalDistance(character.getLocation())
                        );
        }
        return availableLocations.minBy(p1 ->
                    p1.diagonalDistance(team.collect(ReadonlyCharacter::getLocation).minBy(p1::cartesianDistance))
                    );
    }

    private Point2D toEnemy(MutableSet<Point2D> availableLocations, ReadonlyCharacter character){
        if (visibleEnemies.isEmpty()){
            return toTeam(availableLocations, character);
        }
        return availableLocations.minBy(p1 ->
                    p1.diagonalDistance(visibleEnemies.keyValuesView().minBy(p -> p.getTwo().getHealth()).getOne())
                    );
    }
}

ваші ведмеді - це ефективна стратегія проти мого боягузського снайпера :) Занадто багато мішеней для мого бота, щоб потрапити в положення протистояння, щоб почати стрибати! молодці
Moogie

5

Колючий

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

Використовуючи сильний (STR +10) x2, Відновити , Колеси та виконувати повністю STR (+40, 0, 0).

Spiky.java

import fellowship.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;

import com.nmerrill.kothcomm.game.maps.Point2D;

import fellowship.abilities.defensive.Spikes;
import fellowship.abilities.stats.Regenerate;
import fellowship.abilities.stats.Strong;
import fellowship.actions.ReadonlyAction;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;

public class Spiky extends Player {

    @Override
    public List<CharacterTemplate> createCharacters() {
        List<CharacterTemplate> templates = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            templates.add(new CharacterTemplate(40, 0, 0,
                    new Strong(),
                    new Strong(),
                    new Regenerate(),
                    new Spikes()));
        }
        return templates;
    }

    @Override
    public ReadonlyAction choose(Set<ReadonlyAction> actions, ReadonlyCharacter character) {

        ReadonlyAction chosen = null;
        Boolean canSlice = false;
        for (ReadonlyAction action: actions) {
            if (action.getName().equals("Slice")) {
                canSlice = true;
            }
        }

        for (ReadonlyAction action: actions) {
             if (action.getName().equals("Slice")) {
                 chosen = action;
                 chosen.setTarget(action.availableTargets().minBy(ReadonlyCharacter::getHealth));
             }
             if (!canSlice && action.getName().equals("Step")){
                 int x = ThreadLocalRandom.current().nextInt(3, 6 + 1);
                 int y = ThreadLocalRandom.current().nextInt(3, 6 + 1);
                 chosen = action;
                 Point2D destination = null;
                 if (visibleEnemies.isEmpty()){
                     destination = action.availableLocations().minBy(p1 -> p1.cartesianDistance(new Point2D(x, y)));
                 } else {
                     destination = action.availableLocations().minBy(p1 -> p1.cartesianDistance(visibleEnemies.keysView().minBy(p1::cartesianDistance)));
                 }
                 chosen.setLocation(destination);
             }
        }
        if (chosen == null){
            for (ReadonlyAction action: actions){
                if (action.getName().equals("Smile")){
                    chosen = action;
                }
            }
        }

        return chosen;
    }

}

> У вашому поданні не повинно бути декларації про пакет. Ваша заявка повинна міститись у першому багаторядковому кодовому блоці, а перший рядок повинен мати ім'я файлу.
Kritixi Lithos

Я щойно сказав, що було сказано в дописі.
Kritixi Lithos

@KritixiLithos Вгадайте, це єдине, що я зробив правильно. Дякую.
Thrax

Гарний бот! Spiky переміг найкращих моїх ботів.
Kritixi Lithos

Ви можете змінити , ThreadLocalRandom.current()щоб getRandom()? Це дозволяє ігор детерміновано.
Натан Меррілл

5

Чаклун

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

Sorcerer.java

import com.nmerrill.kothcomm.game.maps.Point2D;
import fellowship.Player;
import fellowship.abilities.ActionAbility;
import fellowship.abilities.vision.TrueSight;
import fellowship.actions.ReadonlyAction;
import fellowship.actions.attacking.Slice;
import fellowship.actions.attacking.Weave;
import fellowship.actions.mobility.Step;
import fellowship.actions.other.Clone;
import fellowship.actions.other.Smile;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.collections.api.set.MutableSet;

public class Sorcerer extends Player {

    @Override
    public List<CharacterTemplate> createCharacters() {
        List<CharacterTemplate> templates = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            templates.add(new CharacterTemplate(0, 0, 20,
                    new ActionAbility(Clone::new),
                    new TrueSight(),
                    new ActionAbility(Weave::new)));
        }
        return templates;
    }

    @Override
    public ReadonlyAction choose(Set<ReadonlyAction> actions, ReadonlyCharacter character) {
        ReadonlyAction chosen = getBestAction(actions, character);
        if (chosen == null){
            throw new RuntimeException("No valid actions");
        }
        if (chosen.needsLocation()){
            chosen.setLocation(toEnemy(chosen.availableLocations()));
        } else if (chosen.needsTarget()){
            chosen.setTarget(chooseTargetFor(chosen));
        }
        return chosen;
    }

    private Point2D toEnemy(MutableSet<Point2D> availableLocations){
        if (visibleEnemies.isEmpty()){
            return availableLocations.minBy(p1 ->
                    p1.cartesianDistance(team.minBy(x -> p1.cartesianDistance(x.getLocation())).getLocation())
            );
        }

        return availableLocations.maxBy(p1 ->
                p1.cartesianDistance(visibleEnemies.keysView().maxBy(p1::cartesianDistance))
        );
    }

    private ReadonlyCharacter chooseTargetFor(ReadonlyAction action){
        return action.availableTargets().minBy(ReadonlyCharacter::getHealth);
    }

    private ReadonlyAction getBestAction(Set<ReadonlyAction> actions, ReadonlyCharacter character) {
        Map<Class<?>, ReadonlyAction> actionMap = new HashMap<>();
        for (ReadonlyAction action : actions) {
            actionMap.put(action.actionClass(), action);
        }

        ReadonlyAction clone = actionMap.get(Clone.class);
        if (clone != null && clone.isAvailable() && !clone.availableLocations().isEmpty()) {
            return clone;
        }

        ReadonlyAction weave = actionMap.get(Weave.class);
        if (weave != null && weave.isAvailable() && (clone == null || clone.getRemainingCooldown() > 0)) {
            return weave;
        }

        ReadonlyAction slice = actionMap.get(Slice.class);
        if (slice != null && slice.isAvailable() && !slice.availableLocations().isEmpty() && !character.isInvisible()) {
            return slice;
        }

        ReadonlyAction step = actionMap.get(Step.class);
        if (step != null && step.isAvailable()) {
            return step;
        }

        return actionMap.get(Smile.class);        
    }
}

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

4

LongSword

Використання далекого бою (додає 1 до діапазону Slice), гнучкий (Can - фрагмент в будь-якому з 8 напрямків), Quick (фрагмент двічі, Mana: 3, Відкат: 0), Strong (Ви отримуєте більше 10 очок атрибутів)

ДЕРЖАВИ

Початкові 5 балів - основа

  • STR: 5 + 20 + 10
  • AGI: 5 + 0
  • INT: 5 + 0

Перш за все, мені дуже сподобалось робити цього бота, і мені дуже подобається цей KotH (це моє перше подання на виклик KotH!). (Я можу опублікувати більше ботів)

Бот

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

Для порівняння цього бота з роллю NetHack, я б сказав, що він дуже нагадує Валькірію завдяки концепції "LongSword" та середньому рівню здоров'я.

ІМ’Я

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

ПОВЕДІНКА

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

Якщо персонаж не може бачити ворожий персонаж і має слабке здоров'я, він відступить у напрямку "база" / нерест.

Примітка: Бот ніколи не відступатиме посеред битви. Цей бот ніколи не посміхнеться.

Я використовував такий regex на regexr.com для перетворення свого коду Java у форматизований блок коду.

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

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

LongSword.java

import fellowship.*;
import com.nmerrill.kothcomm.game.maps.Point2D;
import fellowship.abilities.ActionAbility;
import fellowship.abilities.attacking.Flexible;
import fellowship.abilities.attacking.Ranged;
import fellowship.abilities.stats.Strong;
import fellowship.actions.ReadonlyAction;
import fellowship.actions.attacking.Quick;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class LongSword/*Closest NetHack Role: Valkyrie*/ extends Player{

    //debugging
    private boolean debug = false;
    private void println(String text) {
        if(debug)
            System.out.println(text);
    }

    //variables use to hold the start Y coordinate of the bot
    private boolean started = false;
    private int startY = 5;

    @Override
    public List<CharacterTemplate> createCharacters() {
        List<CharacterTemplate> templates = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            templates.add(new CharacterTemplate(30, 0, 0,
                    new Ranged(), //Adds 1 to the range of Slice
                    new Flexible(), //Can Slice in any of the 8 directions
                    new ActionAbility(Quick::new), //Slice twice, Mana: 3, Cooldown: 0
                    new Strong())); //You gain 10 attribute points
        }
        return templates;
    }

    @Override
    public ReadonlyAction choose(Set<ReadonlyAction> actions, ReadonlyCharacter character) {
        if(!started) {
            startY = character.getLocation().getY(); //giving startY the value of the bot's starting y-value
            started = true; //do this only once, that's why there is the if statement
        }

        ReadonlyAction current = null;

        //choosing action depending on priority
        int priority = Integer.MAX_VALUE;
        for(ReadonlyAction action:actions) {
            int priorityLocal = getPriority(action, character);
            if(priorityLocal < priority) {
                current = action;
                priority = priorityLocal;
            }
        }

        if (current == null){
            throw new RuntimeException("No valid actions");
        }

        println(current.getName());

        if(current.needsLocation()) {
            if(visibleEnemies.isEmpty()) {
                if (character.getHealth() < 100) {
                    //if has low health, go backwards towards "base"
                    //println("lowHealth");
                    current.setLocation(move(current, character, "backward"));
                } else {
                    //else go forwards to enemy's "base"
                    current.setLocation(move(current, character, "forward"));
                }
            }else{
                //go towards closest enemy
                current.setLocation(current.availableLocations().minBy(p1->p1.cartesianDistance(visibleEnemies.keysView().minBy(p1::cartesianDistance))));
            }
        }
        if(current.needsTarget()) {
            //get closest target
            current.setTarget(current.availableTargets().minBy(p1 -> 0));
        }

        return current;
    }

    //move backwards or forwards
    private Point2D move(ReadonlyAction readonlyAction, ReadonlyCharacter character, String direction) {
        Point2D location = null;

        //move direction depending on Y coordinate of point
        for(Point2D point2D:readonlyAction.availableLocations()) {
            switch (direction) {
                case "forward":
                    if(startY > 5) { //bot started at bottom
                        if (point2D.getY() < character.getLocation().getY())
                            location = point2D;
                    }else{ //bot started at top
                        if (point2D.getY() > character.getLocation().getY())
                            location = point2D;
                    }
                    break;
                case "backward":
                    if(startY > 5) { //bot started at bottom
                        if (point2D.getY() > character.getLocation().getY())
                            location = point2D;
                    }else{ //bot started at top
                        if (point2D.getY() < character.getLocation().getY())
                            location = point2D;
                    }
                    break;
            }

        }

        //if no available locations, just choose the first available location
        if(location == null) {
            location = readonlyAction.availableLocations().iterator().next();
        }

        println(location.getY()+","+character.getLocation().getY());

        return location;
    }

    private int getPriority(ReadonlyAction action, ReadonlyCharacter character) {
        if(visibleEnemies.isEmpty()) {
            //if there are no visible enemies, Step. In the choose function, this becomes move forward or backward depending on health
            if(action.getName().equals("Step")) {
                return 100;
            }
        }else {
            /*
             * PRIORITIES:
             *  1. Quick (Slice twice)
             *  2. Slice
             *  3. Step (when enemy is not in range --> move towards enemy)
             */
            if (action.getName().equals("Quick")) {
                return 1;
            }else if(action.getName().equals("Slice")) {
                return 10;
            }else if(action.getName().equals("Step")) {
                return 50;
            }
        }
        //Kids, don't Smile, instead Step or Slice
        return 1000;
    }
}

2
соромить моїх трусів. хороша робота
Moogie

4

Дирейлер

Довелося її двічі видалити, оскільки у мене була купа логічних помилок. : P

Це, безумовно, може зірвати ваші плани. ;)

Команда:

  • 1 персонаж із Критичним , Бафф , Сильним та Швидким, щоб швидко вивести ворогів, переможивши їх дуже важко. +25 STR, +2 AGI, +3 INT
  • 1 персонаж за допомогою Clever , Clever , Restore та Zap . Залишається позаду, як підтримка і відновлює здоров’я будь-яких товаришів по команді, які втрачають низький рівень HP, і можуть атакувати та захищати себе необхідними. +14 STR, +3 AGI, +3 INT
  • 1 персонаж із TrueSight , Spikes , Evasive та Weave . Ударити не так просто, і якщо ви зробите це, або якщо ви занадто близько наблизитеся, він побачить вас і вдарить. +13 STR, +3 AGI, +4 INT
Derailer.java

import com.nmerrill.kothcomm.game.maps.Point2D;
import fellowship.Player;
import fellowship.abilities.ActionAbility;
import fellowship.abilities.ReadonlyAbility;
import fellowship.abilities.attacking.Critical;
import fellowship.abilities.defensive.Evasive;
import fellowship.abilities.defensive.Spikes;
import fellowship.abilities.stats.Buff;
import fellowship.abilities.stats.Clever;
import fellowship.abilities.stats.Strong;
import fellowship.abilities.vision.TrueSight;
import fellowship.actions.ReadonlyAction;
import fellowship.actions.attacking.Quick;
import fellowship.actions.attacking.Weave;
import fellowship.actions.damage.Zap;
import fellowship.actions.defensive.Restore;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;

public class Derailer extends Player {
    private static final double CRITICAL_HEALTH_PCT = .175;

    @Override
    public List<CharacterTemplate> createCharacters() {
        List<CharacterTemplate> list = new ArrayList<>();

        list.add(new CharacterTemplate(14, 3, 3,
                                       new Clever(),
                                       new Clever(),
                                       new ActionAbility(Restore::new),
                                       new ActionAbility(Zap::new)));

        list.add(new CharacterTemplate(25, 2, 3,
                                       new Critical(),
                                       new Buff(),
                                       new ActionAbility(Quick::new),
                                       new Strong()));

        list.add(new CharacterTemplate(13, 3, 4,
                                       new TrueSight(),
                                       new Spikes(),
                                       new Evasive(),
                                       new ActionAbility(Weave::new)));
        return list;
    }

    @Override
    public ReadonlyAction choose(Set<ReadonlyAction> actions, ReadonlyCharacter character) {
        List<ReadonlyAbility> abilities = character.getAbilities();
        ReadonlyAction action = null;

        for (ReadonlyAbility a : abilities) {
            String s = a.name();
            int i = s.lastIndexOf(".");
            if (i == -1)
                continue;
            s = s.substring(i+1, s.length());
            if (s.equals("Clever")) {
                action = getActionForChar1(character, actions);
                break;
            }
            else if (s.equals("Buff")) {
                action = getActionForChar2(character, actions);
                break;
            }
            else if (s.equals("Evasive")) {
                action = getActionForChar3(character, actions);
                break;
            }
        }

        return action;
    }

    private ReadonlyAction getActionForChar1(ReadonlyCharacter character, Set<ReadonlyAction> actions) {
        int members = (int) team.stream().filter(c -> !c.isDead()).count();

        List<ReadonlyAction> list = actions.stream()
                                           .sorted(Comparator.comparingInt(this::getPriority))
                                           .collect(Collectors.toList());

        for (ReadonlyAction a : list) {
            String name = a.getName();
            if (name.equals("Restore")) {
                for (ReadonlyCharacter teammate : team) {
                    if (teammate.getHealth() / teammate.getMaxHealth() < CRITICAL_HEALTH_PCT * (4 - members))
                        return a;
                }
            }
            else if (name.equals("Zap") && !a.availableTargets().isEmpty()) {
                a.setTarget(a.availableTargets()
                             .stream()
                             .reduce(
                                 BinaryOperator.minBy(
                                     Comparator.<ReadonlyCharacter>comparingDouble(e -> e.getHealth())
                                 )
                             )
                             .get()
                );
                return a;
            }
            else if (name.equals("Slice") && !a.availableTargets().isEmpty()) {
                a.setTarget(a.availableTargets().iterator().next());
                return a;
            }
            else if (name.equals("Smile"))
                return a;
        }
        throw new RuntimeException("No available actions");
    }

    private ReadonlyAction getActionForChar2(ReadonlyCharacter character, Set<ReadonlyAction> actions) {
        List<ReadonlyAction> list = actions.stream()
                                           .sorted(Comparator.comparingInt(this::getPriority))
                                           .collect(Collectors.toList());

        for (ReadonlyAction a : list) {
            String name = a.getName();
            if (name.equals("Quick") && !a.availableTargets().isEmpty()) {
                a.setTarget(a.availableTargets().minBy(ReadonlyCharacter::getHealth));
                return a;
            }
            else if (name.equals("Slice") && !a.availableTargets().isEmpty()) {
                a.setTarget(a.availableTargets().minBy(ReadonlyCharacter::getHealth));
                return a;
            }
            else if (name.equals("Step") && !a.availableLocations().isEmpty()) {
                Point2D e = getClosestEnemyPoint(character);
                if (e == null) {
                    Point2D p = character.getLocation();
                    if (p.getY() > 5) {
                        a.setLocation(a.availableLocations()
                                       .stream()
                                       .filter(x -> x.getY() < p.getY())
                                       .findFirst()
                                       .orElse(a.availableLocations().iterator().next()));
                    }
                    else if (p.getY() < 4) {
                        a.setLocation(a.availableLocations()
                                       .stream()
                                       .filter(x -> x.getY() > p.getY())
                                       .findFirst()
                                       .orElse(a.availableLocations().iterator().next()));
                    }
                    else
                        a.setLocation(randomLocation(new ArrayList<>(a.availableLocations())));
                }
                else {
                    int currentDistance = character.getLocation().cartesianDistance(e);
                    a.setLocation(a.availableLocations()
                                   .stream()
                                   .filter(x -> x.cartesianDistance(e) < currentDistance)
                                   .findFirst()
                                   .orElse(randomLocation(new ArrayList<>(a.availableLocations()))));
                }
                return a;
            }
            else if (name.equals("Smile"))
                return a;
        }
        throw new RuntimeException("No available actions");
    }

    private ReadonlyAction getActionForChar3(ReadonlyCharacter character, Set<ReadonlyAction> actions) {
        List<ReadonlyAction> list = actions.stream()
                                           .sorted(Comparator.comparingInt(this::getPriority))
                                           .collect(Collectors.toList());

        for (ReadonlyAction a : list) {
            String name = a.getName();
            if (name.equals("Weave") && visibleEnemies.keySet().size() > 1)
                return a;
            else if (name.equals("Slice") && !a.availableTargets().isEmpty()) {
                a.setTarget(a.availableTargets().iterator().next());
                return a;
            }
            else if (name.equals("Smile"))
                return a;
            else if (name.equals("Step")) {
                Point2D p = character.getLocation();
                if (!visibleEnemies.keySet().isEmpty()) {
                    Point2D e = getClosestEnemyPoint(character);
                    int currentDistance = character.getLocation().cartesianDistance(e);
                    a.setLocation(a.availableLocations()
                                   .stream()
                                   .filter(x -> x.cartesianDistance(e) < currentDistance)
                                   .findAny()
                                   .orElse(randomLocation(new ArrayList<>(a.availableLocations()))));
                }
                else if (p.getY() > 5) {
                    a.setLocation(a.availableLocations()
                                   .stream()
                                   .filter(x -> x.getY() < p.getY())
                                   .findFirst()
                                   .orElse(randomLocation(new ArrayList<>(a.availableLocations()))));
                }
                else if (p.getY() < 4) {
                    a.setLocation(a.availableLocations()
                                   .stream()
                                   .filter(x -> x.getY() > p.getY())
                                   .findFirst()
                                   .orElse(randomLocation(new ArrayList<>(a.availableLocations()))));
                }
                else
                    a.setLocation(randomLocation(new ArrayList<>(a.availableLocations())));
                return a;
            }
        }
        throw new RuntimeException("No available actions");
    }

    private Point2D getClosestEnemyPoint(ReadonlyCharacter c) {
        return visibleEnemies.keySet()
                             .stream()
                             .reduce(
                                 BinaryOperator.minBy(
                                     Comparator.comparingInt(x -> x.cartesianDistance(c.getLocation()))
                                 )
                             )
                             .orElse(null);
    }

    private int getPriority(ReadonlyAction action) {
        switch (action.getName()) {
            case "Quick":
            case "Restore":
            case "Weave":
                return 1;
            case "Zap": return 2;
            case "Slice": return 3;
            case "Step": return 4;
            case "Smile": return 5;
        }
        throw new IllegalArgumentException(String.valueOf(action));
    }

    private Point2D randomLocation(List<Point2D> l) {
        return l.get((int) (Math.random() * l.size()));
    }
}

Цікава динаміка команди! змушує мене спробувати деякі більш просунуті дії команди :)
Moogie

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

Цікаво, що заміна третього символу на копію першого призвела до набагато кращих результатів.
ТНТ

4

SniperSquad

Снайперський загін складається з:

  • 1 споттер (оснащений найкращим доступним спостереженням, що дозволяє оглядати майже всю карту)
    • STR: 25; AGI: 5; INT: 5
    • Далекий вигляд , Далекий вигляд , Далекий вигляд , Далекий вигляд
  • 2 стрільці (оснащені новітніми снайперськими гвинтівками, єдиний недолік - повільна швидкість вогню)
    • STR: 25; AGI: 5; INT: 5
    • Ткати , критично , критично , критично

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

SniperSquad.java
import java.util.Arrays;
import java.util.List;

import fellowship.abilities.ActionAbility;
import fellowship.abilities.attacking.Critical;
import fellowship.abilities.vision.FarSight;
import fellowship.actions.ReadonlyAction;
import fellowship.actions.attacking.Weave;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;

public class SniperSquad extends SleafarPlayer {
    private static CharacterTemplate spotterTemplate() {
        return new CharacterTemplate(20, 0, 0,
                new FarSight(), new FarSight(), new FarSight(), new FarSight());
    }

    private static CharacterTemplate shooterTemplate() {
        return new CharacterTemplate(20, 0, 0,
                new ActionAbility(Weave::new), new Critical(), new Critical(), new Critical());
    }

    @Override
    public List<CharacterTemplate> createCharacters() {
        return Arrays.asList(shooterTemplate(), spotterTemplate(), shooterTemplate());
    }

    private class Spotter extends Character {
        protected Spotter(ReadonlyCharacter delegate) {
            super(delegate);
        }

        @Override
        protected ReadonlyAction choose() {
            if (slice != null && setSliceTarget(slice, 100.0)) {
                return slice;
            }
            if (step != null && isInEnemyStepSightRange() && setAvoidEnemiesLocation(step)) {
                return step;
            }
            if (slice != null && setSliceTarget(slice, 0.01)) {
                return slice;
            }
            if (step != null && setExploreLocation(step)) {
                return step;
            }
            return smile;
        }
    }

    private class Shooter extends Character {
        protected Shooter(ReadonlyCharacter delegate) {
            super(delegate);
        }

        @Override
        protected ReadonlyAction choose() {
            ReadonlyAction weave = getAction(Weave.class);
            if (weave != null && !visibleEnemies.isEmpty() &&
                    visibleEnemies.collectDouble(e -> calcSliceRetaliationDamage(e)).sum() < getHealth()) {
                return weave;
            }
            if (slice != null && setSliceTarget(slice, 100.0)) {
                return slice;
            }
            if (step != null && setAvoidEnemiesLocation(step)) {
                return step;
            }
            if (slice != null && setSliceTarget(slice, 0.01)) {
                return slice;
            }
            return smile;
        }
    }

    @Override
    protected Character createCharacter(ReadonlyCharacter delegate) {
        if (hasAbility(delegate, FarSight.class)) {
            return new Spotter(delegate);
        } else if (hasAbility(delegate, Weave.class)) {
            return new Shooter(delegate);
        } else {
            throw new IllegalArgumentException();
        }
    }
}

Хе-хе, ваші снайпери
вистрілюють моїх боягузських

3

Перевертні

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

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

Важко вибрати найбільш ідеальні значення, оскільки навіть ніякі зміни іноді можуть призвести до переходу з "найкращого" на "найгіршого". Поріг відхилення здоров’я тут становить 50, але, схоже, будь-яке значення між 10 і 70 призводить до подібних результатів (жодних інших ботів, які забезпечують достатньо високу проблему, щоб відрізнити точний пік результативності).

PlayerWerewolf.java

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import com.nmerrill.kothcomm.game.maps.Point2D;

import fellowship.Player;
import fellowship.abilities.ActionAbility;
import fellowship.abilities.attacking.Ranged;
import fellowship.abilities.attacking.Swipe;
import fellowship.abilities.stats.Strong;
import fellowship.actions.ReadonlyAction;
import fellowship.actions.stats.Werewolf;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;
import fellowship.characters.EnemyCharacter;

public class PlayerWerewolf extends Player {
    //variables use to hold the start Y coordinate of the bot
    private boolean started = false;
    private int startY = 5;

    @Override
    public List<CharacterTemplate> createCharacters() {
        List<CharacterTemplate> templates = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            templates.add(new CharacterTemplate(30, 0, 0,
                    new Ranged(), //Adds 1 to the range of Slice
                    new Swipe(), //Deal increasing damage
                    new ActionAbility(Werewolf::new), //Turn into a werewolf for 5 turns
                    new Strong())); //You gain 10 attribute points
        }
        return templates;
    }

    @Override
    public ReadonlyAction choose(Set<ReadonlyAction> actions, ReadonlyCharacter character) {
        if(!started) {
            startY = character.getLocation().getY(); //giving startY the value of the bot's starting y-value
            started = true; //do this only once, that's why there is the if statement
        }

        ReadonlyAction current = null;

        //choosing action depending on priority
        int priority = Integer.MAX_VALUE;
        for(ReadonlyAction action:actions) {
            int priorityLocal = getPriority(action, character);
            if(priorityLocal < priority) {
                current = action;
                priority = priorityLocal;
            }
        }

        if (current == null){
            throw new RuntimeException("No valid actions");
        }

        if(current.needsLocation()) {
            if(visibleEnemies.isEmpty()) {
                if (character.getHealth() < 50) {
                    //if has low health, go backwards towards "base"
                    //println("lowHealth");
                    current.setLocation(move(current, character, "backward"));
                } else {
                    //else go forwards to enemy's "base"
                    current.setLocation(move(current, character, "forward"));
                }
            }else{
                //go towards closest enemy
                current.setLocation(current.availableLocations().minBy(p1->p1.cartesianDistance(visibleEnemies.keysView().minBy(p1::cartesianDistance))));
            }
        }
        if(current.needsTarget()) {
            //get closest target
            current.setTarget(current.availableTargets().minBy(p1 -> 0));
        }

        return current;
    }

    //move backwards or forwards
    private Point2D move(ReadonlyAction readonlyAction, ReadonlyCharacter character, String direction) {
        Point2D location = null;

        //move direction depending on Y coordinate of point
        for(Point2D point2D:readonlyAction.availableLocations()) {
            switch (direction) {
            case "forward":
                if(startY > 5) { //bot started at bottom
                    if (point2D.getY() < character.getLocation().getY())
                        location = point2D;
                }else{ //bot started at top
                    if (point2D.getY() > character.getLocation().getY())
                        location = point2D;
                }
                break;
            case "backward":
                if(startY > 5) { //bot started at bottom
                    if (point2D.getY() > character.getLocation().getY())
                        location = point2D;
                }else{ //bot started at top
                    if (point2D.getY() < character.getLocation().getY())
                        location = point2D;
                }
                break;
            }

        }

        //if no available locations, just choose the first available location
        if(location == null) {
            location = readonlyAction.availableLocations().iterator().next();
        }

        return location;
    }

    private int getPriority(ReadonlyAction action, ReadonlyCharacter character) {
        if(visibleEnemies.isEmpty()) {
            //if there are no visible enemies, Step. In the choose function, this becomes move forward or backward depending on health
            if(action.getName().equals("Step")) {
                return 100;
            }
        }else {
            /*
             * PRIORITIES:
             *  1. If near an enemy, and not a werewolf, turn into a werewolf
             *  2. Slice
             *  3. Step (when enemy is not in range --> move towards enemy)
             */
            if (action.getName().equals("Werewolf") && action.isAvailable()) {
                EnemyWrapper wrap = getNearestEnemy(character);
                //don't turn into a werewolf unless we're close to an enemy
                if(wrap.location.diagonalDistance(character.getLocation()) < 3) {
                    return 1;
                }
            }else if(action.getName().equals("Slice")) {
                return 10;
            }else if(action.getName().equals("Step")) {
                return 50;
            }
        }
        //Kids, don't Smile, instead Step or Slice
        return 1000;
    }

    private EnemyWrapper getNearestEnemy(ReadonlyCharacter character) {
        double closestEnemyDistance = Double.MAX_VALUE;
        Point2D closestEnemy = null;
        for ( Point2D enemyLocation : visibleEnemies.keySet()) {
            double visionDistanceDiff = character.getLocation().diagonalDistance(enemyLocation);
            if (visionDistanceDiff< closestEnemyDistance)
            {
                closestEnemyDistance = visionDistanceDiff;
                closestEnemy = enemyLocation;
            }
        }
        return new EnemyWrapper(visibleEnemies.get(closestEnemy), closestEnemy);
    }
    private static class EnemyWrapper {
        public final EnemyCharacter enemy;
        public final Point2D location;

        EnemyWrapper(EnemyCharacter e, Point2D l) {
            enemy = e;
            location = l;
        }
    }
}

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

Я зрозумів це: вам не вистачало імпорту:import fellowship.characters.EnemyCharacter;
Натан Меррілл

@NathanMerrill Я намагався поєднати середній клас у внутрішній клас поза Eclipse, це, мабуть, це було.
Draco18s

Приємно! Ви використовували мої власні функції руху від LongSword!
Kritixi Lithos

@KritixiLithos Так, у мене виникли проблеми з написанням "ай" частини речей, тому я зафіксував ту, що було спрощеною просто для того, щоб мати вихідну точку, і вона працювала дуже добре. Я намагався поспілкуватися з ними, щоб побачити, чи зможу я зробити це краще, оскільки вони просто продовжуватимуть крокувати вперед, якщо вони нікого не бачать, навіть якщо їхній опонент позаду них, але не зробив багато різниця. Переважно тому, що ані вовки, ані Довгі Мечники не можуть протидіяти невидимості.
Draco18s

2

Railbender

Цей бот - це просто версія Derailer , у якої третій символ замінили на копію першого. Це дає набагато кращі результати порівняно з Derailer.

Створюючи Derailer, я хотів надати кожному символу здібності, які б добре синергували один з одним. Маючи одного персонажа з високою потужністю HP та атаки та іншого персонажа з дією Restore, добре працювали разом. Однак, здалося, що третій персонаж не дуже добре вписується. Це, мабуть, було основною причиною того, що Derailer не дав хороших результатів. Тож я подумав, що краще мати ідею третього персонажа, який може добре працювати з іншими.

Railbender.java

import com.nmerrill.kothcomm.game.maps.Point2D;
import fellowship.Player;
import fellowship.abilities.ActionAbility;
import fellowship.abilities.ReadonlyAbility;
import fellowship.abilities.attacking.Critical;
import fellowship.abilities.stats.Buff;
import fellowship.abilities.stats.Clever;
import fellowship.abilities.stats.Strong;
import fellowship.actions.ReadonlyAction;
import fellowship.actions.attacking.Quick;
import fellowship.actions.damage.Zap;
import fellowship.actions.defensive.Restore;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;

public class Railbender extends Player {
    private static final double CRITICAL_HEALTH_PCT = .175;

    @Override
    public List<CharacterTemplate> createCharacters() {
        List<CharacterTemplate> list = new ArrayList<>();

        list.add(new CharacterTemplate(14, 3, 3,
                                       new Clever(),
                                       new Clever(),
                                       new ActionAbility(Restore::new),
                                       new ActionAbility(Zap::new)));

        for (int k = 0; k < 2; k++) {
            list.add(new CharacterTemplate(25, 2, 3,
                                           new Critical(),
                                           new Buff(),
                                           new ActionAbility(Quick::new),
                                           new Strong()));
        }
        return list;
    }

    @Override
    public ReadonlyAction choose(Set<ReadonlyAction> actions, ReadonlyCharacter character) {
        List<ReadonlyAbility> abilities = character.getAbilities();
        ReadonlyAction action = null;

        for (ReadonlyAbility a : abilities) {
            String s = a.name();
            int i = s.lastIndexOf(".");
            if (i == -1)
                continue;
            s = s.substring(i+1, s.length());
            if (s.equals("Clever")) {
                action = getActionForChar1(character, actions);
                break;
            }
            else if (s.equals("Buff")) {
                action = getActionForChar2(character, actions);
                break;
            }
        }

        return action;
    }

    private ReadonlyAction getActionForChar1(ReadonlyCharacter character, Set<ReadonlyAction> actions) {
        int members = (int) team.stream().filter(c -> !c.isDead()).count();

        List<ReadonlyAction> list = actions.stream()
                                           .sorted(Comparator.comparingInt(this::getPriority))
                                           .collect(Collectors.toList());

        Point2D closestEnemy = getClosestEnemyPoint(character);

        for (ReadonlyAction a : list) {
            String name = a.getName();
            if (name.equals("Restore")) {
                for (ReadonlyCharacter teammate : team) {
                    if (teammate.getHealth() / teammate.getMaxHealth() < CRITICAL_HEALTH_PCT * (4 - members))
                        return a;
                }
            }
            else if (name.equals("Zap") && !a.availableTargets().isEmpty() && closestEnemy != null &&
                     character.getLocation().cartesianDistance(closestEnemy) <= 4) {
                a.setTarget(a.availableTargets()
                             .stream()
                             .reduce(
                                 BinaryOperator.minBy(
                                     Comparator.<ReadonlyCharacter>comparingDouble(e -> e.getHealth())
                                 )
                             )
                             .get()
                );
                return a;
            }
            else if (name.equals("Slice") && !a.availableTargets().isEmpty()) {
                a.setTarget(a.availableTargets().iterator().next());
                return a;
            }
            else if (name.equals("Smile"))
                return a;
        }
        throw new RuntimeException("No available actions");
    }

    private ReadonlyAction getActionForChar2(ReadonlyCharacter character, Set<ReadonlyAction> actions) {
        List<ReadonlyAction> list = actions.stream()
                                           .sorted(Comparator.comparingInt(this::getPriority))
                                           .collect(Collectors.toList());

        for (ReadonlyAction a : list) {
            String name = a.getName();
            if (name.equals("Quick") && !a.availableTargets().isEmpty()) {
                a.setTarget(a.availableTargets().minBy(ReadonlyCharacter::getHealth));
                return a;
            }
            else if (name.equals("Slice") && !a.availableTargets().isEmpty()) {
                a.setTarget(a.availableTargets().minBy(ReadonlyCharacter::getHealth));
                return a;
            }
            else if (name.equals("Step") && !a.availableLocations().isEmpty()) {
                Point2D e = getClosestEnemyPoint(character);
                if (e == null) {
                    Point2D p = character.getLocation();
                    if (p.getY() > 5) {
                        a.setLocation(a.availableLocations()
                                       .stream()
                                       .filter(x -> x.getY() < p.getY())
                                       .findFirst()
                                       .orElse(a.availableLocations().iterator().next()));
                    }
                    else if (p.getY() < 4) {
                        a.setLocation(a.availableLocations()
                                       .stream()
                                       .filter(x -> x.getY() > p.getY())
                                       .findFirst()
                                       .orElse(a.availableLocations().iterator().next()));
                    }
                    else
                        a.setLocation(randomLocation(new ArrayList<>(a.availableLocations())));
                }
                else {
                    int currentDistance = character.getLocation().cartesianDistance(e);
                    a.setLocation(a.availableLocations()
                                   .stream()
                                   .filter(x -> x.cartesianDistance(e) < currentDistance)
                                   .findFirst()
                                   .orElse(randomLocation(new ArrayList<>(a.availableLocations()))));
                }
                return a;
            }
            else if (name.equals("Smile"))
                return a;
        }
        throw new RuntimeException("No available actions");
    }

    private Point2D getClosestEnemyPoint(ReadonlyCharacter c) {
        return visibleEnemies.keySet()
                             .stream()
                             .reduce(
                                 BinaryOperator.minBy(
                                     Comparator.comparingInt(x -> x.cartesianDistance(c.getLocation()))
                                 )
                             )
                             .orElse(null);
    }

    private int getPriority(ReadonlyAction action) {
        switch (action.getName()) {
            case "Quick":
            case "Restore":
                return 1;
            case "Zap": return 2;
            case "Slice": return 3;
            case "Step": return 4;
            case "Smile": return 5;
        }
        throw new IllegalArgumentException(String.valueOf(action));
    }

    private Point2D randomLocation(List<Point2D> l) {
        return l.get((int) (Math.random() * l.size()));
    }
}

Дивовижний! Це набагато важче, ніж Derailer
Kritixi Lithos

2

Noob/*Destroyer*/

Використовує сильний * 2, відновлює і оглушує (мета приголомшує в протягом наступних 300 тиків)

ДЕРЖАВИ

  • STR : 5 + 40
  • AGI : 5 + 0
  • INT : 5 + 0

AI

Більшість кодів Нооба взято з мого LongSword.

Стратегія

Коли персонаж вперше бачить ворожий персонаж, пріоритет надається спочатку враження ворога, а потім нарізання ворога, поки вони оглушені. А завдяки своєму високому здоров’ю та відновленню, Нооб повинен мати можливість вижити, поки не зможе знову використовувати Stun.

Noob.java
import fellowship.*;
import com.nmerrill.kothcomm.game.maps.Point2D;
import fellowship.Stat;
import fellowship.abilities.ActionAbility;
import fellowship.abilities.stats.Regenerate;
import fellowship.abilities.stats.Strong;
import fellowship.actions.ReadonlyAction;
import fellowship.actions.defensive.Shield;
import fellowship.actions.statuses.Silence;
import fellowship.actions.statuses.Stun;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;
import fellowship.Player;
import org.eclipse.collections.api.set.MutableSet;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class Noob/*Destroyer*/ extends Player {

    private boolean debug = false;
    private void println(String text) {
        if(debug)
            System.out.println(text);
    }

    private boolean started = false;
    private int startY = 5;

    @Override
    public List<CharacterTemplate> createCharacters() {
        List<CharacterTemplate> templates = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            templates.add(new CharacterTemplate(40, 0, 0,
                    new Regenerate(),
                    new ActionAbility(Stun::new),
                    new Strong(),
                    new Strong()));
        }
        return templates;
    }

    @Override
    public ReadonlyAction choose(Set<ReadonlyAction> actions, ReadonlyCharacter character) {
        if(!started) {
            startY = character.getLocation().getY();
            started = true;
        }

        ReadonlyAction readonlyAction = null;

        //get priority of action
        int priority = Integer.MAX_VALUE;

        for(ReadonlyAction action:actions) {
            int priorityLocal = getPriority(action, character);
            if(priorityLocal < priority) {
                readonlyAction = action;
                priority = priorityLocal;
            }
        }

        if (readonlyAction == null){
            println("NULL!");
            throw new RuntimeException("No valid actions");
        }

        //movement
        if(readonlyAction.needsLocation()) {
            if(visibleEnemies.isEmpty()) {
                if (character.getHealth() < 100) {
                    readonlyAction.setLocation(move(readonlyAction, character, "backward"));
                } else {
                    readonlyAction.setLocation(move(readonlyAction, character, "forward")); //enemy base is "forward"
                }
            }else{
                readonlyAction.setLocation(readonlyAction.availableLocations().minBy(p1->p1.cartesianDistance(visibleEnemies.keysView().minBy(p1::cartesianDistance))));
            }
        }

        if(readonlyAction.needsTarget()) {
            readonlyAction.setTarget(readonlyAction.availableTargets().minBy(p1 -> 0));
        }

        return readonlyAction;
    }

    private Point2D move(ReadonlyAction readonlyAction, ReadonlyCharacter character, String direction) {
        Point2D location = null;

        for(Point2D point2D:readonlyAction.availableLocations()) {
            switch (direction) {
                case "forward":
                    if(startY > 5) { //bot starts at bottom
                        if (point2D.getY() < character.getLocation().getY())
                            location = point2D;
                    }else{ //bot starts at top
                        if (point2D.getY() > character.getLocation().getY())
                            location = point2D;
                    }
                    break;
                case "backward":
                    if(startY > 5) { //bot starts at bottom
                        if (point2D.getY() > character.getLocation().getY())
                            location = point2D;
                    }else{ //bot starts at top
                        if (point2D.getY() < character.getLocation().getY())
                            location = point2D;
                    }
                    break;
            }

        }

        if(location == null) {
            location = readonlyAction.availableLocations().iterator().next();
        }
        return location;
    }

    private int getPriority(ReadonlyAction action, ReadonlyCharacter character) {
        if(visibleEnemies.isEmpty()) {
            if(action.getName().equals("Step")) {
                return 100;
            }
        }else {
            if (action.getName().equals("Slice")) {
                return 10;
            }else if(action.getName().equals("Step")) {
                return 50;
            }else if(action.getName().equals("Stun") && !action.availableTargets().minBy(p1->0).isStunned()) {
                //if target is not stunned, stun 'em
                return 1;
            }
        }
        return 1000;
    }
}

2

Жива стіна

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

  • 2 Гілки : STR 35, AGI 5, INT 5, Сильний , Buff , Buff , Absorb
  • 1 корінь : STR 25, AGI , 5, INT , 5, True Sight , Buff , Buff , Absorb

ШІ неймовірно простий: знайти ворога найближчим до команди, тоді вся стіна зосереджена на тому єдиному ворозі. Є лише незначні ускладнення: якщо жодних ворогів не видно, пройдіться до випадкових куточків та / або до центру карти (таким чином, врешті-решт, висаджуйте ворогів, які ховаються); якщо ворог знаходиться в межах досяжності, атакуйте його, навіть якщо це не той ворог, на який ми націлюємось (але віддайте перевагу зосередженню на ворога, на якого ми націлюємось, і тим більше ворогів ми можемо ОКО).

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

Основна причина успіху команди пов’язана з комбо Buff × 2 та Absorb; це означає, що кожного разу, коли ми вражаємо ворога-первинного STR, ми ефективно набираємо 40 к.с. за короткий термін (лише 10 к.с. в довгостроковій перспективі через посилене відродження вкраденого СТР, але до цього часу боротьба повинна закінчитися і наше природне відновлення повинно нас відливати), а враховуючи природний коефіцієнт регенерації 12,5 або 17,5, крім цього, в основному неможливо зробити пошкодження досить швидко, щоб не відставати від регенерації (команда AGI потенційно може це зробити, використовуючи хіт- і запускайте тактику, але ще ніхто з них не побудував). { Оновлення : Мабуть, цей комбо насправді не працює (поглинає лише 10 л.с.), але команда все одно перемагає.} Тим часом, якщо ворог не будеSTR-первинні, їм не сподобається робити повторні удари по 25 або 35 з пошкодженнями (і насправді вони цілком можуть бути сфокусовані вниз за один із своїх витків); і якщо ворог INT-первинний і використовує заклинання, щоб захистити себе (привіт невразливі!), Absorb врешті-решт зведе свого МП до того моменту, коли вони вже не можуть дозволити собі кидати заклинання. (Крім того, у нас в основному немає чого боятися від більшості заклинань; їхні пошкодження занадто довгі, щоб вони могли попередити наше відродження. Основні винятки - «Пастка», яку ще ніхто не працює, і «Отрута», яка потребує віку, щоб зноситися через 1000 або 1400 HP, але це спрацьовує, якщо Стіна спочатку не переможе монстра.) True Sight все ще є єдиною здатністю, практично здатною перемогти невидимих ​​ворогів (Track doesn '

LivingWall.java
import com.nmerrill.kothcomm.game.maps.Point2D;
import fellowship.abilities.*;
import fellowship.abilities.attacking.*;
import fellowship.abilities.defensive.*;
import fellowship.abilities.vision.*;
import fellowship.abilities.stats.*;
import fellowship.abilities.statuses.*;
import fellowship.actions.*;
import fellowship.actions.attacking.*;
import fellowship.actions.damage.*;
import fellowship.actions.defensive.*;
import fellowship.actions.statuses.*;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;
import fellowship.characters.EnemyCharacter;
import fellowship.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class LivingWall extends Player {
  @Override
  public List<CharacterTemplate> createCharacters() {
    List<CharacterTemplate> templates = new ArrayList<>();

    for (int i = 0; i < 2; i++)
      templates.add(new CharacterTemplate(30, 0, 0,
                                          new Absorb(),
                                          new Strong(),
                                          new Buff(),
                                          new Buff()));
    templates.add(new CharacterTemplate(20, 0, 0,
                                        new Absorb(),
                                        new TrueSight(),
                                        new Buff(),
                                        new Buff()));

    return templates;
  }

  private String lastIdentifier(String s) {
    String[] split = s.split("\\W");
    return split[split.length - 1];
  }

  private boolean hasAbility(ReadonlyCharacter character, String abilityName) {
    for (ReadonlyAbility ability : character.getAbilities()) {
      if (lastIdentifier(ability.name()).equals(abilityName))
        return true;
    }
    return false;
  }

  private boolean hasAbility(EnemyCharacter character, String abilityName) {
    for (ReadonlyAbility ability : character.getAbilities()) {
      if (lastIdentifier(ability.name()).equals(abilityName))
        return true;
    }
    return false;
  }

  private int goalX = 5;
  private int goalY = 5;

  @Override
  public ReadonlyAction choose(Set<ReadonlyAction> actions, ReadonlyCharacter character) {

    /* If we're at the goal square, pick a new one. */
    if (goalX == character.getLocation().getX() &&
        goalY == character.getLocation().getY()) {
      int i = getRandom().nextInt(5);
      goalX = i < 2 ? 1 : i > 2 ? 9 : 5;
      goalY = i == 2 ? 5 : (i % 2) == 1 ? 1 : 9;
    }

    {
      int bestDistance = 99999;
      /* If there are visible enemies, place the goal square under the closest enemy to
         the team. */
      for (Point2D enemyLocation : visibleEnemies.keysView()) {
        int distance = 0;
        for (ReadonlyCharacter ally : team) {
          Point2D allyLocation = ally.getLocation();
          distance +=
            (allyLocation.getX() - enemyLocation.getX()) *
            (allyLocation.getX() - enemyLocation.getX()) +
            (allyLocation.getY() - enemyLocation.getY()) *
            (allyLocation.getY() - enemyLocation.getY());
        }
        if (distance < bestDistance) {
          goalX = enemyLocation.getX();
          goalY = enemyLocation.getY();
          bestDistance = distance;
        }
      }
    }

    /* We use a priority rule for actions. */
    int bestPriority = -2;
    ReadonlyAction bestAction = null;
    for (ReadonlyAction action : actions) {
      int priority = 0;
      if (lastIdentifier(action.getName()).equals("Slice")) {
        int damagePotential = 35;
        /* We use these abilities with very high priority to /kill/ an enemy
           who's weak enough to die from the damage. If they wouldn't die,
           we still want to attack them, but we might prefer to attack
           other enemies instead. The enemy on the goal square (if any)
           is a slightly preferred target, to encourage the team to focus
           on a single enemy. */
        ReadonlyCharacter chosenTarget = null;
        for (ReadonlyCharacter target : action.availableTargets()) {
          if (!isEnemy(target))
            continue;
          chosenTarget = target;
          if (target.getHealth() <= damagePotential) {
            priority = 18;
          } else
            priority = 14;
          if (target.getLocation().getX() == goalX &&
              target.getLocation().getY() == goalY)
            priority++;
        }
        if (chosenTarget == null)
          continue;
        action.setTarget(chosenTarget);
      } else if (lastIdentifier(action.getName()).equals("Smile")) {
        priority = 0;
      } else if (action.movementAction()) {
        /* Move towards the goal location. */
        int bestDistance = 99999;
        Point2D bestLocation = null;
        priority = 1;
        for (Point2D location :
               action.availableLocations().toList().shuffleThis(getRandom())) {
          int distance =
            (location.getX() - goalX) * (location.getX() - goalX) +
            (location.getY() - goalY) * (location.getY() - goalY);
          if (distance < bestDistance) {
            bestDistance = distance;
            bestLocation = location;
          }
        }
        if (bestLocation == null)
          continue;
        action.setLocation(bestLocation);
      } else
        throw new RuntimeException("unknown action" + action.getName());

      if (priority > bestPriority) {
        bestPriority = priority;
        bestAction = action;
      }
    }
    if (bestAction == null)
      throw new RuntimeException("no action?");

    return bestAction;
  }
}

2

DarkAbsorbers

Темні поглиначі - 2 брати, які поглинають життєву силу своїх жертв:

  • Oracle Absorber (може бачити невидимих ​​ворогів)
    • STR: 25; AGI: 5; INT: 5
    • TrueSight , гнучка , діапазонна , поглинаюча
  • Швидкий поглинач (може поглинути навіть швидше, ніж його брат)
    • STR: 25; AGI: 5; INT: 5
    • Швидкий , гнучкий , діапазон , поглинаючи

Їх завжди супроводжує зростаюча Темна Хмара. Після досягнення критичної маси він починає вбивати ворогів.

  • Темна хмара
    • STR: 5; AGI: 5; INT: 25
    • Клон , Зап , Темрява

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

DarkAbsorbers.java
import java.util.Arrays;
import java.util.List;

import org.eclipse.collections.api.map.ImmutableMap;
import org.eclipse.collections.api.set.ImmutableSet;

import com.nmerrill.kothcomm.game.maps.Point2D;

import fellowship.abilities.ActionAbility;
import fellowship.abilities.ReadonlyAbility;
import fellowship.abilities.attacking.Absorb;
import fellowship.abilities.attacking.Flexible;
import fellowship.abilities.attacking.Ranged;
import fellowship.abilities.vision.Darkness;
import fellowship.abilities.vision.TrueSight;
import fellowship.actions.ReadonlyAction;
import fellowship.actions.attacking.Quick;
import fellowship.actions.damage.Zap;
import fellowship.actions.defensive.ForceField;
import fellowship.actions.other.Clone;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;

public class DarkAbsorbers extends SleafarPlayer {
    private ReadonlyCharacter zapTarget = null;

    private CharacterTemplate oracleAbsorberTemplate() {
        return new CharacterTemplate(20, 0, 0,
                new TrueSight(), new Flexible(), new Ranged(), new Absorb());
    }

    private CharacterTemplate quickAbsorberTemplate() {
        return new CharacterTemplate(20, 0, 0,
                new ActionAbility(Quick::new), new Flexible(), new Ranged(), new Absorb());
    }

    private CharacterTemplate darknessCloudTemplate() {
        return new CharacterTemplate(0, 0, 20,
                new ActionAbility(Clone::new), new ActionAbility(Zap::new), new Darkness());
    }

    @Override
    public List<CharacterTemplate> createCharacters() {
        return Arrays.asList(oracleAbsorberTemplate(), quickAbsorberTemplate(), darknessCloudTemplate());
    }

    private class Absorber extends Character {
        protected Absorber(ReadonlyCharacter delegate) {
            super(delegate);
        }

        @Override
        protected ReadonlyAction choose() {
            ReadonlyAction quick = getAction(Quick.class);

            if (quick != null && setSliceTarget(quick, 100.0)) {
                return quick;
            }
            if (slice != null && setSliceTarget(slice, 100.0)) {
                return slice;
            }

            ImmutableMap<Point2D, Double> damage = getEnemySliceDamage();
            ImmutableSet<Point2D> above5Damage = damage.select((k, v) -> v > 5.0).keysView().toSet().toImmutable();

            if (step != null && (above5Damage.contains(getLocation()) ||
                    (getHealth() <= 5.0 && isInEnemySliceRange())) && setAvoidEnemiesLocation(step)) {
                return step;
            }
            if (quick != null && setSliceTarget(quick, 0.01)) {
                return quick;
            }
            if (slice != null && setSliceTarget(slice, 0.01)) {
                return slice;
            }
            if (step != null && getSliceLocations().notEmpty() && setClosestLocation(step, getSliceLocations())) {
                return step;
            }
            if (step != null && setExploreLocation(step)) {
                return step;
            }
            return smile;
        }
    }

    private class DarknessCloud extends Character {
        private int zapCooldown = 0;
        private boolean zapNow = false;
        private boolean zapLater = false;

        protected DarknessCloud(ReadonlyCharacter delegate) {
            super(delegate);
        }

        private void updateZapFlags(double mana) {
            zapNow = zapCooldown == 0 && mana >= 15.0;
            zapLater = mana + 5 * getManaRegen() >= (zapNow ? 30.0 : 15.0);
        }

        private boolean isZappable(ReadonlyCharacter c, int zapNowCount, int zapLaterCount) {
            int forceFieldNow = 0;
            int forceFieldLater = 0;
            for (ReadonlyAbility a : c.getAbilities()) {
                if (a.abilityClass().equals(ForceField.class)) {
                    forceFieldNow = a.getRemaining();
                    forceFieldLater = 5;
                }
            }
            return c.getHealth() + c.getHealthRegen() <= (zapNowCount - forceFieldNow) * 30.0 ||
                    c.getHealth() + c.getHealthRegen() * 6 <= (zapNowCount + zapLaterCount - forceFieldNow - forceFieldLater) * 30.0;
        }

        @Override
        protected ReadonlyAction choose() {
            ReadonlyAction clone = getAction(Clone.class);
            ReadonlyAction zap = getAction(Zap.class);

            zapCooldown = zapCooldown > 0 ? zapCooldown - 1 : 0;
            updateZapFlags(getMana());
            int zapNowCount = characters.count(c -> c instanceof DarknessCloud && ((DarknessCloud) c).zapNow);
            int zapLaterCount = characters.count(c -> c instanceof DarknessCloud && ((DarknessCloud) c).zapLater);

            if (zap != null) {
                if (zapTarget != null && (!zap.availableTargets().contains(zapTarget) || zapTarget.isDead() ||
                        !isZappable(zapTarget, zapNowCount, zapLaterCount))) {
                    zapTarget = null;
                }
                if (zapTarget == null) {
                    zapTarget = chooseSmallest(zap.availableTargets().reject(c ->
                            isBear(c) || !isZappable(c, zapNowCount, zapLaterCount)), HEALTH_COMPARATOR);
                }
                if (zapTarget != null) {
                    zapCooldown = 5;
                    zapNow = false;
                    zap.setTarget(zapTarget);
                    return zap;
                }
            }

            ImmutableMap<Point2D, Double> damage = getEnemySliceDamage();
            ImmutableSet<Point2D> above5Damage = damage.select((k, v) -> v > 5.0).keysView().toSet().toImmutable();

            if (clone != null) {
                if (visibleEnemies.isEmpty()) {
                    if (setFarthestLocation(clone, getTeamHiddenLocations())) {
                        updateZapFlags(getMana() - 100.0);
                        return clone;
                    }
                } else {
                    if (setFarthestLocation(clone, above5Damage, getEnemyLocations()) ||
                            setLocation(clone, chooseSmallest(clone.availableLocations(),
                            (o1, o2) -> Double.compare(damage.get(o1), damage.get(o2))))) {
                        updateZapFlags(getMana() - 100.0);
                        return clone;
                    }
                }

                return clone;
            }
            if (step != null && (above5Damage.contains(getLocation()) ||
                    (getHealth() <= 5.0 && isInEnemySliceRange())) && setAvoidEnemiesLocation(step)) {
                return step;
            }
            if (slice != null && setSliceTarget(slice, 0.01)) {
                return slice;
            }
            if (step != null && !visibleEnemies.isEmpty() &&
                    setFarthestLocation(step, getEnemySliceLocations(), getEnemyLocations())) {
                return step;
            }
            return smile;
        }
    }

    @Override
    protected Character createCharacter(ReadonlyCharacter delegate) {
        if (hasAbility(delegate, Absorb.class)) {
            return new Absorber(delegate);
        } else if (hasAbility(delegate, Darkness.class)) {
            return new DarknessCloud(delegate);
        } else {
            throw new IllegalArgumentException();
        }
    }
}

0

LongSwordv2

"Ви можете бігти, але ви не можете приховати ..." - LongSwordv2

Використання діапазону , гнучка , швидка , TrueSight

Цей бот саме такий такий же, як LongSwordv2, за винятком того, що він використовує TrueSight замість Strong.

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

LongSwordv2.java
import fellowship.*;
import com.nmerrill.kothcomm.game.maps.Point2D;
import fellowship.abilities.ActionAbility;
import fellowship.abilities.attacking.Flexible;
import fellowship.abilities.attacking.Ranged;
import fellowship.abilities.stats.Strong;
import fellowship.abilities.vision.Darkness;
import fellowship.abilities.vision.TrueSight;
import fellowship.actions.ReadonlyAction;
import fellowship.actions.attacking.Quick;
import fellowship.characters.CharacterTemplate;
import fellowship.characters.ReadonlyCharacter;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

public class LongSwordv2 extends Player{
    //debugging
    private boolean debug = false;
    private void println(String text) {
        if(debug)
            System.out.println(text);
    }

    //variables use to hold the start Y coordinate of the bot
    private boolean started = false;
    private int startY = 5;

    private boolean together = false;

    @Override
    public List<CharacterTemplate> createCharacters() {
        List<CharacterTemplate> templates = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            templates.add(new CharacterTemplate(20, 0, 0,
                    new Ranged(), //Adds 1 to the range of Slice
                    new Flexible(), //Can Slice in any of the 8 directions
                    new ActionAbility(Quick::new), //Slice twice, Mana: 3, Cooldown: 0
                    new TrueSight())); //Reveals all hidden units within range 2 at turn start
        }
        return templates;
    }

    @Override
    public ReadonlyAction choose(Set<ReadonlyAction> actions, ReadonlyCharacter character) {
        if(!started) {
            startY = character.getLocation().getY(); //giving startY the value of the bot's starting y-value
            started = true; //do this only once, that's why there is the if statement
        }

        ReadonlyAction current = null;

        //choosing action depending on priority
        int priority = Integer.MAX_VALUE;
        for(ReadonlyAction action:actions) {
            int priorityLocal = getPriority(action, character);
            if(priorityLocal < priority) {
                current = action;
                priority = priorityLocal;
            }
        }

        if (current == null){
            throw new RuntimeException("No valid actions");
        }

        println(current.getName());

        if(current.needsLocation()) {
            if(visibleEnemies.isEmpty()) {
                if (character.getHealth() < 100) {
                    //if has low health, go backwards towards "base"
                    //println("lowHealth");
                    current.setLocation(move(current, character, "backward"));
                } else {
                    //else go forwards to enemy's "base"
                    current.setLocation(move(current, character, "forward"));
                }
            }else{
                //go towards closest enemy
                current.setLocation(current.availableLocations().minBy(p1->p1.cartesianDistance(visibleEnemies.keysView().minBy(p1::cartesianDistance))));
            }
        }
        if(current.needsTarget()) {
            //get closest target
            current.setTarget(current.availableTargets().minBy(p1 -> 0));
        }

        Iterator<ReadonlyCharacter> iterator = current.availableTargets().iterator();

        while(iterator.hasNext()) {
            Point2D loc = iterator.next().getLocation();
            println(loc.getX()+","+loc.getY());
        }

        return current;
    }

    //move backwards or forwards
    private Point2D move(ReadonlyAction readonlyAction, ReadonlyCharacter character, String direction) {
        Point2D location = null;

        //move direction depending on Y coordinate of point
        for(Point2D point2D:readonlyAction.availableLocations()) {
            switch (direction) {
                case "forward":
                    if(startY > 5) { //bot started at bottom
                        if (point2D.getY() < character.getLocation().getY())
                            location = point2D;
                    }else{ //bot started at top
                        if (point2D.getY() > character.getLocation().getY())
                            location = point2D;
                    }
                    break;
                case "backward":
                    if(startY > 5) { //bot started at bottom
                        if (point2D.getY() > character.getLocation().getY())
                            location = point2D;
                    }else{ //bot started at top
                        if (point2D.getY() < character.getLocation().getY())
                            location = point2D;
                    }
                    break;
            }

        }

        //if no available locations, just choose the first available location
        if(location == null) {
            location = readonlyAction.availableLocations().iterator().next();
        }

        println(location.getY()+","+character.getLocation().getY());

        return location;
    }

    private int getPriority(ReadonlyAction action, ReadonlyCharacter character) {
        if(visibleEnemies.isEmpty()) {
            //if there are no visible enemies, Step. In the choose function, this becomes move forward or backward depending on health
            if(action.getName().equals("Step")) {
                return 100;
            }
        }else {
            /*
             * PRIORITIES:
             *  1. Quick (Slice twice)
             *  2. Slice
             *  3. Step (when enemy is not in range --> move towards enemy)
             */
            if (action.getName().equals("Quick")) {
                return 1;
            }else if(action.getName().equals("Slice")) {
                return 10;
            }else if(action.getName().equals("Step")) {
                return 50;
            }
        }
        //Kids, don't Smile, instead Step or Slice
        return 1000;
    }
}

Завантаження цього бота не вдається, оскільки заголовок відсутній.
Sleafar

@Sleafar Там ми йдемо ... додав його!
Kritixi Lithos
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.