3D: Đấu tay đôi rời rạc (Hiện đang mở cho các bài nộp không phải của Java)


31

CẬP NHẬT: isSuicidal () đã được thêm vào lớp máy bay, điều này cho phép bạn kiểm tra xem máy bay có đang ở trong quá trình va chạm không thể đảo ngược với các bức tường không !!

CẬP NHẬT: updateCoolDown () được tách ra từ simulationMove ()

CẬP NHẬT: trình bao bọc mục nhập không phải Java, được viết bởi Sparr , có sẵn để thử nghiệm, xem các bình luận

CẬP NHẬT Trò chơi Zove đã viết một trình hiển thị 3D tuyệt vời cho KOTH này, đây là một video youtube tồi tệ về trò chơi PredictAndAVoid chiến đấu với PredictAndAVoid.

Hàm simulationMove () của lớp Máy bay đã được sửa đổi một chút để nó không cập nhật hạ nhiệt nữa, sử dụng hàm updateCoolDown () mới cho điều đó, sau khi chụp. IsSuicidal () mới trả về đúng nếu máy bay bị ràng buộc là đã chết, sử dụng nó để cắt tỉa di chuyển của kẻ thù và tránh va vào tường. Để có được mã cập nhật, chỉ cần thay thế các lớp Trình điều khiển và Mặt phẳng bằng các lớp trong repo github.

Sự miêu tả

Mục tiêu của thử thách này là mã hóa hai máy bay chiến đấu sẽ đối đầu với hai máy bay của một thí sinh khác. Mỗi lượt bạn di chuyển một khoảng trống và có cơ hội bắn. Đó là nó, nó đơn giản như vậy.

Chà, gần như ...

Đấu trường và di chuyển có thể

Đấu trường là một bức tường 14x14x14 trong không gian. các mặt phẳng của thí sinh 1 bắt đầu tại các địa điểm (0,5,0) và (0,8,0) và các mặt phẳng của thí sinh 2 tại (13,5,13) và (13,8,13). Tất cả các mặt phẳng bắt đầu bằng cách bay theo chiều ngang từ các bức tường thẳng đứng mà chúng gần nhất.

Bây giờ vì bạn là máy bay đang bay chứ không phải máy bay trực thăng, bạn không thể thay đổi hướng tùy ý hoặc thậm chí dừng di chuyển, vì vậy mỗi máy bay có một hướng và sẽ di chuyển một ô theo hướng đó mỗi lượt.

Các hướng có thể là: Bắc (N), Nam (S), Đông (E), Tây (W), Lên (U) và Xuống (D) và bất kỳ kết hợp logic nào của sáu. Trong đó trục NS tương ứng với trục x, WE với y và DU thành z. NW, SU và NED đến với tâm trí như những ví dụ có thể về các hướng; UD là một ví dụ tuyệt vời về sự kết hợp không hợp lệ.

Tất nhiên bạn có thể thay đổi hướng của các mặt phẳng của mình, nhưng có một hạn chế, bạn chỉ có thể thay đổi hướng của mình tối đa 45 độ. Để hình dung điều này, hãy lấy khối lập phương rubik của bạn (tôi biết bạn có một cái) và tưởng tượng tất cả 26 hình khối nhỏ bên ngoài là các hướng có thể (một hướng chữ là các mặt, hai hướng chữ là các cạnh và ba hướng chữ là các góc). Nếu bạn đang đi theo một hướng được đại diện bởi một khối lập phương nhỏ, bạn có thể thay đổi hướng sang từng khối chạm vào khối của bạn (chạm theo đường chéo, nhưng chỉ chạm rõ ràng, không chạm vào khối).

Sau khi tất cả các mặt phẳng đã chỉ ra hướng nào chúng muốn thay đổi, chúng làm như vậy và di chuyển một ô cùng một lúc.

Bạn cũng có thể chọn di chuyển theo hướng hợp lệ nhưng tiếp tục bay theo hướng bạn đang đi, thay vì thay đổi hướng của bạn sang hướng bạn di chuyển đến. Điều này tương tự như sự khác biệt giữa một chiếc xe đi quanh một góc và một chiếc xe thay đổi làn đường.

Bắn và chết

Bạn có thể bắn nhiều nhất một lần mỗi vòng và điều này phải được quyết định đồng thời bạn quyết định bay theo hướng nào và bạn có muốn giữ máy bay của mình (và bằng cách mở rộng, súng của bạn) chỉ cùng hướng hay không. Viên đạn được bắn ngay sau khi máy bay của bạn di chuyển. Có một sự hạ nhiệt của một lượt sau khi quay, ở lượt thứ ba, bạn tốt để quay lại. Bạn chỉ có thể bắn theo hướng bạn đang bay. Một viên đạn là tức thì và bay theo một đường thẳng cho đến khi nó chạm vào tường hoặc máy bay.

Có tính đến cách bạn có thể thay đổi hướng cũng như 'thay đổi làn đường', điều này có nghĩa là bạn có thể đe dọa một cột có tới 3x3 đường phía trước bạn thêm vào một số đường chéo, một đường.

Nếu nó đâm vào một chiếc máy bay, chiếc máy bay này sẽ chết và nhanh chóng biến mất khỏi bảng (vì nó hoàn toàn phát nổ hoặc thứ gì đó). Đạn chỉ có thể bắn trúng tối đa một mặt phẳng. Đạn được bắn đồng thời, vì vậy hai máy bay có thể bắn nhau. Hai viên đạn không thể va chạm trong không khí (buồn, tôi biết).

Tuy nhiên, hai mặt phẳng có thể va chạm với nhau (nếu chúng kết thúc trong cùng một khối lập phương và KHÔNG nếu chúng giao nhau mà không kết thúc trong cùng một mặt phẳng), và điều này dẫn đến cả hai mặt phẳng đều chết (và hoàn toàn phát nổ). Bạn cũng có thể bay vào tường, điều này sẽ khiến máy bay bị chết và bị đặt vào góc để suy nghĩ về hành động của nó. Va chạm được xử lý trước khi chụp.

Giao tiếp với bộ điều khiển

Tôi sẽ chấp nhận các mục trong java cũng như các ngôn ngữ khác. Nếu mục nhập của bạn là trong java, bạn sẽ nhận được đầu vào thông qua STDIN và sẽ xuất qua STDOUT.

Nếu mục nhập của bạn là trong java, mục nhập của bạn phải mở rộng lớp sau:

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

Ví dụ được tạo của lớp đó sẽ tồn tại trong toàn bộ cuộc thi, vì vậy bạn có thể lưu trữ bất kỳ dữ liệu nào bạn muốn lưu trữ trong các biến. Đọc các bình luận trong mã để biết thêm thông tin.

Tôi cũng đã cung cấp cho bạn các lớp trợ giúp sau:

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) {}
}

Bạn có thể tạo các thể hiện của các lớp này và sử dụng bất kỳ chức năng nào của chúng bao nhiêu tùy thích. Bạn có thể tìm thấy mã đầy đủ cho các lớp trợ giúp ở đây .

Dưới đây là một ví dụ về những gì nhập của bạn có thể trông giống như (Hy vọng rằng bạn sẽ làm tốt hơn tôi đã làm dù, hầu hết các trận đấu với những chiếc máy bay cuối cùng với họ bay vào một bức tường, bất chấp những nỗ lực hết sức mình để tránh những bức tường.):

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 sẽ tham gia giải đấu cùng với các mục khác, vì vậy nếu bạn kết thúc lần cuối, đó là lỗi của bạn vì ít nhất là không làm tốt hơn DumbPlanes.

Hạn chế

Các hạn chế được đề cập trong wiki KOTH áp dụng:

  • Mọi nỗ lực để sửa lại bộ điều khiển, thời gian chạy hoặc các bài nộp khác sẽ bị loại. Tất cả các đệ trình chỉ nên làm việc với các đầu vào và lưu trữ mà chúng được đưa ra.
  • Bots không nên được viết để đánh bại hoặc hỗ trợ các bot cụ thể khác. (Điều này có thể được mong muốn trong những trường hợp hiếm hoi, nhưng nếu đây không phải là khái niệm cốt lõi của thử thách, thì tốt hơn là loại trừ.)
  • Tôi bảo lưu quyền loại bỏ các bài nộp sử dụng quá nhiều thời gian hoặc bộ nhớ để chạy thử nghiệm với một lượng tài nguyên hợp lý.
  • Một bot không được thực hiện chiến lược chính xác giống như một chiến lược hiện có, cố ý hay vô tình.

Kiểm tra trình của bạn

Tải về mã điều khiển từ đây . Thêm trình của bạn dưới dạng Something.java. Sửa đổi Controller.java để bao gồm các mục cho máy bay của bạn trong các mục [] và tên []. Biên dịch mọi thứ như một dự án Eclipse hoặc với javac -d . *.java, sau đó chạy Trình điều khiển với java Planes/Controller. Một bản ghi của cuộc thi sẽ được đăng nhập test.txt, với bảng điểm ở cuối. Bạn cũng có thể gọi matchUp()trực tiếp với hai mục nhập làm đối số để chỉ kiểm tra hai mặt phẳng với nhau.

Giành chiến thắng

Người chiến thắng trong cuộc chiến là người có chiếc máy bay cuối cùng bay, nếu sau 100 lượt, vẫn còn hơn 1 đội, đội có nhiều máy bay nhất sẽ chiến thắng. Nếu điều này là bằng nhau, đó là một trận hòa.

Chấm điểm và thi đấu

Giải đấu chính thức tiếp theo sẽ được tổ chức khi hết tiền thưởng hiện tại.

Mỗi mục sẽ chiến đấu với mọi mục khác (ít nhất) 100 lần, người chiến thắng trong mỗi trận đấu trở lên là người có nhiều chiến thắng nhất trong số 100 và sẽ được thưởng 2 điểm. Trong trường hợp bốc thăm, cả hai mục được thưởng 1 điểm.

Người chiến thắng trong cuộc thi là người có nhiều điểm nhất. Trong trường hợp bốc thăm, người chiến thắng là người chiến thắng trong trận đấu giữa các mục đã rút ra.

Tùy thuộc vào số lượng mục, Số lượng trận đấu giữa các mục có thể tăng đáng kể, tôi cũng có thể chọn 2-4 mục tốt nhất sau giải đấu đầu tiên và thiết lập một giải đấu ưu tú giữa các mục có nhiều trận đấu hơn (và có thể nhiều vòng hơn mỗi chiến đấu)

(sơ bộ) Bảng điểm

Chúng tôi đã có một mục mới, người chắc chắn giành vị trí thứ hai trong một giải đấu thú vị khác , có vẻ như Crossfire rất khó để bắn cho tất cả mọi người ngoại trừ PredictAndAvoid. Lưu ý rằng giải đấu này được tổ chức chỉ với 10 trận đấu giữa mỗi bộ máy bay và không có sự thể hiện hoàn toàn chính xác về cách mọi thứ diễn ra.

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

Dưới đây là một ví dụ về đầu ra từ trình bao bọc không phải Java:

NEW CONTEST 14 20 chỉ ra rằng một cuộc thi mới đang bắt đầu, trong một đấu trường 14x14x14 và nó sẽ liên quan đến 20 lượt mỗi trận đấu.

NEW OPPONENT 10 cho biết bạn đang đối mặt với một đối thủ mới và bạn sẽ chiến đấu với đối thủ này 10 lần

NEW FIGHT 5 3 2 chỉ ra rằng một cuộc chiến mới chống lại đối thủ hiện tại đang bắt đầu, rằng bạn đã chiến đấu với đối thủ này 5 lần cho đến nay, thắng 3 và thua 2 trận chiến

ROUNDS LEFT 19 cho thấy có 19 vòng còn lại trong cuộc chiến hiện tại

NEW TURN chỉ ra rằng bạn sắp nhận được dữ liệu cho cả bốn máy bay cho vòng chiến đấu này

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

Bốn dòng này chỉ ra rằng cả hai mặt phẳng của bạn đều còn sống, tại tọa độ [13,8,13] và [13,5,13], cả hai đều hướng về phía Bắc, cả hai đều có thời gian hồi chiêu bằng không. Máy bay địch đầu tiên đã chết, và chiếc thứ hai còn sống, ở [0,8,0] và hướng về phía nam với thời gian hồi chiêu bằng không.

Tại thời điểm này, chương trình của bạn sẽ xuất ra hai dòng tương tự như sau:

NW 0 1
SU 1 0

Điều này chỉ ra rằng chiếc máy bay đầu tiên của bạn sẽ di chuyển theo NorthWest, mà không quay đầu từ tiêu đề hiện tại của nó và bắn nếu có thể. Máy bay thứ hai của bạn sẽ đi du lịch SouthUp, quay mặt về phía SouthUp, không bắn.

Bây giờ bạn được ROUNDS LEFT 18theo dõi, NEW TURNv.v ... Điều này tiếp tục cho đến khi ai đó thắng hoặc vòng đấu kết thúc, tại thời điểm đó bạn nhận được một NEW FIGHTdòng khác với số lần chiến đấu và điểm số được cập nhật, có thể đi trước a NEW OPPONENT.


Nếu bất cứ ai cần giúp đỡ với thử thách này, bạn có thể tham gia vào cuộc trò chuyện mà tôi đã tạo cho thử thách này.
overactor

Các máy bay bắt đầu hướng Đông / Tây hay Bắc / Nam? hay cái gì khác?
bút danh

2
@overactor có lỗi trong mã thời gian hồi chiêu. Bạn đang sử dụng simulationMove trong phần "Tính toán vị trí mới", phần này giảm thời gian hồi chiêu ngoài việc tìm vị trí mới. Điều này có nghĩa là một chiếc máy bay có thể bắn mỗi lượt nếu họ bỏ qua bộ đếm thời gian hồi chiêu của chính họ.
Sparr

2
Đối với những người có thể thấy nó hữu ích, regex này sẽ tìm kiếm thông qua nhật ký để tìm nơi máy bay của bạn bắn ^ Move (. *?) Bắn: true $ (thay thế "Move" bằng tên của bạn và đảm bảo rằng nó không bắt mới dòng)
user2813274

1
đây là một cam kết cho trình bao bọc máy bay của tôi, cùng với một mặt phẳng trăn câm. Tôi thích nó nếu ai đó sẽ viết một mặt phẳng thông minh hơn bằng perl / python / lua / bash / bất cứ điều gì và cho tôi một số phản hồi về nếu / cách trình bao bọc hoạt động cho bạn. github.com/sparr/Dogfight-KOTH/commit/, nếu mọi người có thể / sẽ sử dụng điều này, chúng tôi có thể đưa nó vào repo của @ overactor và cho phép gửi ngôn ngữ tùy ý.
Sparr

Câu trả lời:


5

Trận chiến

Ý tưởng ban đầu của tôi là bắn một máy bay địch bằng cả hai máy bay của mình cùng một lúc, nhưng tôi không thể làm được ... Vì vậy, đây là một chiếc máy bay cố gắng tránh xa các bức tường và ra khỏi tầm bắn của kẻ thù. Các máy bay không bao giờ nên va chạm cũng không bắn các máy bay thân thiện.

Chỉnh sửa: phương thức possibleHitsluôn trả về 0, sau khi sửa nó và thêm một vài cải tiến nhỏ, nó hoạt động tốt hơn trước.

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
Bạn hiện đang là mục tốt nhất thứ hai, sau PredictAndAvoid. Bạn giành chiến thắng trước mọi mục khác, nhưng rút ra khá nhiều. Chống lại Dự đoánAndAvoid, Whirligig quản lý để thực thi nhiều chiến thắng và hòa hơn bạn làm. Tốt nhập bất kể!
overactor

1
@overactor Cảm ơn bạn đã đóng góp! Điều đó có nghĩa là tôi phải làm việc tại phần quay phim ...
CommonGuy

1
Tôi mới thực hiện thêm một số thử nghiệm, có vẻ như bạn thua ít hơn Whirligig so với PredictAndAvoid, PredictAndAvoid quản lý cách chiến thắng nhiều hơn mặc dù, đây là dữ liệu cho 2000 trận đấu: PredictAndAvoid: 1560 Whirligig: 138 | Dự đoánAndAvoid: 1564 Crossfire: 125 | Whirligig: 25 Crossfire: 600
overactor

@overactor Tìm thấy thời gian để cải thiện trình của tôi. Bây giờ, đôi khi nó thắng, hòa và thua trước PredictAndAvoid.
CommonGuy

1
Hoàn thành tốt, sau 10.000 trận đánh: SCORE: Dự đoánAndAvoid: 1240 Crossfire: 6567
overactor

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
hiện đang đánh bại Whirligig gần như mọi lúc. Cần theo dõi một lỗi trong mã tránh bắn kẻ thù.
Sparr

1
đã sửa lỗi. 0 thua trước đối thủ hiện tại.
Sparr

2
đã làm việc giảm rút, tiến bộ đáng kể. cần máy bay địch thông minh hơn trước khi tôi có thể tiến bộ hơn nhiều.
Sparr

1
Tôi sẽ ưu tiên di chuyển đến một không gian an toàn, phía trên chụp không an toàn
Cruncher

1
@Cruncher đã làm điều đó trong bản sao địa phương của tôi và nó cải thiện hiệu suất một vài% so với các đối thủ hiện tại. Ngoài ra bây giờ tránh va chạm khi tôi không đi trước trên các máy bay còn sống. Cập nhật sắp tới!
Sparr

18

Trình hiển thị 3D Dogfight

Tôi đã viết một visualizer nhỏ, nhanh cho thử thách này. Các tập tin mã và jar nằm trên repo github của tôi: https://github.com/Hungary-Dude/DogfightVisualizer
Nó được tạo bằng libGDX ( http://libgdx.com ). Ngay bây giờ giao diện người dùng khá tệ, tôi đã thực hiện việc này nhanh chóng.

Tôi chỉ đang học cách sử dụng Git và Gradle vì vậy hãy bình luận nếu tôi làm sai

Chạy dist/dogfight.bathoặc dist/dogfight.shđể xem DumbPlanes hoạt động!

Để xây dựng từ nguồn, bạn sẽ cần tích hợp Gradle ( http://gradle.org ) và Gradle cho IDE của bạn, nếu bạn có. Sau đó nhân bản repo và chạy gradlew desktop:run. Hy vọng Gradle sẽ nhập tất cả các thư viện cần thiết. Lớp chính là zove.koth.dogfight.desktop.DesktopLauncher.

Chạy mà không cần nhập

Sao chép bất kỳ tập tin lớp máy bay vào dist/. Sau đó, chạy dist/desktop-1.0.jarvới lệnh này:

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

Tôi sẽ cập nhật khi nguồn cho bộ điều khiển Planes được cập nhật, nhưng để tự cập nhật, bạn sẽ cần thêm một số mã vào Planes.Controller. Xem github readme để biết thông tin về điều này.

Đây là một ảnh chụp màn hình: Ảnh chụp màn hình

Nếu bạn có bất kỳ câu hỏi hoặc đề nghị, để lại nhận xét bên dưới!


Điều này thật tuyệt vời, tôi đã có một dự án được thiết lập nơi tôi đã thêm các lớp máy bay, làm thế nào để tôi chạy trình hiển thị với các mặt phẳng này? Có lẽ điều này sẽ được giải thích tốt hơn trong cuộc trò chuyện mặc dù. Theo gợi ý, sẽ rất tuyệt nếu bạn có thể dán vào một bản ghi tối thiểu của một trận đấu và sau đó bước qua trận đấu đó, tôi tin rằng bạn có thể đã gắn mic lên các mặt phẳng, các mặt phẳng sẽ bắt đầu ở sàn và trần nhà tương ứng. Công việc tuyệt vời mặc dù !!
overactor

Tôi lấy Point3Ds đại diện cho vị trí mặt phẳng và trừ 6,5 từ mỗi tọa độ để di chuyển chúng vào chế độ xem. Một cái gì đó giống như plane.transform.setToTranslation(new Vector3(point3d.x-6.5f,point3d.y-6.5f,point3d.z-6.5f))Không có máy bay dường như vượt ra khỏi giới hạn nên tôi nghi ngờ có gì đó không ổn
DankMeme

Đợi đã, bạn đang sử dụng trục y làm chiều cao phải không? (giống như trong hầu hết các trò chơi tôi cho rằng) Trong hệ thống của tôi, z đại diện cho chiều cao, không quan trọng lắm, vì nó đối xứng
overactor

Ohhhhhhh tôi hiểu rồi. Xin lỗi tôi không thực sự nhìn nhiều vào mã của bạn. Tôi vừa dịch trực tiếp Point3Ds sang libgdx Vector3s. Nhân tiện, tôi sẽ đi trong một tuần hoặc bắt đầu từ ngày mai. Xin lỗi nếu tôi không ở đây nếu bạn cần một cái gì đó. Tôi sẽ cố gắng kiểm tra trong khi đi.
DankMeme

12

EmoFockeWulf

Anh ấy trở lại. Anh ta đã bỏ đói chính mình tới 224 byte. Anh ta không biết làm thế nào anh ta kết thúc như thế này.

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
Điều này là nghiêm túc ra khỏi tầm tay bây giờ. Làm thế nào về chúng ta trục xuất anh ta đến bài sơ hở tiêu chuẩn mãi mãi?
dùng80551

13
@ user80551 Tôi nghĩ rằng đó là một phong cách chơi hợp lệ, bất kể. Không có lý do để trục xuất nó.
Seiyria

3
Anh ấy béo hơn 47 byte so với trước đây!
johnchen902

2
Anh ta có thể tự sát nhanh hơn thế này. Không hiệu quả lắm emo.
Sparr

2
@Sparr có, nhưng sau đó nó sẽ có số byte cao hơn và nó sẽ mất đi sự trớ trêu không phải lúc nào cũng mất. : P
cjfaure

10

Weeeeeeeeeeee - 344 byte sau khi xóa khoảng trắng

Có vòng lặp tuyệt vời thứ n. Không thể mất nếu bạn đang thực hiện các vòng lặp.

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

EDIT: rõ ràng khi máy bay của tôi bắt đầu như đội 2, họ vừa đâm thẳng vào tường. Tôi nghĩ rằng tôi đã sửa nó ngay bây giờ. Hy vọng.


Tuyên bố trở lại của bạn là không hợp pháp. Trong Java, để tạo các mảng đối tượng chỉ định tất cả nội dung trên một dòng bạn cần sử dụng, new Type[]{item1, item2, ...}vì vậy trong trường hợp này bạn sẽ córeturn new Move[]{new Move(d,z,a),new Move(d,z,a^=z)};
DankMeme

Ngoài ra hãy thử browxy.com nếu bạn chưa tải xuống IDE. (Nó không mạnh chút nào nhưng nó hoạt động)
DankMeme

cảm ơn, tôi quên nếu điều đó làm việc hay không. tôi chỉ không muốn tải xuống các lớp của mình để làm cho tất cả các kế thừa và gói hoạt động.
bút danh

Sau khi chạy các máy bay của bạn với mã mới, nó chỉ trả về S và SU và chết trong vòng 15 mỗi lần. Bất cứ ý tưởng tại sao?
overactor

hmm ... không. Rõ ràng tôi đã làm hỏng với sự thay đổi của tôi. đã thực sự hy vọng nó sẽ hoạt động .... sẽ hoàn tác chỉnh sửa.
bút danh

6

Máy bay di chuyển và bắn

Tránh các bức tường bằng cách tìm thấy khi nó ở gần bức tường và xoay, bắn khi có thể.

    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?
    }
}     

Tuyên bố miễn trừ trách nhiệm: Tôi hoàn toàn không phải là một lập trình viên Java, vì vậy nếu tôi làm hỏng bất cứ điều gì, xin vui lòng sửa nó cho tôi!


Tôi chưa thử nó, nhưng điều này sẽ không hoạt động, bạn đang cố gắng quay 180 độ cùng một lúc. Để gợi ý, hãy thử N-> NU-> U-> SU-> S thay cho N-> S hoặc thay thế U bằng D nếu mái nhà gần sàn hơn.
overactor

@overactor: Tôi đã bỏ lỡ một you may only change your angle by 45 degreeschút.
Kyle Kanos

Không có thăm dò, không nên quá khó để sửa chữa.
overactor

Bạn có thể nên sử dụng HashMap <String> thay vì Hashtable. Nếu không, new Direction(wayToGo.get(currentDirection))sẽ không hoạt động vì nó quên chuyển sang String. wayToGo.put sau trường cũng không hợp lệ, hãy đặt nó vào một khối {wayToGo.put (blah); blah;} hoặc trong hàm tạo.
Luna

7
Bây giờ nó đã chiến thắng mọi thứ nhờ vào việc không bay vào tường.
overactor

6

Vòng xoáy

Cả hai mặt phẳng đều hướng về trung tâm (ish), sau đó lặp lại trong khi chụp càng thường xuyên càng tốt. Một trong ba trục được chọn cho mỗi lần chiến đấu và cặp này luôn xoay quanh cùng một trục theo hai hướng ngược nhau.

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

DumbPlanes cố gắng hết sức để không bay vào tường, nhưng họ không thông minh lắm về điều đó và cuối cùng thường xuyên đâm vào tường. Họ cũng thỉnh thoảng bắn, nếu chỉ họ biết họ đang bắn vào cái gì.

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 - chưa hoạt động):

Anh ta không thực sự sử dụng tất cả các động thái có sẵn. Nhưng anh ta cố gắng bắn hạ kẻ thù và không đâm vào tường.

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
Nhưng nó có thể làm một cuộn thùng?
Erty Seidohl

1
Tôi đang có một ngoại lệ, Đây là dấu vết ngăn xếp: Ngoại lệ trong luồng "chính" java.lang.NullPulumException tại Planes.Starfox $ 2.test (Starfox.java:99) tại Planes.Starfox $ 2.test (Starfox.java:1 ) tại java.util.ArrayList.remove If (Nguồn không xác định) tại Planes.Starfox.act (Starfox.java:90) tại Planes.Contoder.fight (Controller.java:141) tại Planes.Contoder.matchUp (Controller.java: 85) tại Planes.Controll.main (Controller.java:35) Tôi đã phải thêm Gói máy bay, nếu không nó sẽ không được biên dịch, có lẽ điều đó có liên quan đến nó.
overactor

Tôi đã có nó để chạy nhưng nó không hoạt động tốt như mong đợi, tôi nghĩ vấn đề có thể là tốt nhất là quá thường xuyên.
overactor

Có vẻ như Starfox di chuyển vào lửa địch chứ không phải từ đó, bạn có thể thấy những gì đang xảy ra ở đây.
overactor

3

Nguy hiểm

  • Được viết bằng Python và các giao diện với trình bao bọc mã không phải Java được viết bởi Sparr.

  • Có phải tất cả toán học của nó trong Python thuần túy và hoàn toàn không được tối ưu hóa. Chậm một chút.

  • Cấu hình cao và mở rộng.

  • Làm rất tốt chống lại đệ trình trong quá khứ. Thắng 2: 1 chiến đấu cho mỗi người thua Crossfirehoặc hoặc PredictAndAvoidthắng 98 +% tổng số trận đấu với các đối thủ khác.

Bao gồm công cụ trực quan tùy chọn của riêng mình:

Chiến đấu Crossfire/ PredictAndAvoid, với xếp hạng khu vực nguy hiểm cùng tên được hiển thị trong tập xung quanh:

Video về bốn chiếc máy bay chiến đấu trong hai vòng với lưới voxel màu biến hình xung quanh chúng.

  • Hình dung bằng cách sử dụng nipy_spectralcolourmap từ matplotlib. Các tọa độ nguy hiểm hơn được hiển thị bằng cách sử dụng các màu gần hơn với màu đỏ / trắng trong phổ điện từ và được vẽ bằng các chấm lớn hơn.

  • Nguy hiểm: Xanh lam <Xanh <Vàng <Đỏ <Xám nhạt

Hiệu suất:

1000 vòng với tám thuật toán hàng đầu khác trên bảng xếp hạng:

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.

Mã số:

#!/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))
```
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.