친교의 전투 KotH


45

이 도전에서, 당신은 친교 를 만들 것입니다 전투에서 다른 모든 친교를 물리 치기 것입니다.

친교 (팀)는 3 자로 구성됩니다 . 각 캐릭터는 다른 팀과 독립적으로 움직이지만 적과 싸울 때 함께 일해야합니다. 팀은 서로 마주보고 회전합니다. 승은 3 점, 동점은 1 점, 손실은 0 점입니다.

캐릭터는 능력이 있습니다. 캐릭터가 가진 능력의 선택은이 KotH에서 가장 중요하고 재미있는 부분 중 하나입니다 . 그것들은 모두 강하고 적을 쓸어 낼 수 있습니다.

캐릭터의 체력은 (HP)이고, 체력이 0 이하일 때 죽습니다 . 상대 팀의 모든 캐릭터가 죽으면 승리합니다!

캐릭터에는 마나가 있습니다. 대부분의 작업에는 Mana가 수행해야하며 , 충분하지 않으면 해당 작업을 사용할 수 없습니다.

캐릭터는 턴 지연이 있습니다. 각 턴 사이의 틱 수를 결정합니다 (100에서 시작). 낮을수록 좋습니다.

문자에는 속성이 있습니다. 각 캐릭터는 각 속성에 밑이 ​​5이며, 분할 할 속성 포인트가 20 개 더 있습니다. 속성 포인트를 할당하면 기본 속성 이 가장 높은 속성으로 설정됩니다.

사용 가능한 속성은 다음과 같습니다.

  • 힘 : 1 턴당 최대 HP 10, .5 HP
  • 지능 : 턴당 최대 마나 7과 마나 1을줍니다.
  • 민첩성 : 회전 지연을 1 감소

움직임, 비전, 범위
범위는 다음과 같습니다 (0을 중심으로). 일부 범위는 추기경 이므로 직접 위, 왼쪽, 오른쪽 또는 아래로만 이동할 수 있습니다.

    444
   43334
  4322234
 432111234
 432101234
 432111234
  4322234
   43334
    444

등장 인물은 2의 시작 비전을 가지고 있습니다.

게임 방법

건설
플레이어는 교제를 구성합니다. 다음 단계를 수행해야합니다 .

  1. 각 문자 속성 포인트를 지정하십시오 . 각 캐릭터는 각 스탯에서 5로 시작하고 3 사이에 20을 더 분배합니다.

  2. 각 캐릭터에게 능력을 부여하십시오 . 각 캐릭터는 4 개의 능력 슬롯으로 시작하며 기본적으로 능력은 1 슬롯을 사용합니다. 일부 능력은 반복 가능 하며 캐릭터에게 여러 번 주어질 수 있습니다. 소유자의 허가없이 다른 제출 기능을 사용하는 것은 허용되지 않습니다.

  3. 봇을위한 코드작성 하십시오. 코드는 Java로 작성되어야하며 전투에 사용됩니다 (다음 단계).

행위

모든 캐릭터는 3 가지 표준 액션으로 시작합니다 :

  1. 단계 : 1의 기본 범위에서 캐릭터를 이동
  2. 슬라이스 : 기본 범위 1에서 1 차 속성 으로 적 공격
  3. 스마일 : 아무것도 하지마

캐릭터 차례에 수행 할 액션을 선택해야합니다. 행동에는 마나가 소모되고 재사용 대기 시간이 생겨 해당 행동을 다시 수행하기 전에 기다려야하는 턴 수를 정의 할 수 있습니다.

능력
모든 캐릭터에는 능력 슬롯이 4 개 있습니다. 이탤릭체로 된 능력이라면 행동입니다.

능력

이름 설명 마나 재사용 대기 시간 
                                                     이동                                                     

깜박임         사각형으로 이동, 범위 4 2 2 
          대상과 스왑 스왑 위치 5 5 
 텔레포트      어디든 이동 20 5

대시 단계의 범위를 1 씩 증가시킵니다.                                               
Mobile Step은 8 방향으로 움직일 수 있습니다                                                  
                                                    공격                                                    

         슬라이스 두 번 3 0 
 직조         보이는 모든 적을 한 번만 슬라이스하십시오 15 10

각 조각을 흡수하면 대상의 기본 특성 중 1 개를 훔칩니다. 20 턴 지속                    
각 조각을 쪼개서 인접한 적에게 1/2의 피해를줍니다.                                           
치명타 슬라이스가 30 %의 확률로 200 %의 피해를 입 힙니다. 반복 가능                               
Feast Each Slice는 HP를 3 증가시킵니다.                                             
8 개 방향 중 어느 방향 으로든 플렉시블 캔 슬라이스                                                      
마나 도둑 조각이 2 마나를 훔칩니다. 반복 가능                                                           
슬라이스시 반사 슬라이스 0 3 
원거리 슬라이스의 범위에 1을 더함                                                              
스 와이프 같은 대상에있는 연속 조각 하나당 마지막보다 3 개의 추가 피해를줍니다.               
                                                    상태                                                     

Dispel        대상에서 모든 상태를 제거합니다. 사거리 2. 20 10 
 결투          당신 중 한 명이 죽을 때까지 당신과 대상을 얼립니다. 범위 1 25 0 
 개편,      당신과 대상이 틱 다음 1000 기절 10 10 
 유성        다음 100 25 10 틱에 대한 모든 적들은 기절 
 가죽 끈         대상은 자신이 다음 턴 동안 4 ~ 6 동결 
         5 턴 동안 1 HP에 대한 독 적을 5 0 
 침묵      대상이 5 턴 동안 침묵합니다. 5 7 
 느린          대상이 다음 3 턴 동안 40 틱만큼 느려집니다. 10 5 
 충격          대상이 다음 300 틱 동안 기절합니다. 10 10

추위 2 범위 내의 다른 모든 문자는 10 틱만큼 느려집니다.                                
면역 상태가 적용되지 않습니다                                                           
                                                    방어                                                    

포스 필드   블록 다음 5 가지 피해 원. 하지 스택 (15) 5합니까 
 Ghost를         회전, 모든 피해의 치유 (10) (10) 
 치유          HP 10 3 20 대상을 치유 
 복원       모든 단위는 전체 건강 (20) (40)에 다시 복원 
 쉴드        당신의 다음 턴 때까지 슬라이스를 할 수 없다 3 0

회피가 25 % 확률로 슬라이스가 맞지 않습니다. 반복 가능                                         
기둥 만 한 번만 슬라이스 가능                                                            
부활 죽었을 때 완전한 HP 상태로 상태가 회복됩니다. 
스파이크 데미지를 입었을 때, 데미지의 절반을 되돌립니다                                           
                                                     전망                                                      

망토         팀이 5 턴 동안 보이지 않습니다. 20 20 
 숨기기          5 턴 동안 보이지 않습니다. 4 7 
 단계         1 턴 동안 보이지 않습니다. 0 3 
 트랙         대상이 보이지 않게되어 공격력이 10 %만큼 증가합니다. 10 턴 동안 지속됩니다. 5 5

암흑 적의 시야 범위가 1만큼 감소했습니다.                                 
원거리 시력 범위가 2만큼 증가했습니다.                                                    
보이지 않음 당신은 적의 비전에서 당신의 차례를 시작하면 보이지 않습니다                               
진정한 시야 턴 시작시 범위 2 내의 모든 숨겨진 유닛을 공개합니다                                     
                                                     손해                                                      

드레인은         그들이 한 범위를 유지하면서 5 HP의 대상과 치유자가 5 데미지를 10 5 
 번개     거래 모든 적 20 10-15 손상 
 K / O           대상이 20 % HP 20 0 이하이면 킬 대상 
 트랩          장소 보이지 않는 함정. 함정을 밟으면 함정에 15의 피해를줍니다. 스택. 10 2 
            거래 30 대상에게주는 피해 30 5

정적 1 범위 내의 모든 적에게 1 턴마다 5의 피해를줍니다. 반복 가능                       
                                                      통계                                                      

늑대 인간      5 턴 30 25 모든 통계에 10 추가

버프 HP 풀을 두 배로 늘리십시오. 반복 가능                                                           
영리한 행동의 재사용 대기 시간이 20 % 짧습니다. 반복 가능                                             
집중 마나 회복 속도를 Int / 10만큼 증가시킵니다. 반복 가능                                  
재생 재생력이 2만큼 증가합니다. 반복 가능                                 
현명한 행동은 마나가 2만큼 감소합니다. 반복 가능                                                      
강점 10 특성 포인트를 얻습니다. 반복 가능                                                  
약점 15 점을 잃습니다. 능력 슬롯이 2 개 있습니다 (이 중 하나가 필요합니다)                  
                                                      다른                                                      

          캔 각 합계 8 10 5을 가진 곰 소환 
 복제         복제 자신을. 능력 슬롯 2 개를 가져갑니다. 100 100 
 Steal         이 액션을 마지막으로 사용한 적 대상으로 바꿉니다. 지속 10 턴 5 0 
 벽 목표로          지정된 빈 정사각형, 범위 6 10 10에 통과 할 수없는 벽 만들기 

상태 :

  • 기절은 캐릭터가 스마일 동작 수행하고 X 진드기를 지속 합니다.
  • 동결은 캐릭터가 움직이지 못하게하고 X 턴 동안 지속됩니다.
  • 침묵은 캐릭터가 스마일, 스텝 또는 슬라이스 이외의 것을 수행하지 못하게하고 X 턴을 지속합니다.
  • 독은 Y 턴 동안 X의 피해를 입 힙니다. 다른 독을 적용하면 피해가 합쳐지고 지속 시간이 새로 고쳐집니다.
  • 천천히는 턴 사이의 진드기 수에 X를 더합니다. 그것은 다가오는 턴 에는 영향을 미치지 않으며 , 오직 다음 턴에만 영향을줍니다 .
  • 보이지 않음으로 인해 상대방이 보거나 손상을 입을 수 없습니다. Step 또는 Smile 이외의 작업을 수행하면 제거됩니다. 상대방에게 당신의 시야를 제공하는 능력이 있다면, 투명성이 제거됩니다.

모든 상태 (독을 제외하고)는 서로 독립적으로 작동합니다.

사이드 노트 :

  • 기본 속성과의 연계가 있으면 STR> AGI> INT로 확인됩니다.
  • 10x10 그리드에서 재생합니다. 팀은 반대편에 배치됩니다.
  • Clever를 제외하고 백분율은 곱셈으로 누적됩니다.

제출 규칙

두 가지 기능을 구현해야합니다.

// 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. JRE가 아닌 JDK java 가리켜 야합니다 .
  2. 자식 저장소를 복제 하고 실행하십시오 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
의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
Dennis

번개는으로 표시 Deal 15 damage to all enemies되지만 보이지 않는 적들은 번개의 영향을받지 않습니다. 이것이 버그입니까? 그렇지 않으면, 보이지 않는 것이 나에게 꽤 강해 보인다.
CommonGuy

1
이 과제는 이전의 그러한 과제보다 훨씬 더 복잡합니다. 장기적으로 이와 같은 것을 더욱 경쟁력있게 만든 형식이 있었으면 좋겠습니다.
Sparr

2
그러나 -g 옵션이 있는지 알고 있지만 봇을 개발할 때 아직 사용 가능한 상태가 아니기 때문에 대안을 만들기 시작했습니다. 현재 매우 초보적이지만 가시 광선 반경이 있습니다. 곰 기병 대 템플릿 플레이어의 캡처입니다! 포착 .
Moogie

1
새로운 캐릭터를 테스트하고 점수를 업데이트 할 수 있습니까?
파괴 가능한 레몬

답변:


10

스태틱 클라우드

가까이 다가오는 사람에게 정적 인 피해를주는 지각있는 성장하는 구름. 다음으로 구성됩니다.

  • 1/3 보이지 않는 부분
    • STR : 5; AGI : 5; 지능 : 25
    • 클론 , 보이지 않는 , 정적
  • 2/3 보이는 부분
    • STR : 5; AGI : 5; 지능 : 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
Static은 보이지 않는 것을 깨뜨리지 않기 때문에 흥미 롭습니다.
Draco18s

7

템플릿 플레이어

용도 원거리 , 유연한 , 기력 , 그리고 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

겁쟁이 스나이퍼 Mk2

사용 , FarSight * 2, 숨기기 .

이 봇은 겁쟁이입니다. 최우선 순위는 대상이 아닙니다. 이를 위해 그는 뛰어난 시야를 사용하여 적의 위치를 ​​봅니다. 이 지식을 사용하여 적을 보지 않고 추적 / 추적하는 동안 보이지 않도록합니다. 그것이 보이거나 다음 턴에 보일 수 있다면, 봇은 한동안 보이지 않게 '숨겨 질'것입니다.

추적 모드에서 충분한 마나 및 재사용 대기 시간이 재설정되면 가장 약한 적을 '약탈'합니다.

마나가 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이어야합니다.
Nathan Merrill

oops : P 잘라내어 붙여 넣기를하면 바보처럼 보입니다!
Moogie

마리오 카트 Wii의 B 교반기 MK2 : 생각 나는
Kritixi LITHOS

@KritixiLithos Splatoon의 Bamboozler 14 Mk II를 상기시킵니다. ;)
TNT

7

TheWalkingDead

좀비, 모두가 그들을 알고 있습니다. 그들은 누군가가 나타날 때까지 아무것도하지 않는 그룹에 머물러 있습니다. 그들은 죽이기 어렵고, 당신이 얼마나 많은 사람을 죽이더라도 항상 더 있습니다. 그리고 그들은 보통 등 뒤에서 아무데도 나타나지 않습니다.

  • 1 x 좀비 # 1 (가장 강력하고 알파 좀비)
    • STR : 25; AGI : 5; 지능 : 15
    • 클론 , 부활 , 강한
  • 2 x 좀비 # 2 (아무도, closing 신용에 좀비 # 3되고 싶어하지, 따라서 모두 같은 번호를 가지고)
    • STR : 15; AGI : 5; 지능 : 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 기본 클래스는 최신 마스터 브랜치에없는 것 같은 fellowship.characters.CharacterInterface를 가져옵니다 ...
Moogie


7

무적

거의 모든 것에서 살아남을 수있는 강력한 전사들로 구성된 팀. 이로 인해 많은 시간 초과가 발생하지만 불행히도 종종 이기지 못합니다. 그러나 내가 본 매치업은 이길 수 없으며 팀이 패배 할 때 캐릭터가 여전히 살아남는 경우가 많습니다.

이 팀의 가장 어려운 경기는 베어 기병대와의 경기입니다 (그들은 문자 그대로이 팀을 닦을 수는 없지만 일반적으로 숫자로 인해 순위 결정에서 승리합니다). Rogue Squad (팀은 독에 약하다); 뱀파이어 (아직 왜 아직 확실하지 않습니다).

시뮬레이션에서 팀은 거의 항상 1 ~ 2 위를 차지합니다. 점수는 꽤 안정적입니다. 우승자는 일반적으로 Rogue Squad가 다른 경쟁자에 비해 얼마나 잘 수행하는지에 달려 있습니다 (이의 배치는 무적의 것보다 훨씬 무작위입니다).

유해한 구체의 마법사

  • STR : 5; AGI : 5; 지능 : 25

마법사는 Pillar and Force Field 의 콤보를 사용하여 스스로를 보호합니다 . 집중 되고 과 높은 지능을 가지고, 그 때마다 재사용 대기 시간에 캐스팅 포스 필드에 충분한 마나를 다시 생성합니다. 상대방이 민첩성이 향상되지 않았다고 가정하면 5 턴당 최대 5 개의 조각으로 적을 공격 할 수 있으며이 기간 동안 5 개의 피해 소스가 차단됩니다. 다시 말해, 마법사는 주문을 물리 치기 위해 절대적으로 필요합니다. 당신이 아무리 잘해도 슬라이싱은 그 자체로는 효과가 없습니다.

마법사는 비상시 슬라이싱 (Slicing)을 통해 25 동안 공격 할 수 있지만, 대부분 독을 반복해서 시전 합니다. 포이즌은 팀의 비전에 의해서만 제한되는 무한한 범위를 가지므로 마법사는이 팀이 HP가 무겁거나 회생하는 적을 물리 치는 방식입니다. 나머지 팀은 비전을 유지하고 마법사는 2 차 피해를 입 힙니다. 독은 필연적으로 HP 재생 속도를 앞질렀습니다. 프로세스의 스파이크와 같은 정적 손상에 대해 걱정할 필요가 없습니다.

자이언트 클리프 탑 팔 코너

  • STR : 35; AGI : 5; 지능 : 5

Falconer의 주요 임무는 나머지 팀의 목표물을 목표로하여 공격 할 수 있도록 매의 눈으로 맵을 정찰하는 것입니다. 원거리 시력 은 숨기거나 도망치려는 적들이 일반적으로지도 구석에있는 시야 범위에 갇힐 수있는 충분한 시야를 제공합니다. Falconer에게 대안을 제공하는 것은 쉽지 않습니다. True Sight 는 보이지 않는 적들에 대한이 팀의 주요 수단으로, 그렇지 않으면 피해를 줄 수 없습니다. Falconer가 크고 따라서 강력 하면 손상에 매우 강하고 필요할 때 무거운 조각을 만들 수 있습니다. 마지막으로, 팔 코너는 아래의 적군에게 팔콘을 풀어 적들 사이 에 직조 하도록 지시하고 막대한 (35)의 지역 피해를 입 힙니다.

Falconer의 직업은 회피 적을 사냥하고, 마법사가 독을 계속 유지할 수 있도록 적의 시야를 유지하는 것 외에도 가끔 35 명을 짜는 능력이 적군 팀을 상대하는 데 중요합니다. 그런 식으로 많은 적을 공격 할 수 있으며, 팀의 나머지 팀이 완료 할 수있을 정도로 낮은 상태로 둘 수 있습니다 (이상적으로는 다른 직조와 함께). 스 워밍은 이러한 규칙에 따른 강력한 전략이며, 위브 (Wave)는 몇 가지 실제 카운터 중 하나입니다. 그럼에도 불구하고, 그 자체로는 충분하지 않습니다.

트롤 본 스컬 렌더

  • STR : 25; AGI : 5; 지능 : 5

트롤 본의 임무는 다른 부대가 임무를 수행하는 동안 적의 무리를 진압하는 것입니다. 마법사와 마찬가지로 트롤 본은 녹아웃 에서 무한한 주문을 사용 합니다. 이것은 마법사의 독과 아주 잘 어울립니다. 적을 하나씩 상대 할 수 있다면 (그리고 많은 팀과 맞서면), 팔 코너는 비전을 얻고, 트롤 본은 그들을 기절시키고, 마법사는 그들에게 독을 쌓아 죽게됩니다. 아무 것도 할 수있는 능력없이 (녹아웃은 대상에서 1000 틱을 지속하고 트롤 본은 재사용 대기 시간을 약간 빠르게 회복합니다). 또한 하나의 강한 적들로부터 트롤 본을 보호하는 데 아주 좋습니다. 그들은 의식이 없으면 그에게 아무것도 할 수 없습니다. 물론, 적과 함께 해골을 때리는 것은 두 가지 뇌진탕을 남기고기절시키고 독에 중독시키는 것 (그리고 아무도 신경 쓰지 않는 다른 상태들). 그럼에도 불구하고 트롤 본은 마법에 사로 잡히지 않는 마법에 집중된 캐릭터로서 지능을 통해서가 아니라 적의 피를 마시면서 적을 때마다 마나 훔치기 를 통해 마법을 재생합니다 . 이것은 꽤 좋은 MP 재생 속도를 제공합니다 (그리고 기절 한 적들은 MP를 훔치기 쉬운 대상으로 만듭니다). 마지막으로, Trollbone 가끔 미쳐가는 것 위브 에 머리를 부딪 히고 그들의 피를 마시는 동안 적의 계급을 통해서. 충분히 큰 적 무리에 대항하여, 이것은 실제로 마나를 회복하며, 팔 코너가 약해진 무리를 마무리 짓습니다 (25 + 35는 60이므로 적들이 그 사이에 어느 정도 재생 되더라도 작동합니다).

전략

많은 팀과 달리 팀 구축뿐만 아니라 AI에도 많은 관심을 기울였습니다. 기본 규칙 중 하나는 팀이 다른 일을 하느라 바쁘지 않으면 항상 그룹화를 시도하여 서로 포위하고 서로를 방어하기가 더 어렵다는 것입니다. 그들이 무리를 짓고 있다면, 그들은 모퉁이에 숨기려고 노력할 것입니다. 반면에 적군이 도망 치거나 도망 가려고하면지도를 돌아 다니며 무작위 구석이나 중앙으로 이동합니다. 이것은 Falconer가 결국 목표를 발견 할 것임을 보증합니다. 이 운동은 가능한 경우 적이 첫 번째 공격을받지 않도록 설계되었습니다. 적은 슬라이스 범위로 들어가야합니다. 마법사는 항상힘 소진을 위해 MP를 남겨두면 MP 소진으로 인한 손실을 막을 수 있습니다. (실패 할 수있는 유일한 방법은 피해를 입지 않아도 포스 필드를 통과 할 수있는 Absorb입니다.) 이것은 일반적으로 문제가되지 않습니다. 일반적으로 마법사는 매 턴마다 아무 문제없이 독을 스팸으로 분류 할 수 있습니다. 방해받지 않으면 팀은 적을 한 번에 하나씩 추격하여 시력에 들어올 때 기절시킨 다음 죽을 때까지 반복적으로 중독시키는 것을 선호합니다. 주변의 다른 적들과 함께, 팀은 가능하다면 연을 날 리려고 노력하며, 원을 그리며 뛰어 다니며 대부분의 적을 쫓아 내며, 적 중 하나를 놀라게하고 중독시킵니다. 주요 문제는 무리에 관한 것이므로 여기에 직물이 너무 많지만 실제로 전략을이기는 것은 어려워 보입니다.

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

체력이 50 인 체력 필드를 가진 캐릭터는 내 멍청이에게 물리 칠 수 없습니다!
Kritixi Lithos

@Sleafar가 지적한 버그를 수정했습니다.

나는 당신이 당신의 팔 코너와 트롤 본에 너무 많은 기술 포인트를 가지고 있다고 생각합니다
Eumel

너무 고쳤다. 설명에서 오타 일뿐 코드는 정확합니다.

7

도적 분대

불량 팀은 다음과 같이 구성됩니다.

  • 1 정찰병 (지도를 탐색하는 동안 그림자에 머물러 있습니다)
    • STR : 5; AGI : 5; 지능 : 25
    • 클론 , 보이지 않는 , 원거리
  • 암살자 2 명 (치명적인 독으로 적을 공격)
    • STR : 5; AGI : 5; 지능 : 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();
    }
}

잘 했어요 이길 힘든 팀 ... 도전 수락 : P
Moogie

6

흡혈귀

나는 이것에 익숙하지 않으며 내가 무엇을하고 있는지 잘 모르겠지만, 그것이 흥미로워 보인다고 생각합니다.

뱀파이어는 적을 찾고 가장 약한 대상을 목표로 삼아 생명을 빼앗아 가고 강해지면서 자신의 건강을 회복하고 다음 희생자로 넘어갈 준비를합니다. 그들이 중상을 입을 경우, 자연 재생이 전투 상태로 회복 될 때까지 이동을 시도합니다.

사용하여 흡수 , 축제 , 재생성 , 강한 의 모든과 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자를 갖도록합니다. 그런 다음 그들은 적을 충전하고, 가능할 때마다 스팸 곰, 그리고 통계 흡수 공격으로 적을 약화시킵니다.

코드가 엉망이지만 작동하는 것 같습니다. 템플릿 플레이어에서 일부를 복사했습니다.

이 팀의 캐릭터를 원하는 방식으로 사용할 수 있습니다.

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

대못 같은

Spiky는 그의 이름에서 알 수 있듯이 맹목적으로 공격을받지 않습니다. 그는 탱커이고 많은 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

좋은 봇! 뾰족한 것은 내 봇 중 최고를 물리 쳤다.
Kritixi Lithos

로 변경할 ThreadLocalRandom.current()getRandom()있습니까? 게임이 결정론적일 수 있습니다.
Nathan Merrill

5

마법사

클론 자신과 함께 가능한 한 모든 적에게 많은 인스턴트 손상으로 다루는 위브 (가 번개 이전했지만, 위브 더 많은 피해를 수행하고 낮은 마나가 있습니다.

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

롱소드

원거리 사용 (슬라이스 범위에 1을 더함), 유연성 (8 방향으로 슬라이스 가능 ), 빠름 (두 번 슬라이스, 마나 : 3, 재사용 대기 시간 : 0), 강함 (특성 점수 10 개 증가)

통계

시작 5 점은 기본입니다

  • STR : 5 + 20 + 10
  • AGI : 5 + 0
  • 지능 : 5 + 0

우선, 나는이 봇을 만드는 것을 즐겼고, 나는이 KotH를 정말로 좋아합니다 (이것은 KotH 도전에 대한 첫 번째 제출입니다!). (나는 더 많은 봇을 게시 할 수 있습니다)

이 봇은 적을 제압하기 위해 공격 능력에 의존합니다. 내가 테스트 한 한,이 봇은 건강이 비교적 낮은 봇에 대해 정말 좋습니다. 또한, 공격 범위가 넓고, 대부분의 적 (또는 절반)의 적을 쉽게 겨냥 할 수 있습니다.

이 봇을 NetHack 역할과 비교하기 위해 "LongSword"의 개념과 평균 건강으로 인해 Valkyrie 와 매우 유사하다고 말하고 싶습니다.

이름

이 봇은 일반 봇보다 약간 더 길며 어떤 방향 으로든 공격 할 수 있습니다. 이것은 NetHack에서 대부분의 Long Sword를 생각 나게 했으므로 봇의 이름을 그렇게 정했습니다.

행동

캐릭터가 적 캐릭터를 볼 수 없다면, 필드의 반대편 (적의 스폰 지역 / 적의 "베이스")으로 가서 적 캐릭터를 찾습니다. 적을 찾으면 빠른 슬라이스 (우선 순위 감소)로 적을 공격합니다. 적을 목표로 할 수 없다면, 로봇은 적 캐릭터를 향해 그들을 파괴합니다.

캐릭터가 적 캐릭터를 볼 수없는 경우 낮은 상태를 가지고, 그것은 "기본"/ 스폰 지역으로 후퇴 할 것이다.

참고 : 봇은 전투 도중에 퇴각하지 않습니다. 이 로봇은 절대 웃지 않을 것입니다.

나는 다음 정규식을 사용했다regexr.com 을 Java 코드를 형식이 지정된 코드 블록으로 변환했습니다.

아래 코드는 주석 처리되어 이해하기 쉬워야합니다. 어떻게 작동하는지에 대한 질문이나 설명이 있으시면, Battle of the Fellowships 대화방 에서 저에게 핑을 보내주십시오 !

편집 : 시작 위치에 따라 봇의 움직임 (앞으로)을 조정하기 위해 프로그램에서 사소한 실수를 수정했습니다. 나는 이것을 잊었다. 그래서 나는 지금 그것을 편집했다.

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

탈선 자

그것을 두 번 삭제했다많은 논리 오류가 발생하여 . :피

이것은 분명히 당신의 계획을 탈선시킬 수 있습니다. ;)

팀:

  • 크리티컬 , 버프 , 스트롱 , 으로 1 명의 캐릭터를 처치 하면 적을 물리 치기가 매우 어렵습니다. 힘 +25, 지능 +2, 지능 +3
  • Clever , Clever , RestoreZap이있는 1 문자 . 지원을 유지하고 HP를 많이 사용하는 팀원의 건강을 회복하고 필요한 공격과 방어를 할 수 있습니다. 체력 +14, 지능 +3, 지능 +3
  • TrueSight , Spikes , EvasiveWeave가있는 1 개의 문자 . 너무 쉽게 칠 수 없으며, 그렇게하거나 너무 가까워지면 당신을보고 파업합니다. 힘 +13, 지능 +3, 지능 +4
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

감사. 팀 내에서 시너지를 통합하려고 시도했지만 세 번째 캐릭터가 약간 벗어난 것 같습니다. 아마도 미래의 봇에서이 전략을 개선 할 것입니다.
TNT

흥미롭게도 세 번째 문자를 첫 번째 문자의 사본으로 바꾸면 훨씬 더 나은 결과를 얻을 수있었습니다.
TNT

4

스나이퍼 스쿼드

저격 팀은 다음과 같이 구성됩니다.

  • 1 스 포터 (사용 가능한 최고의 스포팅 장비를 갖추고있어 거의 전체지도를 볼 수 있음)
    • STR : 25; AGI : 5; 지능 : 5
    • 원거리 시력 , 원거리 시력 , 원거리 시력 , 원거리 시력
  • 저격수 2 명 (최신 멀티 타겟 저격 소총 장착 한이 유일한 단점 느린 화재 속도입니다)
    • STR : 25; AGI : 5; 지능 : 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();
        }
    }
}

Hehe, 당신의 저격수는 저의 겁쟁이 저격수를
도청

3

늑대 인간

AI 선택 선택 을 작성하는 데 있어서는 특히,이 규칙만큼 복잡한 규칙 집합에 대해서는 가장 훌륭하지 않습니다 . 게임 상태를보고 배우가 의사 결정을 내리는 능력이 낮고 (실행마다 결과가 약간 다르면 AI 논리를 향상시키기 위해 약간의 변화로 인한 성공 마진을 계산하는 능력이 거의 없음) 결합 할 수있었습니다. 기존 봇 세트를 지배하는 탁월한 능력 / 속성 선택.

용도는 원거리 , 출근을 , 강한 , 그리고 늑대 인간을 달리 같은 AI 로직 사용 롱소드를 약간 변경 있지만.

변경 사항이 없어도 "최고"에서 "최악"으로 떨어질 수 있으므로 가장 이상적인 값을 선택하기가 어렵습니다. 체력 임계 값은 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;
        }
    }
}

몇 가지 문제 (패키지 선언 및 파일 이름을 첫 줄에 넣지 않음)가 있었으며 문제를 해결했습니다. 즉, 정적 클래스가 컴파일러를 지나쳐서 볼 수는 없습니다.
Nathan Merrill

나는 그것을 알아 냈습니다 : 당신은 수입이 누락되었습니다 :import fellowship.characters.EnemyCharacter;
Nathan Merrill

@NathanMerrill 이차 클래스를 Eclipse 외부의 내부 클래스로 결합하려고 시도했습니다. 아마 그게 그렇습니다.
Draco18s

좋은! LongSword에서 내 자신의 이동 기능을 사용했습니다!
Kritixi Lithos

@KritixiLithos 그래, 나는 "ai"부분을 작성하는 데 어려움을 겪고 있었기 때문에 시작점을 갖는 것만으로도 간단하게 잘 작동했습니다. 나는 그들이 더 나은 것을 할 수 있는지 확인하기 위해 그들과 바이올린을하려고 노력했습니다. 그들은 상대방이 뒤에 있더라도 아무도 볼 수 없다면 앞으로 계속 행진 할 것입니다. 차이. 늑대 나 롱소드 맨은 보이지 않는 것에 맞서 싸울 수 없기 때문입니다.
Draco18s

2

레일 벤더

이 봇은 단순히 탈선 자의 버전입니다 세 번째 캐릭터가 첫 번째 캐릭터로 교체 된 . Derailer에 비해 훨씬 나은 결과를 제공합니다.

Derailer를 만들면서 각 캐릭터에게 서로 시너지 효과를 줄 수있는 능력을 부여하고 싶었습니다. HP가 높고 공격력이 높은 캐릭터와 복원 작업이있는 캐릭터를 함께 사용하면 효과가 좋았습니다. 그러나 세 번째 캐릭터가 잘 어울리지 않는 것 같습니다. 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*/

Strong * 2를 사용합니다 . 재생성스턴을 (다음 300 개 진드기 기절 대상)

통계

  • STR : 5 + 40
  • AGI : 5 + 0
  • 지능 : 5 + 0

일체 포함

Noob 코드는 대부분 LongSword에서 가져 왔습니다.

전략

캐릭터가 처음으로 적 캐릭터를 볼 때, 우선 적을 기절시킨 다음 기절 한 적을 슬라이싱하는 것이 우선 순위입니다. 또한 건강과 재생성이 높기 때문에 Noob은 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, 강함 , 버프 , 버프 , 흡수
  • 1 루트 : STR 25, AGI , 5, INT , 5, True Sight , Buff , Buff , Absorb

AI는 엄청나게 간단합니다. 팀과 가장 가까운 적을 찾으면 벽 전체가 단일 적에게 집중됩니다. 사소한 합병증 만 있습니다 : 적들이 보이지 않으면 무작위 구석이나지도 중앙을 향해 걸어가십시오 (따라서 숨어있는 적을 사냥). 적이 손이 닿는 범위 내에 있다면, 우리가 목표로 삼고있는 적이 아니더라도 공격하십시오 (그러나 우리가 목표로 삼고있는 적에게 집중하는 것을 선호하십시오.

팀은 엄청나게 잘합니다. 시뮬레이션에서 이길 수있는 유일한 팀 (작성 당시 존재)은 RogueSquad이며 항상 그런 것은 아닙니다 (때로는 RogueSquad조차도 벽의 힘으로 죽습니다). 무적은 때때로 무승부를 긁습니다.

팀의 성공 이유는 Buff × 2와 Absorb의 결합 때문입니다. 이것은 우리가 STR 1 차 적에게 적중 할 때마다 단기적으로 효과적으로 40 HP를 얻는다는 것을 의미합니다 (도난당한 STR로부터의 재생 증가로 인해 장기적으로 10 HP 만). 우리의 자연 재생은 우리를 물어야합니다.) 그리고 그 위에 12.5 또는 17.5의 자연 재생 속도가 주어지면, 재생과 보조를 맞추기에 충분히 빠른 피해를주는 것은 기본적으로 불가능합니다 (AGI 팀은 잠재적으로 그리고 전술을 실행하지만 아무도 아직 그중 하나를 구축하지 않았습니다). { 업데이트 : 분명히이 콤보는 실제로 작동하지 않지만 (어댑터는 10 HP 만 소모) 어쨌든 팀은 승리합니다.} 한편, 적이 그렇지 않으면STR 기본, 그들은 25 또는 35의 피해를 반복하는 것을 좋아하지 않을 것입니다 (실제로 자신의 턴 중 하나에 집중 될 수 있습니다). 적군이 기본적이지 않고 자신을 방어하기 위해 주문을 사용하는 경우 (Hi Invulnerables!), Absorb는 더 이상 주문을 시전 할 여유가 없을 때까지 MP를 소모합니다. (또한 기본적으로 대부분의 주문에서 두려워 할 것이 없습니다. 재사용 대기 시간이 너무 길어서 피해를 입히기 어렵습니다. 주요 예외는 아직 달리지 않는 함정과 1000 년이나 1400 년까지 낡아가는 독입니다. HP, 그러나 벽이 시전자를 먼저 이길 수없는 경우에는 효과가 있습니다.) 실제 시력은 여전히 ​​보이지 않는 적을 물리 칠 수있는 유일한 능력입니다.

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

다크 흡수기

다크 업소버는 2 명의 형제이며 피해자의 생명력을 흡수합니다.

  • Oracle Absorber (보이지 않는 적을 볼 수 있음)
    • STR : 25; AGI : 5; 지능 : 5
    • TrueSight , 유연성 , 원거리 , 흡수
  • 빠른 흡수기 (오빠보다 훨씬 빨리 흡수 할 수 있음)
    • STR : 25; AGI : 5; 지능 : 5
    • 빠른 , 유연 , 원거리 , 흡수

그들은 항상 증가하는 어둠 구름을 동반합니다. 임계 질량에 도달하면 적을 죽이기 시작합니다.

  • 어둠의 구름
    • STR : 5; AGI : 5; 지능 : 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

이 봇은 정확히 이 강한 대신 TrueSight를 사용하는 것을 제외하고, LongSwordv2과 동일.

보이지 않는 봇의 상승을보고 많은 봇이 감지 할 수 없기 때문에 봇을 꺼내는 데 초점을 맞춘 봇을 만들기로 결정했습니다. 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.