3D : 개별 도그 파이팅 결투 (이제 Java 이외의 제출에 공개)


31

업데이트 : 비행기 클래스에 isSuicidal ()이 추가되었습니다. 비행기가 벽과 돌이킬 수없는 충돌 과정에 있는지 확인할 수 있습니다!

업데이트 : updateCoolDown ()이 simulateMove ()와 분리되었습니다.

업데이트 : Sparr이 작성하고 테스트에 사용할 수있는 Java 이외의 항목 래퍼

UPDATE Zove 게임 작성했습니다 멋진 3D 시각화를 ,이 KOTH 위해 여기 엿의 YouTube 동영상의 PredictAndAVoid 싸움 PredictAndAVoid의는.

Plane 클래스의 SimulateMove () 함수가 약간 수정되어 더 이상 쿨 다운을 업데이트하지 않습니다. 촬영 후 새로운 updateCoolDown () 함수를 사용하십시오. 새로운 isSuicidal ()은 비행기가 죽었을 때 참을 반환하고, 비행기를 사용하여 적의 움직임을 제거하고 벽에 부딪히지 않도록합니다. 업데이트 된 코드를 얻으려면 Controller 및 Plane 클래스를 github 리포지토리의 클래스로 바꾸십시오 .

기술

이 도전의 목표는 다른 참가자가 두 비행기와 대면하는 두 개의 공중전 비행기 를 코딩 하는 것입니다. 매 턴마다 한 칸씩 움직여서 쏠 기회가 있습니다. 그것으로 간단합니다.

거의 ..

경기장과 가능한 움직임

경기장은 공간에 14x14x14 벽으로 둘러싸여 있습니다. 참가자 1의 비행기는 (0,5,0) 및 (0,8,0) 위치에서 시작하고 참가자 2의 비행기는 (13,5,13) 및 (13,8,13)에서 시작합니다. 모든 비행기는 가장 가까운 수직 벽에서 수평으로 비행하여 시작합니다.

이제 헬리콥터가 아닌 비행기를 조종하고 있기 때문에 원하는 방향으로 변경하거나 이동을 멈출 수 없으므로 각 비행기는 방향을 가지며 매 턴마다 그 방향으로 타일 하나를 움직입니다.

가능한 방향은 North (N), South (S), East (E), West (W), Up (U) 및 Down (D) 및 이들 6 개의 논리적 조합입니다. NS 축이 x 축에 해당하는 경우 WE에서 y로, DU에서 z로. NW, SU 및 NED는 가능한 방향의 예를 떠올리게합니다. UD는 잘못된 조합의 좋은 예입니다.

물론 비행기의 방향을 변경할 수 있지만 한계는 최대 45도까지만 변경할 수 있습니다. 이것을 시각화하기 위해 루빅스 큐브를 잡고 (나는 당신이 하나 있음을 알고 있습니다) 26 개의 바깥 쪽 작은 큐브가 모두 가능한 방향이라고 생각하십시오 (한 글자 방향은 얼굴, 두 글자 방향은 가장자리, 세 글자 방향은 모서리). 작은 입방체로 표시되는 방향으로 향하고 있다면, 당신과 접촉하는 각 입방체로 방향을 바꿀 수 있습니다 (대각선으로 만지지 만 눈에 띄게 만져서 입방체를 만지지 않습니다).

모든 평면이 어떤 방향으로 변경하고 싶은지 표시 한 후에는 변경하고 한 타일을 동시에 이동합니다.

방향을 이동 한 방향으로 변경하는 대신 올바른 방향으로 이동하도록 선택했지만 진행 한 방향으로 계속 비행 할 수 있습니다. 이것은 코너를 돌고있는 차와 차가 바뀌는 차선의 차이와 유사합니다.

사격과 사망

당신은 한 라운드에 최대 한 번을 쏠 수 있으며, 어떤 방향으로 비행 할 것인지와 비행기 (및 연장에 의해 총)가 같은 방향을 향하게할지 여부를 결정할 때 동시에 결정해야합니다. 비행기가 움직이 자마자 총알이 쏜다. 촬영 후 한 차례의 쿨 다운이 있습니다. 세 번째 차례에는 다시 가십시오. 날아가는 방향으로 만 쏠 수 있습니다. 총알은 즉석에서 벽이나 비행기에 닿을 때까지 직선으로 날아갑니다.

'차선 변경'뿐만 아니라 방향을 바꿀 수있는 방법을 고려하면 대각선 앞쪽에 최대 3x3 줄의 열을 추가로 위협 할 수 있습니다.

비행기에 부딪히면이 비행기는 죽고 보드에서 즉시 사라집니다 (완전히 폭발하거나 무언가 때문에). 총알은 최대 하나의 비행기에만 맞을 수 있습니다. 총알이 동시에 발사되므로 두 비행기가 서로 쏠 수 있습니다. 두 개의 총알이 공중에서 충돌 할 수는 없습니다 (슬픈, 알아요).

그러나 두 평면이 충돌 할 수 있습니다 (동일한 큐브로 끝나고 동일한 평면에서 끝나지 않고 서로 교차하면 NOT). 벽으로 날아 가면 문제의 비행기가 죽고 그 행동에 대해 생각하기 위해 모퉁이에 놓일 수 있습니다. 충돌은 촬영 전에 처리됩니다.

컨트롤러와의 통신

Java 및 다른 언어로 된 항목을 허용합니다. 입력 항목이 java 인 경우 STDIN을 통해 입력을 받고 STDOUT을 통해 출력합니다.

항목이 java 인 경우 .your 항목은 다음 클래스를 확장해야합니다.

package Planes;

//This is the base class players extend.
//It contains the arena size and 4 plane objects representing the planes in the arena.
public abstract class PlaneControl {

    // note that these planes are just for your information, modifying these doesn't affect the actual plane instances, 
    // which are kept by the controller
    protected Plane[] myPlanes = new Plane[2];
    protected Plane[] enemyPlanes = new Plane[2];
    protected int arenaSize;
    protected int roundsLeft;

    ...

    // Notifies you that a new fight is starting
    // FightsFought tells you how many fights will be fought.
    // the scores tell you how many fights each player has won.
    public void newFight(int fightsFought, int myScore, int enemyScore) {}

    // notifies you that you'll be fighting anew opponent.
    // Fights is the amount of fights that will be fought against this opponent
    public void newOpponent(int fights) {}

    // This will be called once every round, you must return an array of two moves.
    // The move at index 0 will be applied to your plane at index 0,
    // The move at index1 will be applied to your plane at index1.
    // Any further move will be ignored.
    // A missing or invalid move will be treated as flying forward without shooting.
    public abstract Move[] act();
}

해당 클래스로 생성 된 인스턴스는 전체 경쟁 기간 동안 유지되므로 변수에 저장하려는 모든 데이터를 저장할 수 있습니다. 자세한 내용은 코드의 주석을 읽으십시오.

또한 다음 도우미 클래스를 제공했습니다.

package Planes;

//Objects of this class contain all relevant information about a plane
//as well as some helper functions.
public class Plane {
    private Point3D position;
    private Direction direction;
    private int arenaSize;
    private boolean alive = true;
    private int coolDown = 0;

    public Plane(int arenaSize, Direction direction, int x, int y, int z) {}

    public Plane(int arenaSize, Direction direction, Point3D position) {}    

    // Returns the x coordinate of the plane
    public int getX() {}

    // Returns the y coordinate of the plane
    public int getY() {}

    // Returns the z coordinate of the plane
    public int getZ() {}

    // Returns the position as a Point3D.
    public Point3D getPosition() {}

    // Returns the distance between the plane and the specified wall,
    // 0 means right next to it, 19 means at the opposite side.
    // Returns -1 for invalid input.
    public int getDistanceFromWall(char wall) {}

    // Returns the direction of the plane.
    public Direction getDirection() {}

    // Returns all possible turning directions for the plane.
    public Direction[] getPossibleDirections() {}

    // Returns the cool down before the plane will be able to shoot, 
    // 0 means it is ready to shoot this turn.
    public int getCoolDown() {}

    public void setCoolDown(int coolDown) {}

    // Returns true if the plane is ready to shoot
    public boolean canShoot() {}

    // Returns all positions this plane can shoot at (without first making a move).
    public Point3D[] getShootRange() {}

    // Returns all positions this plane can move to within one turn.
    public Point3D[] getRange() {}

    // Returns a plane that represents this plane after making a certain move,
    // not taking into account other planes.
    // Doesn't update cool down, see updateCoolDown() for that.
    public Plane simulateMove(Move move) {}

    // modifies this plane's cool down
    public void updateCoolDown(boolean shot) {
        coolDown = (shot && canShoot())?Controller.COOLDOWN:Math.max(0, coolDown - 1);
    }


    // Returns true if the plane is alive.
    public boolean isAlive() {}

    // Sets alive to the specified value.
    public void setAlive(boolean alive) {}

    // returns a copy of itself.
    public Plane copy() {}

    // Returns a string representing its status.
    public String getAsString() {}

    // Returns a string suitable for passing to a wrapped plane process
    public String getDataString() {}

    // Returns true if a plane is on an irreversable colision course with the wall.
    // Use this along with simulateMove() to avoid hitting walls or prune possible emeny moves.
    public boolean isSuicidal() {}
}


// A helper class for working with directions. 
public class Direction {
    // The three main directions, -1 means the first letter is in the direction, 1 means the second is, 0 means neither is.
    private int NS, WE, DU;

    // Creates a direction from 3 integers.
    public Direction(int NSDir, int WEDir, int DUDir) {}

    // Creates a direction from a directionstring.
    public Direction(String direction) {}

    // Returns this direction as a String.
    public String getAsString() {}

    // Returns The direction projected onto the NS-axis.
    // -1 means heading north.
    public int getNSDir() {}

    // Returns The direction projected onto the WE-axis.
    // -1 means heading west.
    public int getWEDir() {}

    // Returns The direction projected onto the DU-axis.
    // -1 means heading down.
    public int getDUDir() {}

    // Returns a Point3D representing the direction.
    public Point3D getAsPoint3D() {}

    // Returns an array of chars representing the main directions.
    public char[] getMainDirections() {}

    // Returns all possible turning directions.
    public Direction[] getPossibleDirections() {}

    // Returns true if a direction is a valid direction to change to
    public boolean isValidDirection(Direction direction) {}
}

public class Point3D {
    public int x, y, z;

    public Point3D(int x, int y, int z) {}

    // Returns the sum of this Point3D and the one specified in the argument.
    public Point3D add(Point3D point3D) {}

    // Returns the product of this Point3D and a factor.
    public Point3D multiply(int factor) {}

    // Returns true if both Point3D are the same.
    public boolean equals(Point3D point3D) {}

    // Returns true if Point3D is within a 0-based arena of a specified size.
    public boolean isInArena(int size) {}
}


public class Move {
    public Direction direction;
    public boolean changeDirection;
    public boolean shoot;

    public Move(Direction direction, boolean changeDirection, boolean shoot) {}
}

이 클래스의 인스턴스를 만들고 원하는만큼 함수를 사용할 수 있습니다. 이 헬퍼 클래스의 전체 코드는 여기에서 찾을 수 있습니다 .

다음은 귀하의 입장이 어떻게 보이는지에 대한 예입니다 (바람직하게 당신은 내가했던 것보다 더 잘할 것입니다.이 비행기와의 대부분의 경기는 벽을 피하려는 최선의 노력에도 불구하고 벽으로 날아가는 것으로 끝납니다).

package Planes;

public class DumbPlanes extends PlaneControl {

    public DumbPlanes(int arenaSize, int rounds) {
        super(arenaSize, rounds);
    }

    @Override
    public Move[] act() {
        Move[] moves = new Move[2];
        for (int i=0; i<2; i++) {
            if (!myPlanes[i].isAlive()) {
                moves[i] = new Move(new Direction("N"), false, false); // If we're dead we just return something, it doesn't matter anyway.
                continue;
            }
            Direction[] possibleDirections = myPlanes[i].getPossibleDirections(); // Let's see where we can go.

            for (int j=0; j<possibleDirections.length*3; j++) {

                int random = (int) Math.floor((Math.random()*possibleDirections.length)); // We don't want to be predictable, so we pick a random direction out of the possible ones.

                if (myPlanes[i].getPosition().add(possibleDirections[random].getAsPoint3D()).isInArena(arenaSize)) { // We'll try not to fly directly into a wall.
                    moves[i] = new Move(possibleDirections[random], Math.random()>0.5, myPlanes[i].canShoot() && Math.random()>0.2);
                    continue; // I'm happy with this move for this plane.
                }

                // Uh oh.
                random = (int) Math.floor((Math.random()*possibleDirections.length));
                moves[i] = new Move(possibleDirections[random], Math.random()>0.5, myPlanes[i].canShoot() && Math.random()>0.2);
            }
        }

        return moves;
    }

    @Override
    public void newFight(int fightsFought, int myScore, int enemyScore) {
        // Using information is for schmucks.
    }

    @Override
    public void newOpponent(int fights) {
        // What did I just say about information?
    }
}

DumbPlanes는 다른 참가작과 함께 토너먼트에 참가하므로 마지막으로 종료하면 DumbPlanes보다 더 나은 성적을 거두지 못한 것은 본인의 잘못입니다.

제한 사항

KOTH 위키에 언급 된 제한 사항이 적용됩니다.

  • 컨트롤러, 런타임 또는 기타 제출물을 수정하려는 시도는 실격 처리됩니다. 모든 제출물은 제공된 입력 및 저장 장치에서만 작동해야합니다.
  • 봇은 다른 특정 봇을 이길 수 있도록 작성해서는 안됩니다. (드문 경우에 바람직 할 수 있지만 이것이 핵심 도전 과제 개념이 아닌 경우에는 배제하는 것이 좋습니다.)
  • 충분한 양의 리소스를 사용하여 평가판을 실행하기 위해 너무 많은 시간이나 메모리를 사용하는 제출물을 실격 처리 할 권리를 보유합니다.
  • 봇은 의도적으로 또는 실수로 기존 전략과 정확히 동일한 전략을 구현해서는 안됩니다.

제출 테스트

여기 에서 컨트롤러 코드를 다운로드 하십시오 . 제출물을 Something.java로 추가하십시오. 항목 [] 및 이름 []에 비행기 항목을 포함하도록 Controller.java를 수정하십시오. Eclipse 프로젝트 또는로 모든 것을 컴파일 javac -d . *.java한 다음java Planes/Controller . 컨테스트 로그는에 test.txt있으며 스코어 보드는 끝에 있습니다. matchUp()두 개의 항목을 인수로 직접 호출 하여 두 평면을 서로 테스트 할 수도 있습니다 .

싸움에서 이기다

싸움의 승자는 마지막 비행기를 비행 한 사람입니다. 100 턴 후에도 여전히 1 팀 이상 남았을 경우 가장 많은 비행기를 가진 팀이 승리합니다. 이것이 같다면 무승부입니다.

득점과 경쟁

현재 바운티가 소진되면 다음 공식 토너먼트가 진행됩니다.

각 참가작은 모든 다른 참가작 (최소한)에 100 번 싸울 것이며, 각 경기의 승자는 100 점 중 가장 많은 승리를 거둔 사람이며 2 점을받습니다. 추첨의 경우 두 항목 모두 1 점을받습니다.

경쟁의 승자가 가장 많은 점수를 얻은 사람입니다. 추첨의 경우, 우승자는 추첨 된 항목들 사이의 경기에서 승리 한 사람입니다.

출전 횟수에 따라 출전 횟수가 크게 늘어날 수 있습니다. 또한 첫 토너먼트 후 2-4 개의 최고 출전 선수를 선택하고 더 많은 시합을 가진 출전 선수들 사이에서 엘리트 토너먼트를 설정할 수도 있습니다. 싸움)

(예비) 스코어 보드

우리는 또 다른 흥미 진진한 토너먼트 에서 단연 2 위를 차지하는 새로운 참가작 을 얻었습니다. 이 토너먼트는 각 비행기 사이에서 10 번의 시합만으로 진행되었으므로 상황이 어떻게 진행되는지를 완전히 정확하게 나타내는 것은 아닙니다.

----------------------------
¦ 1. PredictAndAvoid:   14 ¦
¦ 2. Crossfire:         11 ¦
¦ 3. Weeeeeeeeeeee:      9 ¦
¦ 4. Whirligig:          8 ¦
¦ 4. MoveAndShootPlane:  8 ¦
¦ 6. StarFox:            4 ¦
¦ 6. EmoFockeWulf:       2 ¦
¦ 7. DumbPlanes:         0 ¦
----------------------------

다음은 비 Java 랩퍼의 출력 예입니다.

NEW CONTEST 14 20 14x14x14 경기장에서 새로운 컨테스트가 시작되고 있으며 전투 당 20 턴이 소요됩니다.

NEW OPPONENT 10 새로운 상대를 상대하고 있으며이 상대와 10 번 싸울 것임을 나타냅니다.

NEW FIGHT 5 3 2 는 현재 상대와의 새로운 싸움이 시작되고 있음을 나타냅니다.이 상대와 지금까지 5 번 싸워 3 번 승리하고 2 번의 전투에서 패배했음을 나타냅니다

ROUNDS LEFT 19 현재 전투에서 19 라운드가 남아 있음을 나타냅니다

NEW TURN 이 전투에서 네 비행기 모두에 대한 데이터를받을 예정임을 나타냅니다.

alive 13 8 13 N 0
alive 13 5 13 N 0
dead 0 0 0 N 0
alive 0 8 0 S 0

이 4 개의 선은 두 평면이 모두 [13,8,13] 및 [13,5,13] 좌표에서 각각 북쪽을 향하고 쿨 다운이 0 인 상태임을 나타냅니다. 첫 번째 적 비행기는 죽었고 두 번째는 [0,8,0]에 살아 있고 남쪽으로 쿨 다운이 없습니다.

이 시점에서 프로그램은 다음과 비슷한 두 줄을 출력해야합니다.

NW 0 1
SU 1 0

이것은 첫 번째 비행기가 현재 방향을 돌리지 않고 가능한 경우 발사하지 않고 노스 웨스트를 여행 함을 나타냅니다. 두 번째 비행기는 사우스 업을 여행하며 사격하지 않고 사우스 업을 향하게됩니다.

이제 당신은 등을 ROUNDS LEFT 18따릅니다 NEW TURN. 이것은 누군가이기거나 라운드 타임 아웃 될 때까지 계속 됩니다 .이 시점 NEW FIGHT에서 업데이트 된 전투 횟수와 점수를 가진 다른 라인 을 얻을 수 NEW OPPONENT있습니다.


누구든지이 도전에 도움이 필요하면이 도전에 대해 내가 만든 채팅에 참여할
overactor

비행기가 동쪽 / 서쪽 또는 북쪽 / 남쪽으로 향합니까? 또는 다른 것?
pseudonym117

2
@overactor 쿨 다운 코드에 버그가 있습니다. "새 위치 계산"섹션에서 시뮬레이션 이동을 사용하면 새 위치를 찾는 것 외에 쿨 다운이 감소합니다. 이는 비행기가 자신의 쿨 다운 카운터를 무시하면 매 턴마다 발사 할 수 있음을 의미합니다.
Sparr

2
유용하다고 생각되는 사람들을 위해이 정규 표현식은 로그를 검색하여 비행기가 촬영 한 위치를 찾습니다. ^ Move (. *?) shoot : true $ ( "Move"를 이름으로 바꾸고. 라인)
user2813274

1
여기에 멍청한 파이썬 비행기와 함께 비행기 래퍼에 대한 커밋이 있습니다. 누군가 perl / python / lua / bash /에 더 똑똑한 비행기를 쓰고 래퍼가 당신을 위해 어떻게 / 어떻게 작동하는지에 대한 피드백을 주면 좋겠습니다. github.com/sparr/Dogfight-KOTH/commit/… 사람들이 이것을 사용할 수 있거나 사용 한다면 @ overactor 's repo로 가져 와서 임의의 언어 제출을 허용 할 수 있습니다.
Sparr

답변:


5

일제 공세

내 첫 아이디어는 동시에 두 비행기로 적 비행기를 쏘는 것이었지만 해결할 수 없었습니다. 그래서 여기 벽에서 멀리 떨어져서 사격장 밖으로 나 가려고 시도하는 비행기가 있습니다. 적. 비행기는 절대로 충돌하거나 우호적 인 비행기를 쏘지 않아야합니다.

편집 : 이 방법은 possibleHits항상 0을 반환했습니다. 고정하고 약간의 개선 사항을 추가하면 이전보다 성능이 향상됩니다.

package Planes;

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

public class Crossfire extends PlaneControl {
    final List<Point3D> dangerList = new ArrayList<>(); //danger per point
    final List<Plane> targets = new ArrayList<>(); //targets being shot
    Plane[] futurePlanes = null; //future friendly planes

    public Crossfire(int arenaSize, int rounds) {
        super(arenaSize, rounds);
    }

    @Override
    public Move[] act() {
        dangerList.clear();     //initialize
        targets.clear();
        final int PLANE_COUNT = myPlanes.length;
        Move[] moves = new Move[PLANE_COUNT];
        futurePlanes = new Plane[PLANE_COUNT];

        // calculate danger per field/enemy
        for (int i = 0; i < PLANE_COUNT; i++) {
            updateDanger(enemyPlanes[i]);
        }   

        // get best moves for each plane
        for (int i = 0; i < PLANE_COUNT; i++) {         
            moves[i] = getBestMove(myPlanes[i]);
            futurePlanes[i] = myPlanes[i].simulateMove(moves[i]);
            updateTargets(futurePlanes[i]);
        }

        // try to shoot if no friendly plane is hit by this bullet
        for (int i = 0; i < myPlanes.length; i++) {
            if (myPlanes[i].canShoot() && canShootSafely(futurePlanes[i]) && possibleHits(futurePlanes[i]) > 0) {
                moves[i].shoot = true;
            }
        }

        return moves;
    }

    private void updateTargets(Plane plane) {
        if (!plane.canShoot() || !canShootSafely(plane)) {
            return;
        }
        Point3D[] range = plane.getShootRange();
        for (Plane enemyPlane : enemyPlanes) {
            for (Move move : getPossibleMoves(enemyPlane)) {
                Plane simPlane = enemyPlane.simulateMove(move);
                for (Point3D dest : range) {
                    if (dest.equals(simPlane.getPosition())) {
                        targets.add(enemyPlane);
                    }
                }
            }           
        }
    }

    private void updateDanger(Plane plane) {
        if (!plane.isAlive()) {
            return;
        }
        for (Move move : getPossibleMoves(plane)) {
            Plane futurePlane = plane.simulateMove(move);
            // add position (avoid collision)
            if (!isOutside(futurePlane)) {
                dangerList.add(futurePlane.getPosition());
                // avoid getting shot
                if (plane.canShoot()) {
                    for (Point3D dest : futurePlane.getShootRange()) {
                        dangerList.add(dest);
                    }
                }
            }
        }
    }

    private Move getBestMove(Plane plane) {
        if (!plane.isAlive()) {
            return new Move(new Direction("N"), false, false);
        }

        int leastDanger = Integer.MAX_VALUE;
        Move bestMove = new Move(new Direction("N"), false, false);
        for (Move move : getPossibleMoves(plane)) {
            Plane futurePlane = plane.simulateMove(move);
            int danger = getDanger(futurePlane) - (possibleHits(futurePlane) *2);
            if (danger < leastDanger) {
                leastDanger = danger;
                bestMove = move;
            }
        }
        return bestMove;
    }

    private int getDanger(Plane plane) {
        if (!plane.isAlive() || hugsWall(plane) || collidesWithFriend(plane) || isOutside(plane)) {
            return Integer.MAX_VALUE - 1;
        }
        int danger = 0;
        Point3D pos = plane.getPosition();
        for (Point3D dangerPoint : dangerList) {
            if (pos.equals(dangerPoint)) {
                danger++;
            }
        }
        // stay away from walls
        for (char direction : plane.getDirection().getMainDirections()) {
            if (plane.getDistanceFromWall(direction) <= 2) {
                danger++;
            }
        }
        return danger;
    }

    private boolean collidesWithFriend(Plane plane) {
        for (Plane friendlyPlane : futurePlanes) {
            if (friendlyPlane != null && plane.getPosition().equals(friendlyPlane.getPosition())) {
                return true;
            }
        }
        return false;
    }

    private boolean hugsWall(Plane plane) {
        if (!plane.isAlive() || isOutside(plane)) {
            return true;
        }
        char[] mainDirs = plane.getDirection().getMainDirections();
        if (mainDirs.length == 1) {
            return plane.getDistanceFromWall(mainDirs[0]) == 0;
        }
        if (mainDirs.length == 2) {
            return plane.getDistanceFromWall(mainDirs[0]) <= 1
                    && plane.getDistanceFromWall(mainDirs[1]) <= 1;
        }
        if (mainDirs.length == 3) {
            return plane.getDistanceFromWall(mainDirs[0]) <= 1
                    && plane.getDistanceFromWall(mainDirs[1]) <= 1
                    && plane.getDistanceFromWall(mainDirs[2]) <= 1;
        }
        return false;
    }

    private Set<Move> getPossibleMoves(Plane plane) {
        Set<Move> possibleMoves = new HashSet<>();
        for (Direction direction : plane.getPossibleDirections()) {
            possibleMoves.add(new Move(direction, false, false));
            possibleMoves.add(new Move(direction, true, false));
        }
        return possibleMoves;
    }

    private boolean canShootSafely(Plane plane) {
        if (!plane.canShoot() || isOutside(plane)) {
            return false;
        }
        for (Point3D destPoint : plane.getShootRange()) {
            for (Plane friendlyPlane : futurePlanes) {
                if (friendlyPlane == null) {
                    continue;
                }
                if (friendlyPlane.isAlive() && friendlyPlane.getPosition().equals(destPoint)) {
                    return false;
                }
            }
        }
        return true;
    }

    private int possibleHits(Plane plane) {
        if (!plane.canShoot() || !canShootSafely(plane)) {
            return 0;
        }
        int possibleHits = 0;
        Point3D[] range = plane.getShootRange();
        for (Plane enemyPlane : enemyPlanes) {
            for (Move move : getPossibleMoves(enemyPlane)) {
                Plane simPlane = enemyPlane.simulateMove(move);
                for (Point3D dest : range) {
                    if (dest.equals(simPlane.getPosition())) {
                        possibleHits++;
                    }
                }
            }           
        }
        return possibleHits;
    }

    private boolean isOutside(Plane plane) {
        return !plane.getPosition().isInArena(arenaSize);
    }
}

1
당신은 현재 PredictAndAvoid 다음으로 두 번째로 좋은 항목입니다. 당신은 다른 모든 항목에 대해 승리하지만, 꽤 많이 그립니다. Whirligig는 PredictAndAvoid에 대해 당신보다 더 많은 승리와 무승부를 시행합니다. 상관없이 좋은 입장!
overactor

1
@overactor 입력 해 주셔서 감사합니다! 그것은 내가 총격 사건에서 일해야한다는 것을 의미합니다.
CommonGuy

1
방금 좀 더 테스트를했는데 PredictAndAvoid보다 Whirligig보다 패배가 적은 것 같습니다. PredictAndAvoid는 더 많은 승리를 관리하지만 여기에는 2000 건의 시합에 대한 데이터가 있습니다. PredictAndAvoid : 1564 크로스 파이어 : 125 | Whirligig : 25 크로스 파이어 : 600
오버 액터

@overactor 제출을 개선 할 시간을 찾았습니다. 이제는 때때로 PredictAndAvoid에 대해 이기고, 뽑고 풉니 다.
CommonGuy

1
10,000 번의 싸움 후 : SCORE : PredictAndAvoid : 1240 Crossfire : 6567
overactor 10

20
/*
    PREDICT AND AVOID

    Rules of behavior:
    - Avoid hitting walls
    - Move, safely, to shoot at spaces our enemy might fly to
    - (contingent) Move to a safe space that aims closer to the enemy
    - Move to a safe space
    - Move, unsafely, to shoot at spaces our enemy might fly to
    - Move to any space (remember to avoid walls)

    Chooses randomly between equally prioritized moves

    contingent strategy is evaluated during early fights
*/

package Planes;

import java.util.Random;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;


public class PredictAndAvoid extends PlaneControl {

    public PredictAndAvoid(int arenaSize, int rounds) {
        super(arenaSize, rounds);
    }


    private int fightsPerMatch = 0;
    private int fightNum = 0;
    private int roundNum = 0;
    private boolean useHoming = true;
    private int homingScore = 0;
    private int[][][] enemyHistory = new int[arenaSize][arenaSize][arenaSize];

    // don't need to take roots here, waste of cpu cycles
    int distanceCubed(Point3D a, Point3D b) {
        return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) + (a.z-b.z)*(a.z-b.z);
    }

    // is this plane guaranteed to hit a wall, now or soon?
    boolean dangerZone(Plane icarus) {
        // outside the arena?
        // already dead
        // this should never happen for my planes
        if (!icarus.getPosition().isInArena(arenaSize)) {
            return true;
        }
        // adjacent to a wall?
        // directly facing the wall?
        // death next turn
        if (
            icarus.getDirection().getMainDirections().length==1 &&
            icarus.getDistanceFromWall(icarus.getDirection().getMainDirections()[0]) == 0
        ) {
                return true;
        }
        // on an edge?
        // 2d diagonal facing into that edge?
        // death next turn
        if (
            icarus.getDirection().getMainDirections().length==2 &&
            icarus.getDistanceFromWall(icarus.getDirection().getMainDirections()[0]) == 0 &&
            icarus.getDistanceFromWall(icarus.getDirection().getMainDirections()[1]) == 0
        ) {
                return true;
        }
        // near a corner?
        // 3d diagonal facing into that corner?
        // death in 1-2 turns
        if (
            icarus.getDirection().getMainDirections().length==3 &&
            icarus.getDistanceFromWall(icarus.getDirection().getMainDirections()[0]) < 2 &&
            icarus.getDistanceFromWall(icarus.getDirection().getMainDirections()[1]) < 2 &&
            icarus.getDistanceFromWall(icarus.getDirection().getMainDirections()[2]) < 2
        ) {
                return true;
        }
        // there's at least one way out of this position
        return false;
    }

    @Override
    public Move[] act() {
        Move[] moves = new Move[2];

        for (int i=0; i<2; i++) {
            Plane p = myPlanes[i];
            if (!p.isAlive()) {
                moves[i] = new Move(new Direction("N"), false, false); // If we're dead we just return something, it doesn't matter anyway.
                continue;
            }

            // a list of every move that doesn't commit us to running into a wall
            // or a collision with the previously moved friendly plane
            ArrayList<Move> potentialMoves = new ArrayList<Move>();
            for (Direction candidateDirection : p.getPossibleDirections()) {
                if (i==1 && myPlanes[0].simulateMove(moves[0]).getPosition().equals(myPlanes[1].simulateMove(new Move(candidateDirection,false,false)).getPosition())) {

                } else {                
                    Plane future = new Plane(arenaSize, 0, p.getDirection(), p.getPosition().add(candidateDirection.getAsPoint3D())); 
                    if (!dangerZone(future)) {
                                potentialMoves.add(new Move(candidateDirection, false, false));
                    }
                    future = new Plane(arenaSize, 0, candidateDirection, p.getPosition().add(candidateDirection.getAsPoint3D())); 
                    if (!dangerZone(future)) {
                            potentialMoves.add(new Move(candidateDirection, true, false));
                    }
                }
            }

            // everywhere our enemies might end up
            // including both directions they could be facing for each location
            ArrayList<Plane> futureEnemies = new ArrayList<Plane>();
            for (Plane e : enemyPlanes) {
                if (e.isAlive()) {
                    for (Direction candidateDirection : e.getPossibleDirections()) {
                        futureEnemies.add(new Plane(
                            arenaSize, 
                            e.getCoolDown(), 
                            candidateDirection, 
                            e.getPosition().add(candidateDirection.getAsPoint3D())
                            ));
                        // don't make a duplicate entry for forward moves
                        if (!candidateDirection.getAsPoint3D().equals(e.getDirection().getAsPoint3D())) {
                            futureEnemies.add(new Plane(
                                arenaSize, 
                                e.getCoolDown(), 
                                e.getDirection(), 
                                e.getPosition().add(candidateDirection.getAsPoint3D())
                                ));
                        }
                    }
                }
            }

            // a list of moves that are out of enemies' potential line of fire
            // also skipping potential collisions unless we are ahead on planes
            ArrayList<Move> safeMoves = new ArrayList<Move>();
            for (Move candidateMove : potentialMoves) {
                boolean safe = true;
                Point3D future = p.simulateMove(candidateMove).getPosition();
                for (Plane ec : futureEnemies) {
                    if (ec.getPosition().equals(future)) {
                        if (
                            (myPlanes[0].isAlive()?1:0) + (myPlanes[1].isAlive()?1:0)
                            <= 
                            (enemyPlanes[0].isAlive()?1:0) + (enemyPlanes[1].isAlive()?1:0)
                        ) {
                            safe = false;
                            break;
                        }
                    }
                    if (ec.isAlive() && ec.canShoot()) {
                        Point3D[] range = ec.getShootRange();
                        for (Point3D t : range) {
                            if (future.equals(t)) {
                                safe = false;
                                break;
                            }
                        }
                        if (safe == false) {
                            break;
                        }
                    }
                }
                if (safe == true) {
                    safeMoves.add(candidateMove);
                }
            }

            // a list of moves that let us attack a space an enemy might be in
            // ignore enemies committed to suicide vs a wall
            // TODO: don't shoot at friendly planes
            ArrayList<Move> attackMoves = new ArrayList<Move>();
            for (Move candidateMove : potentialMoves) {
                int attackCount = 0;
                Plane future = p.simulateMove(candidateMove);
                Point3D[] range = future.getShootRange();
                for (Plane ec : futureEnemies) {
                    for (Point3D t : range) {
                        if (ec.getPosition().equals(t)) {
                            if (!dangerZone(ec)) {
                                    attackMoves.add(new Move(candidateMove.direction, candidateMove.changeDirection, true));
                                    attackCount++;
                            }
                        }
                    }
                }
                if (attackCount > 0) {

                }
            }

            // find all attack moves that are also safe moves
            ArrayList<Move> safeAttackMoves = new ArrayList<Move>();
            for (Move safeCandidate : safeMoves) {
                for (Move attackCandidate : attackMoves) {
                    if (safeCandidate.direction == attackCandidate.direction) {
                        safeAttackMoves.add(attackCandidate);
                    }
                }
            }

            // choose the safe move that aims closest potential enemy positions
            int maxDistanceCubed = arenaSize*arenaSize*arenaSize*8;
            Move homingMove = null;
            int bestHomingMoveTotalDistancesCubed = maxDistanceCubed*1000;
            for (Move candidateMove : safeMoves) {
                int totalCandidateDistancesCubed = 0;
                for (Plane ec : futureEnemies) {
                    if (ec.isAlive()) {
                        int distThisEnemyCubed = maxDistanceCubed;
                        Point3D[] range = p.simulateMove(candidateMove).getShootRange();
                        for (Point3D t : range) {
                            int d1 = distanceCubed(t, ec.getPosition());
                            if (d1 < distThisEnemyCubed) {
                                distThisEnemyCubed = d1;
                            }
                        }
                        totalCandidateDistancesCubed += distThisEnemyCubed;
                    }
                }
                if (totalCandidateDistancesCubed < bestHomingMoveTotalDistancesCubed) {
                    bestHomingMoveTotalDistancesCubed = totalCandidateDistancesCubed;
                    homingMove = candidateMove;
                }
            }

            Random rng = new Random();
            // move to attack safely if possible
            // even if we can't shoot, this is good for chasing enemies
            if (safeAttackMoves.size() > 0) {
                moves[i] = safeAttackMoves.get(rng.nextInt(safeAttackMoves.size()));
                }
            // turn towards enemies if it's possible and safe
            // tests indicate value of this strategy varies significantly by opponent
            // useHoming changes based on outcome of early fights with[out] it
            // TODO: track enemy movement, aim for neighborhood
            else if (useHoming == true && homingMove != null) {
                moves[i] = homingMove;
                }
            // make random move, safe from attack
            else if (safeMoves.size() > 0) {
                moves[i] = safeMoves.get(rng.nextInt(safeMoves.size()));
                }
            // move to attack unsafely only if there are no safe moves
            else if (attackMoves.size() > 0 && p.canShoot()) {
                moves[i] = attackMoves.get(rng.nextInt(attackMoves.size()));
                }
            // make random move, safe from walls
            else if (potentialMoves.size() > 0) {
                moves[i] = potentialMoves.get(rng.nextInt(potentialMoves.size()));
                }
            // keep moving forward
            // this should never happen
            else {
                moves[i] = new Move(p.getDirection(), false, true);
                }
        }
        roundNum++;
        return moves;
    }

    @Override
    public void newFight(int fightsFought, int myScore, int enemyScore) {
        // try the homing strategy for 1/8 of the match
        // skip it for 1/8, then choose the winning option
        if (fightsFought == fightsPerMatch/8) {
            homingScore = myScore-enemyScore;
            useHoming = false;
        } else if (fightsFought == (fightsPerMatch/8)*2) {
            if (homingScore*2 > myScore-enemyScore) {
                useHoming = true;
            }
        }
        fightNum = fightsFought;
        roundNum = 0;
    }

    @Override
    public void newOpponent(int fights) {
        fightsPerMatch = fights;
    }
}

1
현재 거의 매번 Whirligig를 이깁니다. 적 사격 방지 코드에서 버그를 추적해야합니다.
Sparr

1
버그를 수정했습니다. 현재 상대에게 0 손실.
Sparr

2
무승부 감소, 상당한 진전을 위해 노력하고 있습니다. 훨씬 더 진보하기 전에 더 똑똑한 적 비행기가 필요합니다.
Sparr

1
안전하지 않은 촬영 위의 안전한 공간으로 이동하는 것을 우선시합니다
Cruncher

1
@Cruncher는 이미 로컬 사본 에서이 작업을 수행하고 있으며 현재 상대에 비해 성능이 약간 % 향상되었습니다. 또한 살아남은 비행기에 앞서 있지 않을 때 충돌을 피할 수 있습니다. 올 업데이트!
Sparr

18

공중전 3D 시각화

이 과제에 대해 작고 빠른 시각화 도구를 작성했습니다. 코드 및 jar 파일은 내 github 저장소에 있습니다 : https://github.com/Hungary-Dude/DogfightVisualizer
libGDX ( http://libgdx.com )를 사용하여 만들어졌습니다 . 지금은 UI가 꽤 나쁩니다.

나는 Git과 Gradle을 사용하는 방법을 배우기 때문에 내가 잘못했다면 의견을 말하십시오.

실행 dist/dogfight.bat하거나 dist/dogfight.shDumbPlanes가 작동하는지 확인하십시오!

소스에서 빌드하려면 IDE에 Gradle ( http://gradle.org ) 및 Gradle 통합이 필요합니다 (있는 경우). 그런 다음 repo를 복제하고 실행하십시오 gradlew desktop:run. Gradle이 필요한 모든 라이브러리를 가져 오기를 바랍니다. 주요 수업은 zove.koth.dogfight.desktop.DesktopLauncher입니다.

가져 오지 않고 실행

비행기 클래스 파일을에 복사하십시오 dist/. 그런 다음 dist/desktop-1.0.jar이 명령으로 실행하십시오.

java -cp your-class-folder/;desktop-1.0.jar;Planes.jar zove.koth.dogfight.desktop.DesktopLauncher package.YourPlaneController1 package.YourPlaneController2 ...

Planes 컨트롤러의 소스가 업데이트되면 업데이트하지만 직접 업데이트하려면에 코드를 추가해야합니다 Planes.Controller. 이에 대한 정보는 github readme를 참조하십시오.

스크린 샷은 다음과 같습니다. 스크린 샷

질문이나 제안 사항이 있으면 아래에 의견을 남겨주세요!


비행기 클래스를 추가 한 프로젝트를 만들었습니다. 이제이 비행기로 비주얼 라이저를 어떻게 실행합니까? 어쩌면 이것은 채팅 에서 더 잘 설명 될 것 입니다. 제안으로, 최소한 일치하는 로그를 붙여 넣은 다음 해당 일치를 단계별로 진행할 수 있다면 좋을 것입니다. 또한 나는 당신이 cooridates를 쥐었을 수도 있다고 생각합니다. 비행기는 바닥과 천장에서 각각 시작해야합니다. 그래도 놀라운 일 !!
overactor

평면 위치를 나타내는 Point3D를 가져 와서 각 좌표에서 6.5를 빼면 뷰로 이동합니다. 같은 것plane.transform.setToTranslation(new Vector3(point3d.x-6.5f,point3d.y-6.5f,point3d.z-6.5f))비행기가없는 것 것이 경계를 벗어나는 것처럼 보이므로 뭔가 잘못되었다고 의심합니다.
DankMemes

아 잠깐, y 축을 높이로 사용하고 있습니까? (내가 생각하는 대부분의 게임에서와 같이) 내 시스템에서 z는 높이이기 때문에 대칭 적이기 때문에 중요하지 않습니다.
overactor

아 그렇습니다. 실제로 코드를 많이 보지 못했습니다. 방금 Point3D를 libgdx Vector3로 직접 변환했습니다. 그건 그렇고, 나는 내일부터 일주일 정도 떠나있을 것입니다. 당신이 뭔가 필요하면 내가 여기에 없다면 죄송합니다. 멀리있는 동안 체크인하려고합니다.
DankMemes

12

이모 폭스 울프

그가 돌아왔다. 그는 자신을 224 바이트로 굶었습니다. 그는 어떻게 이런 일이 일어 났는지 모른다.

package Planes;public class EmoFockeWulf extends PlaneControl{public EmoFockeWulf(int s, int r){super(s,r);}public Move[] act(){Move[] m=new Move[2];m[0]=new Move(myPlanes[0].getDirection(),false,false);m[1]=m[0];return m;}}

13
이것은 지금 심각하게 손을 떼고 있습니다. 우리는 표준 허점에 영원히 그를 추방하는 것은 어떻습니까?
user80551

13
@ user80551 나는 그것이 유효한 플레이 스타일이라고 생각한다. 추방 할 이유가 없습니다.
Seiyria

3
그는 자신보다 47 바이트 더 뚱뚱하다!
johnchen902

2
그는 이것보다 더 빨리 자살 할 수있었습니다. 매우 효과적으로 이모가 아닙니다.
Sparr

2
@Sparr 예, 그러나 더 높은 바이트 수를 가질 것이고 항상지는 것이 아니라는 아이러니를 잃을 것입니다. : P
cjfaure

10

Weeeeeeeeeeee - 공백을 제거한 후 344 바이트

굉장한 루프 n 물건을 않습니다. 루프를하고 있다면 잃을 수 없습니다.

package Planes;
public class W extends PlaneControl{
    int i,c;
    int[] s={1,1,1,0,-1,-1,-1,0};
    public W(int a,int r){
        super(a,r);
    }
    public void newFight(int a,int b,int c){
        i=4;
    }
    public Move[] act(){
        Plane p=myPlanes[0];
        if(++i<6)
            c=p.getX()==0?1:-1;
        Move n=new Move(i<8?p.getDirection():new Direction(c*s[(i+2)%8],0,c*s[i%8]),0<1,i%2<1);
        Move[] m={n,n};
        return m;
    }
}

편집 : 분명히 내 비행기가 팀 2로 시작했을 때 즉시 벽에 충돌했습니다. 나는 지금 그것을 고쳤다 고 생각한다. 잘만되면


귀하의 반환 진술은 합법적이지 않습니다. 자바에서 사용할 필요가 한 줄에 모든 내용을 지정하는 객체의 배열을 만들 수 있습니다 new Type[]{item1, item2, ...}당신이 가진 것이이 경우에 이렇게return new Move[]{new Move(d,z,a),new Move(d,z,a^=z)};
DankMemes

IDE를 다운로드하지 않은 경우 browxy.com을 사용해보십시오 . (그것은 전혀 강력하지는 않지만 작동합니다)
DankMemes

고마워, 나는 그것이 효과가 있는지 잊었다. 모든 상속 및 패키지 작업을 위해 클래스를 다운로드하고 싶지 않았습니다.
pseudonym117

새 코드로 비행기를 실행 한 후에는 S와 SU 만 반환하고 매 15 번째 라운드에서 죽습니다. 왜 그런지 알아?
overactor

흠 ... 분명히 나는 ​​내 변화를 망쳤다. 실제로 작동하기를 바랐습니다. 편집을 취소 할 것입니다.
pseudonym117

6

이동 및 촬영 비행기

벽이 벽에 가까워 졌을 때를 찾아 벽을 피하고 가능하면 쏴줍니다.

    package Planes;

public class MoveAndShootPlane extends PlaneControl {

    public MoveAndShootPlane(int arenaSize, int rounds) {
        super(arenaSize, rounds);
    }

    @Override
    public Move[] act() {
        Move[] moves = new Move[2];

        for (int i=0; i<2; i++) {
            if (!myPlanes[i].isAlive()) {
                moves[i] = new Move(new Direction("N"), false, false); // If we're dead we just return something, it doesn't matter anyway.
                continue;
            }
            // What direction am I going again?
            Direction currentDirection = myPlanes[i].getDirection();

            // Is my plane able to shoot?
            boolean canIShoot = myPlanes[i].canShoot();

            // if a wall is near me, turn around, otherwise continue along
            if (myPlanes[i].getDirection().getAsString().equals("N") && myPlanes[i].getDistanceFromWall('N') <= 2) {
                if (myPlanes[i].getDistanceFromWall('U') > myPlanes[i].getDistanceFromWall('D')) {
                    moves[i] = new Move(new Direction("NU"), true, canIShoot);
                } else {
                    moves[i] = new Move(new Direction("ND"), true, canIShoot);
                } 
            } else if (myPlanes[i].getDirection().getAsString().equals("S") && myPlanes[i].getDistanceFromWall('S') <= 2) {
                if (myPlanes[i].getDistanceFromWall('U') > myPlanes[i].getDistanceFromWall('D')) {
                    moves[i] = new Move(new Direction("SU"), true, canIShoot);
                } else {
                    moves[i] = new Move(new Direction("SD"), true, canIShoot);
                } 
            } else {
                if (myPlanes[i].getDirection().getAsString().equals("N") || myPlanes[i].getDirection().getAsString().equals("S")) {             
                    moves[i] = new Move(currentDirection, false, canIShoot);
                } else if (myPlanes[i].getDistanceFromWall('N') < myPlanes[i].getDistanceFromWall('S')) {
                    if (myPlanes[i].getDirection().getAsString().equals("NU")) {
                        moves[i] = new Move(new Direction("U"), true, canIShoot);
                    } else if (myPlanes[i].getDirection().getAsString().equals("U")) {
                        moves[i] = new Move(new Direction("SU"), true, canIShoot);
                    } else if (myPlanes[i].getDirection().getAsString().equals("SU")) {
                        moves[i] = new Move(new Direction("S"), true, canIShoot);
                    } else if (myPlanes[i].getDirection().getAsString().equals("ND")) {
                        moves[i] = new Move(new Direction("D"), true, canIShoot);
                    } else if (myPlanes[i].getDirection().getAsString().equals("D")) {
                        moves[i] = new Move(new Direction("SD"), true, canIShoot);
                    } else if (myPlanes[i].getDirection().getAsString().equals("SD")) {
                        moves[i] = new Move(new Direction("S"), true, canIShoot);
                    }
                } else {
                    if (myPlanes[i].getDirection().getAsString().equals("SU")) {
                        moves[i] = new Move(new Direction("U"), true, canIShoot);
                    } else if (myPlanes[i].getDirection().getAsString().equals("U")) {
                        moves[i] = new Move(new Direction("NU"), true, canIShoot);
                    } else if (myPlanes[i].getDirection().getAsString().equals("NU")) {
                        moves[i] = new Move(new Direction("N"), true, canIShoot);
                    } else if (myPlanes[i].getDirection().getAsString().equals("SD")) {
                        moves[i] = new Move(new Direction("D"), true, canIShoot);
                    } else if (myPlanes[i].getDirection().getAsString().equals("D")) {
                        moves[i] = new Move(new Direction("ND"), true, canIShoot);
                    } else if (myPlanes[i].getDirection().getAsString().equals("ND")) {
                        moves[i] = new Move(new Direction("N"), true, canIShoot);
                    }
                }
            }
        }
        return moves;
    }

    @Override
    public void newFight(int fightsFought, int myScore, int enemyScore) {
        // Using information is for schmucks.
    }

    @Override
    public void newOpponent(int fights) {
        // What did I just say about information?
    }
}     

면책 조항 : 나는 전혀 Java 프로그래머가 아니므로 아무것도 망쳤 으면 나를 위해 고쳐주세요!


아직 테스트하지는 않았지만 작동하지 않습니다. 한 번에 180도 회전하려고합니다. 힌트로, N-> S 대신 N-> NU-> U-> SU-> S를 시도하거나 지붕이 바닥보다 가까운 경우 U를 D로 바꾸십시오.
overactor

@ overactor : 나는 you may only change your angle by 45 degrees비트를 놓쳤다 .
Kyle Kanos

문제 없음, 너무 고쳐서는 안됩니다.
overactor

Hashtable 대신 HashMap <String>을 사용해야합니다. 그렇지 않으면 new Direction(wayToGo.get(currentDirection))String으로 캐스팅하는 것을 잊어 버려 작동하지 않습니다. 필드 뒤의 wayToGo.put도 유효하지 않습니다. {wayToGo.put (blah); blah;} 블록 또는 생성자에 입력하십시오.
Luna

7
지금은 벽으로 날지 않기 때문에 모든 것을 이깁니다.
오버 액터

6

lig 리 히그

두 비행기 모두 중앙으로 향한 다음 가능한 자주 촬영하는 동안 반복합니다. 전투 당 ​​3 개의 축 중 하나가 선택되며 쌍은 항상 같은 축을 중심으로 반대 방향으로 회전합니다.

package Planes;

public class Whirligig extends PlaneControl{

    public Whirligig(int arenaSize, int rounds) {
        super(arenaSize, rounds);
        cycle = -1;
    }

    int cycle;
    String[][] cycles = {
            {"E","EU","U","WU","W","WD","D","ED"},
            {"N","NU","U","SU","S","SD","D","ND"},
            {"S","SW","W","NW","N","NE","E","SE"},
            {"ED","D","WD","W","WU","U","EU","E"},
            {"ND","D","SD","S","SU","U","NU","N"},
            {"SE","E","NE","N","NW","W","SW","S"},
    };

    private Move act(int idx){
        Plane plane = myPlanes[idx];
        Move move = new Move(plane.getDirection(), true, plane.canShoot());
        if(!plane.isAlive())
            return new Move(new Direction("N"), false, false);

        if(cycle < 0){
            if(idx == 0 && (myPlanes[1].getZ() == 0 || myPlanes[1].getZ() == 13)){
                return move;
            }
            if(distanceToCenter(plane.getPosition()) > 2){
                move.direction = initialMove(plane);
            } else {
                cycle = (int)(Math.random()*3);
            }
        } else {
            move.direction = continueCycle(plane, cycle + (idx*3));
        }
        return move;
    }

    private Direction initialMove(Plane plane){
        if(plane.getDirection().getNSDir() > 0)
            return new Direction("SU");
        else
            return new Direction("ND");
    }

    private Direction continueCycle(Plane plane, int pathIndex){
        Direction current = plane.getDirection();
        String[] path = cycles[pathIndex];
        for(int i=0;i<path.length;i++)
            if(path[i].equals(current.getAsString()))
                return new Direction(path[(i+1)%path.length]);

        Direction[] possible = plane.getPossibleDirections();
        int step = (int)(Math.random()*path.length);
        for(int i=0;i<path.length;i++){
            for(int j=0;j<possible.length;j++){
                if(path[(i+step)%path.length].equals(possible[j].getAsString()))
                    return new Direction(path[(i+step)%path.length]);
            }
        }       
        return plane.getDirection();
    }

    private int distanceToCenter(Point3D pos){
        int x = (int)Math.abs(pos.x - 6.5); 
        int y = (int)Math.abs(pos.y - 6.5); 
        int z = (int)Math.abs(pos.z - 6.5);
        return Math.max(x, Math.max(y,z));
    }

    @Override
    public Move[] act() {
        Move[] moves = new Move[2];
        for(int i=0;i<2;i++){
            moves[i] = act(i);
        }
        return moves;
    }

    @Override
    public void newFight(int fought, int wins, int losses){
        cycle = -1;
    }

    @Override
    public void newOpponent(int fights){
        cycle = -1;
    }

}

4

멍청한 비행기

DumbPlanes는 벽으로 날지 않기 위해 너무 열심히 노력하지만, 그것에 대해별로 똑똑하지 않으며 보통 어쨌든 벽에 부딪칩니다. 또한 자신이 무엇을 촬영하고 있는지 아는 경우에도 가끔 촬영합니다.

package Planes;

public class DumbPlanes extends PlaneControl {

    public DumbPlanes(int arenaSize, int rounds) {
        super(arenaSize, rounds);
    }

    @Override
    public Move[] act() {
        Move[] moves = new Move[2];
        for (int i=0; i<2; i++) {
            if (!myPlanes[i].isAlive()) {
                moves[i] = new Move(new Direction("N"), false, false); // If we're dead we just return something, it doesn't matter anyway.
                continue;
            }
            Direction[] possibleDirections = myPlanes[i].getPossibleDirections(); // Let's see where we can go.

            for (int j=0; j<possibleDirections.length*3; j++) {

                int random = (int) Math.floor((Math.random()*possibleDirections.length)); // We don't want to be predictable, so we pick a random direction out of the possible ones.

                if (myPlanes[i].getPosition().add(possibleDirections[random].getAsPoint3D()).isInArena(arenaSize)) { // We'll try not to fly directly into a wall.
                    moves[i] = new Move(possibleDirections[random], Math.random()>0.5, myPlanes[i].canShoot() && Math.random()>0.2);
                    continue; // I'm happy with this move for this plane.
                }

                // Uh oh.
                random = (int) Math.floor((Math.random()*possibleDirections.length));
                moves[i] = new Move(possibleDirections[random], Math.random()>0.5, myPlanes[i].canShoot() && Math.random()>0.2);
            }
        }

        return moves;
    }

    @Override
    public void newFight(int fightsFought, int myScore, int enemyScore) {
        // Using information is for schmucks.
    }

    @Override
    public void newOpponent(int fights) {
        // What did I just say about information?
    }
}

4

Starfox (WIP-아직 작동하지 않음) :

실제로 사용 가능한 모든 동작을 사용하지는 않습니다. 그러나 그는 적을 격추하려하고 벽에 부딪치지 않습니다.

package Planes;

import java.util.ArrayList;
import java.util.function.Predicate;

public class Starfox extends PlaneControl
{

    public Starfox(int arenaSize, int rounds)
    {
        super(arenaSize, rounds);
    }

    private ArrayList<Point3D> dangerousPositions;
    private ArrayList<Point3D> riskyPositions;

    @Override
    public Move[] act()
    {
        dangerousPositions = new ArrayList<>();
        riskyPositions = new ArrayList<>();

        // add corners as places to be avoided
        dangerousPositions.add(new Point3D(0,0,0));
        dangerousPositions.add(new Point3D(0,0,arenaSize-1));
        dangerousPositions.add(new Point3D(0,arenaSize-1,0));
        dangerousPositions.add(new Point3D(0,arenaSize-1,arenaSize-1));
        dangerousPositions.add(new Point3D(arenaSize-1,0,0));
        dangerousPositions.add(new Point3D(arenaSize-1,0,arenaSize-1));
        dangerousPositions.add(new Point3D(arenaSize-1,arenaSize-1,0));
        dangerousPositions.add(new Point3D(arenaSize-1,arenaSize-1,arenaSize-1));


        for (Plane p : super.enemyPlanes)
        {
            for (Direction d : p.getPossibleDirections())
            {
                Point3D potentialPosition = new Point3D(p.getX(), p.getY(), p.getZ()).add(d.getAsPoint3D());
                if (potentialPosition.isInArena(arenaSize))
                {
                    riskyPositions.add(potentialPosition);
                    if (p.canShoot())
                    {
                        for (Point3D range : p.getShootRange())
                        {
                            riskyPositions.add(range.add(potentialPosition));
                        }
                    }
                }
            }
        }

        ArrayList<Move> moves = new ArrayList<>();

        for (Plane p : myPlanes)
        {
            if (p.isAlive())
            {
                ArrayList<Direction> potentialDirections = new ArrayList<>();

                for (Direction d : p.getPossibleDirections())
                {
                    Point3D potentialPosition = new Point3D(p.getX(), p.getY(), p.getZ()).add(d.getAsPoint3D());
                    if (potentialPosition.isInArena(arenaSize))
                    {
                        potentialDirections.add(d);
                    }
                }

                // remove dangerous positions from flight plan
                potentialDirections.removeIf(new Predicate<Direction>()
                {
                    @Override
                    public boolean test(Direction test)
                    {
                        boolean result = false;
                        for (Point3D compare : dangerousPositions)
                        {
                            if (p.getPosition().add(test.getAsPoint3D()).equals(compare))
                            {
                                result = true;
                            }
                        }
                        return result && potentialDirections.size() > 0;
                    }
                });

                // remove positions with no future from flight plan

                potentialDirections.removeIf(new Predicate<Direction>()
                {
                    @Override
                    public boolean test(Direction test)
                    {
                        boolean hasFuture = false;
                        for (Direction compare : p.getPossibleDirections())
                        {
                            Plane future = new Plane(arenaSize, 0, compare, p.getPosition().add(compare.getAsPoint3D()));
                            if (future!=null && future.getDirection()!=null) {
                                for (Direction d : future.getPossibleDirections())
                                {
                                    if (future.getPosition().add(d.getAsPoint3D()).isInArena(arenaSize))
                                    {
                                        hasFuture = true;
                                        break;
                                    }
                                }
                            }
                        }
                        return !hasFuture;
                    }
                });

                // remove risky positions from flight plan
                potentialDirections.removeIf(new Predicate<Direction>()
                {
                    @Override
                    public boolean test(Direction test)
                    {
                        boolean result = false;
                        for (Point3D compare : riskyPositions)
                        {
                            if (p.getPosition().add(test.getAsPoint3D()).equals(compare))
                            {
                                result = true;
                            }
                        }
                        return result && potentialDirections.size() > 0;
                    }
                });

                // check for targets
                Direction best = null;
                if (p.canShoot())
                {
                    int potentialHits = 0;
                    for (Direction d : potentialDirections)
                    {
                        Plane future = new Plane(arenaSize, 0, d, p.getPosition().add(d.getAsPoint3D()));
                        for (Point3D t : future.getShootRange())
                        {
                            int targets = 0;
                            for (Plane e : super.enemyPlanes)
                            {
                                for (Direction s : e.getPossibleDirections())
                                {
                                    Plane target = new Plane(arenaSize, 0, s, e.getPosition().add(s.getAsPoint3D()));
                                    if (target.getPosition().equals(t))
                                    {
                                        targets++;
                                    }

                                }
                            }
                            if (targets > potentialHits)
                            {
                                best = d;
                                potentialHits = targets;
                            }
                        }
                    }
                }

                if (best == null)
                {
                    if (potentialDirections.size() > 0) {
                        best = potentialDirections.get((int) Math.floor(Math.random() * potentialDirections.size()));
                    } else {
                        best = new Direction("N");
                    }
                }

                moves.add(new Move(best, true, false));
                dangerousPositions.add(p.getPosition().add(best.getAsPoint3D()));

            }
            else
            {
                // this plane is dead, not much to do but go hide in corner
                moves.add(new Move(new Direction("N"), false, false));

            }
        }

        Move[] movesArr = {moves.get(0), moves.get(1)};
        return movesArr;
    }

    @Override
    public void newFight(int fightsFought, int myScore, int enemyScore)
    {
        // Using information is for schmucks.
    }

    @Override
    public void newOpponent(int fights)
    {
        // What did I just say about information?
    }
}

7
그러나 배럴 롤을 할 수 있습니까?
Erty Seidohl

1
예외가 발생합니다. 스택 추적은 다음과 같습니다. Planes.Starfox $ 2.test (Starfox.java:99)의 스레드 "main"java.lang.NullPointerException 예외 Planes.Starfox $ 2.test (Starfox.java:1 ) java.util.ArrayList.removeIf (Unknown Source)에서 Planes.Controller.matchUp (Controller.java : Planes.Controller.fight (Controller.java:141)의 Planes.Starfox.act (Starfox.java:90) 85) Planes.Controller.main (Controller.java:35)에서 Package Planes를 추가해야했습니다. 그렇지 않으면 컴파일되지 않았을 수도 있습니다.
overactor

나는 그것을 실행했지만 예상대로 성능이 좋지 않습니다. 최고가 너무 자주 null이라는 문제가있을 수 있습니다.
오버 액터

Starfox가 아닌 곳에서 적의 화재로 움직이는 것처럼 보입니다 . 여기
오버 액터

3

위험

  • Python으로 작성되었으며 Sparr이 작성한 비 Java 코드 랩퍼와 인터페이스합니다.

  • 순수한 파이썬으로 모든 수학을 수행하고 완전히 최적화되지 않았습니다. 조금 느립니다.

  • 고도로 구성 가능하고 확장 가능합니다.

  • 과거의 제출물에 매우 적합합니다. 승 1 : 2 에 대한 손실 모두를위한 싸움을 Crossfire하거나 PredictAndAvoid, 그리고 승리 98 + % 다른 경쟁자에 대한 모든 싸움을.

자체 선택적 시각화 도구를 포함합니다.

싸우는 Crossfire/ PredictAndAvoid, 주변 볼륨에 시인 위험 구역 등급이 표시되어 있습니다.

주위에 색깔의 복셀 그리드가있는 두 개의 라운드에서 4 개의 비행기가 싸우는 비디오.

  • nipy_spectral컬러 맵을 사용하여 시각화 했습니다 matplotlib. 더 위험한 좌표는 전자기 스펙트럼에서 빨강 / 흰색에 가까운 색상을 사용하여 렌더링되며 더 큰 점으로 그려집니다.

  • 위험 : 파란색 <녹색 <노란색 <빨간색 <밝은 회색

공연:

순위표에있는 8 가지 다른 알고리즘을 사용하여 1000 라운드 :

SCORE: DumbPlanes: 0 Dangerzoner: 1000
SCORE: Crossfire: 132 Dangerzoner: 367
SCORE: PredictAndAvoid: 165 Dangerzoner: 465
SCORE: Wee: 0 Dangerzoner: 1000
SCORE: Whirligig: 0 Dangerzoner: 989
SCORE: MoveAndShootPlane: 0 Dangerzoner: 1000
SCORE: Starfox: 4 Dangerzoner: 984
SCORE: DumbPy: 0 Dangerzoner: 1000
SCORES:

DumbPlanes: 2 points.
Crossfire: 12 points.
PredictAndAvoid: 14 points.
Wee: 10 points.
Whirligig: 8 points.
MoveAndShootPlane: 6 points.
Starfox: 4 points.
DumbPy: 0 points.
Dangerzoner: 16 points.


THE OVERALL WINNER(S): Dangerzoner
With 16 points.

암호:

#!/usr/bin/env python3
"""
DangerZoner

Each turn:
    1) Make a list of all possible locations to move to, explicitly excluding suicidal positions that will collide with the walls, an ally, or an ally's bullet.
    2) Rate each possible location using heuristics that estimate the approximate danger in that zone, accounting for the following factors:
        -Proximity to walls. (Manoeuvring constrictions and risk of collision.)
        -Proximity to fronts of planes. (Risk of mid-air collisions.)
        -High distance from enemy planes. (Risk of enemies easily turning to shoot.)
        -Intersection with all enemy attack vectors. (Explicit safety on the next round.)
        -Proximity to enemy forward vectors. (Approximate probability of being targeted in upcoming rounds.)
    3) If certain respective thresholds are met in the possible moves' danger ratings, then do the following if possible:
        -Take a potshot at a random position that an enemy might move to next turn (but never shoot an ally).
        -Take a potshot at an extrapolated position that an enemy will likely move to next turn if they keep up their current rate of turn (but never shoot an ally).
        -Turn to pursue the closest enemy.
        -Move randomly to confound enemy predictive mechanisms. (Disabled since implementing explicit enemy attack vectors in danger zone calculation.)
    4) If none of those thresholds are met, then choose the move rated as least dangerous.
"""

import math, random, functools, sys

#import NGrids
NGrids = lambda: None
class NSpace(object):
    """Object for representing an n-dimensional space parameterized by a list of extents in each dimension."""
    def __init__(self, dimensions):
        self.dimensions = tuple(dimensions)
    def check_coordshape(self, coord):
        return len(coord) == len(self.dimensions)
    def enforce_coordshape(self, coord):
        if not self.check_coordshape(coord):
            raise ValueError(f"Attempted to access {len(coord)}-coordinate point from {len(self.dimensions)}-coordinate space: {coord}")
    def check_coordrange(self, coord):
        return all((0 <= c <= b) for c, b in zip(coord, self.dimensions))
    def enforce_coordrange(self, coord):
        if not self.check_coordrange(coord):
            raise ValueError(f"Attempted to access coordinate point out of range of {'x'.join(str(d) for d in self.dimensions)} space: {coord}")
    def check_coordtype(self, coord):
        return True
    def enforce_coordtype(self, coord):
        if not self.check_coordtype(coord):
            raise TypeError(f"Attempted to access grid point with invalid coordinates for {type(self).__name__}(): {coord}")
    def enforce_coord(self, coord):
        for f in (self.enforce_coordshape, self.enforce_coordrange, self.enforce_coordtype):
            f(coord)
    def coords_grid(self, step=None):
        if step is None:
            step = tuple(1 for i in self.dimensions)
        self.enforce_coord(step)
        counts = [math.ceil(d/s) for d, s in zip(self.dimensions, step)]
        intervals = [1]
        for c in counts:
            intervals.append(intervals[-1]*c)
        for i in range(intervals[-1]):
            yield tuple((i//l)*s % (c*s) for s, l, c in zip(step, intervals, counts))
NGrids.NSpace = NSpace

def Pythagorean(*coords):
    return math.sqrt(sum(c**2 for c in coords))

class Plane(object):
    """Object for representing a single dogfighting plane."""
    def __init__(self, alive, coord, vec, cooldown=None, name=None):
        self.alive = alive
        self.set_alive(alive)
        self.coord = coord
        self.set_coord(coord)
        self.vec = vec
        self.set_vec(vec)
        self.cooldown = cooldown
        self.set_cooldown(cooldown)
        self.name = name
    def set_alive(self, alive):
        self.lastalive = self.alive
        self.alive = alive
    def set_coord(self, coord):
        self.lastcoord = self.coord
        self.coord = coord
    def set_vec(self, vec):
        self.lastvec = self.vec
        self.vec = vec
    def set_cooldown(self, cooldown):
        self.lastcooldown = self.cooldown
        self.cooldown = cooldown
    def update(self, alive=None, coord=None, vec=None, cooldown=None):
        if alive is not None:
            self.set_alive(alive)
        if coord is not None:
            self.set_coord(coord)
        if vec is not None:
            self.set_vec(vec)
        if cooldown is not None:
            self.set_cooldown(cooldown)
    def get_legalvecs(self):
        return getNeighbouringVecs(self.vec)
    def get_legalcoords(self):
        return {tuple(self.coord[i]+v for i, v in enumerate(vec)) for vec in self.get_legalvecs()}
    def get_legalfutures(self):
        return (lambda r: r.union((c, self.vec) for c, v in r))({(vecAdd(self.coord, vec),vec) for vec in self.get_legalvecs()})

class DangerZones(NGrids.NSpace):
    """Arena object for representing an n-dimensional volume with both enemy and allied planes in it and estimating the approximate safety/danger of positions within it. """
    def __init__(self, dimensions=(13,13,13), walldanger=18.0, walldistance=3.5, wallexpo=2.0, walluniformity=5.0, planedanger=8.5, planeexpo=8.0, planeoffset=1.5, planedistance=15.0, planedistancedanger=2.0, planedistanceexpo=1.5, firedanger=9.0, collisiondanger=10.0, collisiondirectionality=0.6, collisiondistance=2.5, collisionexpo=0.2):
        NGrids.NSpace.__init__(self, dimensions)
        self.walldanger = walldanger
        self.walldistance = walldistance
        self.wallexpo = wallexpo
        self.walluniformity = walluniformity
        self.planedanger = planedanger
        self.planeexpo = planeexpo
        self.planeoffset = planeoffset
        self.planedistance = planedistance
        self.planedistancedanger = planedistancedanger
        self.planedistanceexpo = planedistanceexpo
        self.firedanger = firedanger
        self.collisiondanger = collisiondanger
        self.collisiondirectionality = collisiondirectionality
        self.collisiondistance = collisiondistance
        self.collisionexpo = collisionexpo
        self.set_planes()
        self.set_allies()
        self.clear_expectedallies()
    def filteractiveplanes(self, planes=None):
        if planes is None:
            planes = self.planes
        return (p for p in planes if all((p.alive, p.coord, p.vec)))
    def rate_walldanger(self, coord):
        self.enforce_coordshape(coord)
        return (lambda d: (max(d)*self.walluniformity+sum(d))/(self.walluniformity+1))((1-min(1, (self.dimensions[i]/2-abs(v-self.dimensions[i]/2))/self.walldistance)) ** self.wallexpo * self.walldanger for i, v in enumerate(coord))
    def rate_planedanger(self, coord, planecoord, planevec):
        for v in (planecoord, planevec, coord):
            self.enforce_coordshape(v)
        return max(0, (1 - vecAngle(planevec, vecSub(coord, vecSub(planecoord, vecMult(planevec, (self.planeoffset,)*len(self.dimensions)))) ) / math.pi)) ** self.planeexpo * self.planedanger
        offsetvec = convertVecTrinary(planevec, length=self.planeoffset)
        relcoord = [v-(planecoord[i]-offsetvec[i]) for i, v in enumerate(coord)]
        nrelcoord = (lambda m: [(v/m if m else 0) for v in relcoord])(Pythagorean(*relcoord))
        planevec = (lambda m: [(v/m if m else 0) for v in planevec])(Pythagorean(*planevec))
        return max(0, sum(d*p for d, p in zip(planevec, nrelcoord))+2)/2 ** self.planeexpo * self.planedanger + min(1, Pythagorean(*relcoord)/self.planedistance) ** self.planedistanceexpo * self.planedistancedanger
    def rate_planedistancedanger(self, coord, planecoord, planevec):
        return Pythagorean(*vecSub(planecoord, coord))/self.planedistance ** self.planedistanceexpo * self.planedistancedanger
    def rate_firedanger(self, coord, plane):
        return (min(vecAngle(vecSub(coord, c), v) for c, v in plane.get_legalfutures()) < 0.05) * self.firedanger
    def rate_collisiondanger(self, coord, planecoord, planevec):
        if coord == planecoord:
            return self.collisiondanger
        offsetvec = tuple(p-c for p,c in zip(planecoord, coord))
        return max(0, vecAngle(planevec, offsetvec)/math.pi)**self.collisiondirectionality * max(0, 1-Pythagorean(*offsetvec)/self.collisiondistance)**self.collisionexpo*self.collisiondanger
    def set_planes(self, *planes):
        self.planes = planes
    def set_allies(self, *allies):
        self.allies = allies
    def rate_planesdanger(self, coord, planes=None):
        if planes is None:
            planes = {*self.planes}
        return max((0, *(self.rate_planedanger(coord, planecoord=p.coord, planevec=p.vec) for p in self.filteractiveplanes(planes))))
    def rate_planedistancesdanger(self, coord, planes=None):
        if planes is None:
            planes = {*self.planes}
        return max((0, *(self.rate_planedistancedanger(coord, planecoord=p.coord, planevec=p.vec) for p in self.filteractiveplanes(planes))))
    def rate_firesdanger(self, coord, planes=None):
        if planes is None:
            planes = {*self.planes}
        return sum(self.rate_firedanger(coord, p) for p in self.filteractiveplanes(planes))
    def rate_collisionsdanger(self, coord, pself=None, planes=None):
        if planes is None:
            planes = {*self.planes, *self.allies}
        return max((0, *(self.rate_collisiondanger(coord , planecoord=p.coord, planevec=p.vec) for p in self.filteractiveplanes(planes) if p is not pself)))
    def rate_sumdanger(self, coord, pself=None, planes=None):
        return max((self.rate_walldanger(coord), self.rate_planesdanger(coord, planes=planes), self.rate_planedistancesdanger(coord, planes=planes), self.rate_firesdanger(coord, planes=planes), self.rate_collisionsdanger(coord, pself=pself, planes=planes)))
    def get_expectedallies(self):
        return {*self.expectedallies}
    def clear_expectedallies(self):
        self.expectedallies = set()
    def add_expectedallies(self, *coords):
        self.expectedallies.update(coords)
    def get_expectedshots(self):
        return {*self.expectedshots}
    def clear_expectedshots(self):
        self.expectedshots = set()
    def add_expectedshots(self, *rays):
        self.expectedshots.update(rays)
    def tickturn(self):
        self.clear_expectedallies()
        self.clear_expectedshots()

def stringException(exception):
    import traceback
    return ''.join(traceback.format_exception(type(exception), exception, exception.__traceback__))

try:
    import matplotlib.pyplot, matplotlib.cm, mpl_toolkits.mplot3d, time
    class PlottingDangerZones(DangerZones):
        """Arena object for calculating danger ratings and rendering 3D visualizations of the arena state and contents to both files and an interactive display on each turn."""
        plotparams = {'dangersize': 80, 'dangersizebase': 0.2, 'dangersizeexpo': 2.0, 'dangeralpha': 0.2, 'dangerres': 1, 'dangervrange': (0, 10), 'dangercmap': matplotlib.cm.nipy_spectral, 'dangermarker': 'o', 'allymarker': 's', 'enemymarker': 'D', 'vectormarker': 'x', 'planesize': 60, 'vectorsize': 50, 'planecolour': 'black', 'deathmarker': '*', 'deathsize': 700, 'deathcolours': ('darkorange', 'red'), 'deathalpha': 0.65, 'shotlength': 4, 'shotcolour': 'darkviolet', 'shotstyle': 'dashed'}
        enabledplots = ('enemies', 'allies', 'vectors', 'danger', 'deaths', 'shots', 'names')
        def __init__(self, dimensions=(13,13,13), plotparams=None, plotautoturn=0, plotsavedir=None, enabledplots=None, disabledplots=None, tickwait=0.0, plotcycle=0.001, **kwargs):
            DangerZones.__init__(self, dimensions, **kwargs)
            self.figure = None
            self.axes = None
            self.frame = None
            self.plotobjs = {}
            self.plotshown = False
            if plotparams:
                self.set_plotparams(plotparams)
            self.plotautoturn = plotautoturn
            self.plotsavedir = plotsavedir
            if enabledplots:
                self.enabledplots = tuple(enabledplots)
            if disabledplots:
                self.enabledplots = tuple(m for m in self.enabledplots if m not in disabledplots)
            self.tickwait = tickwait
            self.plotcycle = plotcycle
            self.lasttick = time.time()
        def set_plotparams(self, plotparams):
            self.plotparams = {**self.plotparams, **plotparams}
        def prepare_plotaxes(self, figure=None, clear=True):
            if self.figure is None and figure is None:
                self.figure = matplotlib.pyplot.figure()
                self.frame = 0
            if self.axes is None:
                self.axes = self.figure.add_subplot(projection='3d')
            elif clear:
                self.axes.clear()
            for d, h in zip((self.axes.set_xlim, self.axes.set_ylim, self.axes.set_zlim), self.dimensions):
                d(0, h)
            return (self.figure, self.axes)
        def plotter(kind):
            def plotterd(funct):
                def plott(self):
                    kws = dict(getattr(self, funct.__name__.replace('plot_', 'plotparams_'))())
                    if '*args' in kws:
                        args = tuple(kws.pop('*args'))
                    else:
                        args = tuple()
                    if False and funct.__name__ in self.plotobjs:
                        self.plotobjs[funct.__name__].set(**kws)
                    else:
                        self.plotobjs[funct.__name__] = getattr(self.axes, kind)(*args, **kws)
                return plott
            return plotterd
        def plotparams_enemies(self):
            r = {'xs': tuple(), 'ys': tuple(), 'zs': tuple(), 'marker': self.plotparams['enemymarker'], 's': self.plotparams['planesize'], 'c': self.plotparams['planecolour']}
            planes = tuple(self.filteractiveplanes(self.planes))
            if planes:
                r['xs'], r['ys'], r['zs'] = zip(*(p.coord for p in planes))
            return r
        def plotparams_allies(self):
            r = {'xs': tuple(), 'ys': tuple(), 'zs': tuple(), 'marker': self.plotparams['allymarker'], 's': self.plotparams['planesize'], 'c': self.plotparams['planecolour']}
            planes = tuple(self.filteractiveplanes(self.allies))
            if planes:
                r['xs'], r['ys'], r['zs'] = zip(*(p.coord for p in planes))
            return r
        def plotparams_vectors(self):
            r = {'xs': tuple(), 'ys': tuple(), 'zs': tuple(), 'marker': self.plotparams['vectormarker'], 's': self.plotparams['vectorsize'], 'c': self.plotparams['planecolour']}
            planes = tuple(self.filteractiveplanes(self.allies+self.planes))
            if planes:
                r['xs'], r['ys'], r['zs'] = zip(*(vecAdd(p.coord, p.vec) for p in planes))
            return r
        def plotparams_danger(self):
            r = {'xs': tuple(), 'ys': tuple(), 'zs': tuple(), 'marker': self.plotparams['dangermarker'], 'cmap': self.plotparams['dangercmap'], 'alpha': self.plotparams['dangeralpha']}
            coords = tuple(self.coords_grid((self.plotparams['dangerres'],)*len(self.dimensions)))
            r['xs'], r['ys'], r['zs'] = zip(*coords)
            r['c'] = tuple(self.rate_sumdanger(c) for c in coords)
            m = max(r['c'])
            r['s'] = tuple((d/m)**self.plotparams['dangersizeexpo']*self.plotparams['dangersize']+self.plotparams['dangersizebase'] for d in r['c'])
            if self.plotparams['dangervrange']:
                r['vmin'], r['vmax'] = self.plotparams['dangervrange']
            return r
        def plotparams_deaths(self):
            r = {'xs': tuple(), 'ys': tuple(), 'zs': tuple(), 'marker': self.plotparams['deathmarker'], 's': self.plotparams['deathsize'], 'c': self.plotparams['deathcolours'][0], 'linewidths': self.plotparams['deathsize']/180, 'edgecolors': self.plotparams['deathcolours'][1], 'alpha': self.plotparams['deathalpha']}
            deaths = tuple(p.lastcoord for p in self.planes+self.allies if p.lastalive and not p.alive)
            if deaths:
                r['xs'], r['ys'], r['zs'] = zip(*deaths)
            return r
        def plotparams_shots(self):
            r = {'length': self.plotparams['shotlength'], 'linestyles': self.plotparams['shotstyle'], 'color': self.plotparams['shotcolour'], 'arrow_length_ratio': 0.0, '*args': []}
            planes = tuple(p for p in self.filteractiveplanes(self.allies+self.planes) if not (p.lastcooldown is None or p.cooldown is None) and (p.cooldown > p.lastcooldown))
            if planes:
                for s in zip(*(p.coord for p in planes)):
                    r['*args'].append(s)
                for s in zip(*(p.vec for p in planes)):
                    r['*args'].append(s)
            else:
                for i in range(6):
                    r['*args'].append(tuple())
            return r
        @plotter('scatter')
        def plot_enemies(self):
            pass
        @plotter('scatter')
        def plot_allies(self):
            pass
        @plotter('scatter')
        def plot_vectors(self):
            pass
        @plotter('scatter')
        def plot_danger(self):
            pass
        @plotter('scatter')
        def plot_deaths(self):
            pass
        @plotter('quiver')
        def plot_shots(self):
            pass
        def plot_names(self):
            if 'plot_names' in self.plotobjs:
                pass
            self.plotobjs['plot_names'] = [self.axes.text(*p.coord, s=f"{p.name}") for i, p in enumerate(self.filteractiveplanes(self.allies+self.planes))]
        def plotall(self):
            for m in self.enabledplots:
                getattr(self, f'plot_{m}')()
        def updateallplots(self):
            self.prepare_plotaxes()
            self.plotall()
            if self.plotautoturn:
                self.axes.view_init(30, -60+self.frame*self.plotautoturn)
            matplotlib.pyplot.draw()
            if self.plotsavedir:
                import os
                os.makedirs(self.plotsavedir, exist_ok=True)
                self.figure.savefig(os.path.join(self.plotsavedir, f'{self.frame}.png'))
            self.frame += 1
            if not self.plotshown:
                matplotlib.pyplot.ion()
                matplotlib.pyplot.show()#block=False)
                self.plotshown = True
        def tickturn(self):
            DangerZones.tickturn(self)
            self.updateallplots()
            matplotlib.pyplot.pause(max(self.plotcycle, self.lasttick+self.tickwait-time.time()))
            self.lasttick = time.time()
except Exception as e:
    print(f"Could not define matplotlib rendering dangerzone handler:\n{stringException(e)}", file=sys.stderr)


def vecEquals(vec1, vec2):
    return tuple(vec1) == tuple(vec2)

def vecAdd(*vecs):
    return tuple(sum(p) for p in zip(*vecs))

def vecSub(vec1, vec2):
    return tuple(a-b for a, b in zip(vec1, vec2))

def vecMult(*vecs):
    return tuple(functools.reduce(lambda a, b: a*b, p) for p in zip(*vecs))

def vecDiv(vec1, vec2):
    return tuple(a-b for a, b in zip(vec1, vec2))

def vecDotProduct(*vecs):
    return sum(vecMult(*vecs))
    #return sum(d*p for d, p in zip(vec1, vec2))

def vecAngle(vec1, vec2):
    try:
        if all(c == 0 for c in vec1) or all(c == 0 for c in vec2):
            return math.nan
        return math.acos(max(-1, min(1, vecDotProduct(vec1, vec2)/Pythagorean(*vec1)/Pythagorean(*vec2))))
    except Exception as e:
        raise ValueError(f"{e!s}: {vec1} {vec2}")

def convertVecTrinary(vec, length=1):
    return tuple((max(-length, min(length, v*math.inf)) if v else v) for v in vec)

def getNeighbouringVecs(vec):
    vec = convertVecTrinary(vec, length=1)
    return {ve for ve in (tuple(v+(i//3**n%3-1) for n, v in enumerate(vec)) for i in range(3**len(vec))) if all(v in (-1,0,1) for v in ve) and any(v and v==vec[i] for i, v in enumerate(ve))}

def getVecRotation(vec1, vec2):
    #Just do a cross product/perpendicular to tangential plane/normal?
    pass

def applyVecRotation(vec, rotation):
    pass

class DangerZoner(Plane):
    """Dogfighting plane control object."""
    def __init__(self, arena, snipechance=0.60, snipechoices=3, firesafety=7.5, chasesafety=5.0, jinkdanger=math.inf, jink=0, name=None):
        Plane.__init__(self, True, None, None)
        self.arena = arena
        self.lookahead = 1
        self.snipechance = snipechance
        self.snipechoices = snipechoices
        self.firesafety = firesafety
        self.chasesafety = chasesafety
        self.jinkdanger = jinkdanger
        self.jink = jink
        self.vec = None
        self.name = name
    def get_enemies(self):
        return (p for p in self.arena.filteractiveplanes(self.arena.planes))
    def get_vecsuicidal(self, vec, coord=None, steps=5):
        if coord is None:
            coord = self.coord
        if all(3 < c < self.arena.dimensions[i]-3 for i, c in enumerate(coord)):
            return False
        if not all(0 < c < self.arena.dimensions[i] for i, c in enumerate(coord)):
            return True
        elif steps >= 0:
            return all(self.get_vecsuicidal(v, coord=vecAdd(coord, vec), steps=steps-1) for v in getNeighbouringVecs(vec))
        return False
    def get_sanevecs(self):
        legalvecs = self.get_legalvecs()
        s = {vec for vec in legalvecs if vecAdd(self.coord, vec) not in self.arena.get_expectedallies() and not any(vecAngle(vecSub(vecAdd(self.coord, vec), sc), sv) < 0.05 for sc, sv in self.arena.get_expectedshots()) and not self.get_vecsuicidal(vec, coord=vecAdd(self.coord, vec))}
        if not s:
            return legalvecs
            raise Exception()
        return s
    def rate_vec(self, vec, lookahead=None):
        if lookahead is None:
            lookahead = self.lookahead
        return self.arena.rate_sumdanger(tuple(c+v*lookahead for v, c in zip(vec, self.coord)), pself=self)
    def get_validshots(self, snipe=True):
        if snipe and random.random() < self.snipechance:
            enemypossibilities = set.union(*({vecAdd(p.coord, p.vec)} if not p.lastvec or vecEquals(p.vec, p.lastvec) else {vecAdd(p.coord, ve) for ve in sorted(p.get_legalvecs(), key=lambda v: -vecAngle(v, p.lastvec))[:self.snipechoices]} for p in self.get_enemies()))
        else:
            enemypossibilities = set().union(*(p.get_legalcoords() for p in self.get_enemies()))
        validshots = []
        if self.cooldown:
            return validshots
        for vec in self.get_sanevecs():
            coord = tuple(c + v for c, v in zip(self.coord, vec))
            if any(vecAngle(tuple(n-v for n, v in zip(t, self.coord)), self.vec) < 0.1 for t in enemypossibilities if t != self.coord) and not any(vecAngle(vecSub(a, coord), self.vec) < 0.05 for a in self.arena.get_expectedallies()):
                validshots.append({'vec': vec, 'turn': False, 'fire': True})
            if any(vecAngle(tuple(n-v for n, v in zip(t, self.coord)), vec) < 0.1 for t in enemypossibilities if t != self.coord) and not any(vecAngle(vecSub(a, coord), vec) < 0.05 for a in self.arena.get_expectedallies()):
                validshots.append({'vec': vec, 'turn': True, 'fire': True})
        if snipe and not validshots:
            validshots = self.get_validshots(snipe=False)
        return validshots
    def get_chase(self):
        enemydirs = {vecSub(vecAdd(p.coord, p.vec), self.coord) for p in self.get_enemies()}
        paths = sorted(self.get_sanevecs(), key=lambda vec: min([vecAngle(vec, e) for e in enemydirs if not all(v == 0 for v in e)]+[math.inf]))
        if paths:
            return paths[0]
    def get_move(self):
        if not self.alive:
            return {'vec': (1,1,1), 'turn': False, 'fire': False}
        fires = self.get_validshots()
        if fires:
            fires = sorted(fires, key=lambda d: self.rate_vec(d['vec']))
            if self.rate_vec(fires[0]['vec']) <= self.firesafety:
                return fires[0]
        vec = self.get_chase()
        if vec is None or self.rate_vec(vec) > self.chasesafety:
            vec = sorted(self.get_sanevecs(), key=self.rate_vec)
            vec = vec[min(len(vec)-1, random.randint(0,self.jink)) if self.rate_vec(vec[0]) > self.jinkdanger else 0]
        return {'vec': vec, 'turn': True, 'fire': False}
    def move(self):
        move = self.get_move()
        coord = vecAdd(self.coord, move['vec'])
        self.arena.add_expectedallies(coord)
        if move['fire']:
            self.arena.add_expectedshots((coord, move['vec'] if move['turn'] else self.vec))
        return move

VecsCarts = {(0,-1):'N', (0,1):'S', (1,1):'E', (1,-1):'W', (2,1):'U', (2,-1):'D'}

def translateCartVec(cartesian):
    vec = [0]*3
    for v,l in VecsCarts.items():
        if l in cartesian:
            vec[v[0]] = v[1]
    return tuple(vec)

def translateVecCart(vec):
    vec = convertVecTrinary(vec)
    return ''.join(VecsCarts[(i,v)] for i, v in enumerate(vec) if v != 0)

def parsePlaneState(text):
    return (lambda d: {'alive':{'alive': True, 'dead': False}[d[0]], 'coord':tuple(int(c) for c in d[1:4]), 'vec':translateCartVec(d[4]), 'cooldown': int(d[5])})(text.split(' '))

def encodePlaneInstruction(vec, turn, fire):
    return f"{translateVecCart(vec)} {int(bool(turn))!s} {int(bool(fire))!s}"

class CtrlReceiver:
    """Object for interacting through STDIN and STDOUT in a dogfight with an arena, controlled planes, and enemy planes."""
    def __init__(self, logname='danger_log.txt', arenatype=DangerZones, arenaconf=None, planetype=DangerZoner, planeconf=None, enemyname='Enemy', stdin=sys.stdin, stdout=sys.stdout):
        self.logname = logname
        self.arenatype = arenatype
        self.arenaconf = dict(arenaconf) if arenaconf else dict()
        self.planetype = planetype
        self.planeconf = dict(planeconf) if planeconf else dict()
        self.enemyname = enemyname
        self.stdin = stdin
        self.stdout = stdout
        self.log = open('danger_log.txt', 'w')
    def __enter__(self):
        return self
    def __exit__(self, *exc):
        self.log.__exit__()
    def getin(self):
        l = self.stdin.readline()
        self.log.write(f"IN: {l}")
        return l
    def putout(self, content):
        self.log.write(f"OUT: {content}\n")
        print(content, file=self.stdout, flush=True)
    def logout(self, content):
        self.log.write(f"MSG: {content}\n")
    def logerr(self, content):
        self.log.write(f"ERR: {content}\n")
    def run_setup(self, arenasize, rounds):
        self.arena = self.arenatype(dimensions=(arenasize,)*3, **self.arenaconf)
        self.planes = [self.planetype(arena=self.arena, name=f"{self.planetype.__name__} #{i}", **self.planeconf) for i in range(2)]
        self.arena.set_planes(*(Plane(True, None, None, name=f"{self.enemyname} #{i}") for i in range(2)))
        self.arena.set_allies(*self.planes)
    def run_move(self):
        self.arena.tickturn()
        for p in self.planes:
            p.update(**parsePlaneState(self.getin()))
        for p in self.arena.planes:
            p.update(**parsePlaneState(self.getin()))
        for p in self.planes:
            self.putout(encodePlaneInstruction(**p.move()))
    def run(self):
        line = ''
        while not line.startswith('NEW CONTEST '):
            line = self.getin()
        self.run_setup(arenasize=int(line.split(' ')[2])-1, rounds=None)
        while True:
            line = self.getin()
            if line.startswith('NEW TURN'):
                self.run_move()

if True and __name__ == '__main__' and not sys.flags.interactive:
    import time
    DoPlot = False
    #Use the arena object that visualizes progress every turn.
    DangerPlot = True
    #Compute and render a voxel cloud of danger ratings within the arena each turn if visualizing it.
    SparseDangerPlot = False
    #Use a lower resolution for the voxel cloud if visualizing danger ratings.
    TurntablePlot = True
    #Apply a fixed animation to the interactive visualization's rotation if visualizing the arena.
    with CtrlReceiver(logname='danger_log.txt', arenatype=PlottingDangerZones if DoPlot else DangerZones, arenaconf=dict(disabledplots=None if DangerPlot else ('danger'), plotparams=dict(dangerres=2) if SparseDangerPlot else dict(dangeralpha=0.1), plotautoturn=1 if TurntablePlot else 0, plotsavedir=f'PngFrames') if DoPlot else None, planetype=DangerZoner) as run:
        try:
            run.run()
        except Exception as e:
            run.logerr(stringException(e))
```
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.