3D: Duel ไม่ต่อเนื่อง Dogfighting (ตอนนี้เปิดให้ส่งที่ไม่ใช่ Java)


31

UPDATE: isSuicidal () ถูกเพิ่มเข้ามาในคลาสเครื่องบินซึ่งช่วยให้คุณตรวจสอบว่าเครื่องบินอยู่ในเส้นทางที่ชนกับกำแพงได้หรือไม่ !!

UPDATE: updateCoolDown () แยกออกจาก simulateMove ()

ปรับปรุง: wrapper รายการที่ไม่ใช่ Java เขียนโดยSparrพร้อมสำหรับการทดสอบดูความคิดเห็น

อัป เดต Zove Games ได้เขียนโปรแกรมสร้างภาพ 3 มิติที่ยอดเยี่ยมสำหรับ KOTH นี่คือวิดีโอ youtube ที่น่ารังเกียจของ PredictAndAVoid Fighting PredictAndAVoid

ฟังก์ชั่น simulateMove () ของคลาสเครื่องบินได้รับการแก้ไขเล็กน้อยดังนั้นจึงไม่อัปเดตความเย็นอีกต่อไปให้ใช้ฟังก์ชั่น updateCoolDown () ใหม่หลังจากถ่ายภาพแล้ว ใหม่ isSuicidal () ผลตอบแทนจริงถ้าเครื่องบินถูกผูกไว้เพื่อจบลงตายใช้มันเพื่อตัดการเคลื่อนไหวของศัตรูและหลีกเลี่ยงการชนกำแพง ในการรับรหัสที่อัปเดตเพียงแค่แทนที่คลาส Controller และ Plane ด้วยรหัสที่อยู่ใน gitub repo

ลักษณะ

เป้าหมายของความท้าทายนี้คือการเขียนรหัสเครื่องบิน dogfightingสองลำที่จะเผชิญหน้ากับเครื่องบินสองลำโดยผู้เข้าแข่งขันคนอื่น ทุกเทิร์นที่คุณย้ายหนึ่งช่องว่างและมีโอกาสที่จะยิง เพียงเท่านี้ก็ง่ายเหมือนกัน

เกือบแล้ว ...

สนามกีฬาและการเคลื่อนไหวที่เป็นไปได้

สนามกีฬาเป็นกำแพงขนาด 14x14x14 อวกาศ เครื่องบินของผู้เข้าประกวด 1 เริ่มที่สถานที่ (0,5,0) และ (0,8,0) และของผู้เข้าประกวด 2 ที่ (13,5,13) และ (13,8,13) เครื่องบินทุกลำเริ่มต้นด้วยการบินในแนวนอนห่างจากผนังแนวตั้งที่อยู่ใกล้ที่สุด

ตอนนี้เนื่องจากคุณกำลังบินเครื่องบินและไม่ใช่เฮลิคอปเตอร์คุณไม่สามารถเปลี่ยนทิศทางได้ตามต้องการหรือหยุดเคลื่อนที่ดังนั้นเครื่องบินแต่ละลำจึงมีทิศทางและจะย้ายไพ่หนึ่งใบในทิศทางนั้นทุก ๆ ตา

The possible directions are: North(N), South(S), East(E), West(W), Up(U) and Down(D) and any logical combination of those six. Where the NS axis corresponds to the x axis, WE to y and DU to z. NW, SU and NED come to mind as possible examples of directions; UD is a great example of an invalid combination.

แน่นอนว่าคุณสามารถเปลี่ยนทิศทางของเครื่องบินได้ แต่มีข้อ จำกัด คุณสามารถเปลี่ยนทิศทางของคุณได้สูงสุด 45 องศาเท่านั้น ในการมองเห็นสิ่งนี้ให้จับลูกบาศก์รูบิคของคุณ (ฉันรู้ว่าคุณมี) และจินตนาการว่าลูกบาศก์เล็ก ๆ ชั้นนอกทั้ง 26 ก้อนเป็นทิศทางที่เป็นไปได้ (ทิศทางตัวอักษรตัวหนึ่งคือใบหน้าทิศทางของตัวอักษรสองตัวคือขอบ หากคุณกำลังมุ่งหน้าไปในทิศทางที่แสดงโดยคิวบ์เล็ก ๆ คุณสามารถเปลี่ยนทิศทางไปยังแต่ละคิวบ์ที่สัมผัสกับคุณ (นับจำนวนการสัมผัสในแนวทแยงมุม แต่เพียงสัมผัสอย่างเห็นได้ชัดเท่านั้นที่ไม่ได้สัมผัสผ่านคิวบ์)

หลังจากที่ทุกระนาบได้ระบุทิศทางที่พวกเขาต้องการเปลี่ยนพวกมันจะทำเช่นนั้นและย้ายไทล์หนึ่งไปพร้อม ๆ กัน

คุณยังสามารถเลือกที่จะย้ายไปในทิศทางที่ถูกต้อง แต่ยังคงบินไปในทิศทางที่คุณกำลังไปโดยเปลี่ยนทิศทางของคุณไปเป็นทิศทางที่คุณย้ายไป นี่เป็นความคล้ายคลึงกับความแตกต่างระหว่างรถที่วิ่งไปรอบ ๆ มุมถนนกับรถที่เปลี่ยนเลน

ยิงและกำลังจะตาย

คุณสามารถยิงได้มากที่สุดต่อรอบและจะต้องตัดสินใจในเวลาเดียวกันกับที่คุณตัดสินใจว่าจะบินไปทางไหนและคุณต้องการให้เครื่องบินของคุณ (และโดยการขยายปืนของคุณ) ชี้ไปในทิศทางเดียวกันหรือไม่ กระสุนถูกยิงทันทีหลังจากที่เครื่องบินเคลื่อนที่ มีการเทลงหนึ่งครั้งหลังจากยิงในเทิร์นที่สามคุณก็พร้อมที่จะกลับไปอีกครั้ง คุณสามารถยิงไปในทิศทางที่คุณบินเท่านั้นกระสุนนัดหนึ่งและบินเป็นเส้นตรงจนกว่าจะชนกับกำแพงหรือเครื่องบิน

เมื่อคำนึงถึงวิธีที่คุณสามารถเปลี่ยนทิศทางและ 'เปลี่ยนเลน' ซึ่งหมายความว่าคุณสามารถขู่คอลัมน์ได้ถึง 3x3 บรรทัดต่อหน้าคุณนอกจากนี้ในแนวทแยงบางเส้นเดียว

ถ้ามันกระทบกับระนาบระนาบนี้จะตายและหายไปจากกระดานทันที (เพราะมันระเบิดหรืออะไรบางอย่างโดยสิ้นเชิง) กระสุนสามารถยิงได้เพียงหนึ่งระนาบเท่านั้น กระสุนถูกยิงพร้อมกันดังนั้นเครื่องบินสองลำสามารถยิงกันได้ กระสุนสองนัดไม่สามารถชนกันในอากาศได้ (เศร้าฉันรู้)

เครื่องบินสองลำสามารถชนกันได้ (หากพวกเขาลงเอยด้วยลูกบาศก์เดียวกันและไม่ใช่ถ้าพวกเขาข้ามกันโดยไม่ต้องลงเอยด้วยระนาบเดียวกัน) และผลลัพธ์นี้ทำให้ทั้งสองลำกำลังจะตาย (และระเบิดโดยสิ้นเชิง) คุณยังสามารถบินไปที่กำแพงซึ่งจะส่งผลให้เครื่องบินตกอยู่ในความสงสัยและถูกวางไว้ที่มุมเพื่อคิดเกี่ยวกับการกระทำของมัน ชนได้รับการจัดการก่อนที่จะยิง

การสื่อสารกับคอนโทรลเลอร์

ฉันจะยอมรับรายการเป็นภาษาจาวาและภาษาอื่น ๆ หากรายการของคุณเป็นแบบจาวาคุณจะได้รับอินพุตผ่าน STDIN และจะแสดงผลผ่าน STDOUT

หากรายการของคุณอยู่ใน java รายการของคุณต้องขยายคลาสต่อไปนี้:

package Planes;

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

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

    ...

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

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

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

อินสแตนซ์ที่สร้างขึ้นของคลาสนั้นจะคงอยู่ตลอดการแข่งขันดังนั้นคุณสามารถเก็บข้อมูลใด ๆ ที่คุณต้องการเก็บไว้ในตัวแปร อ่านความคิดเห็นในรหัสสำหรับข้อมูลเพิ่มเติม

ฉันยังให้คุณกับผู้ช่วยชั้นเรียนต่อไปนี้:

package Planes;

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

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

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

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

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

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

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

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

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

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

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

    public void setCoolDown(int coolDown) {}

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

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

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

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

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


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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

คุณสามารถสร้างอินสแตนซ์ของคลาสเหล่านี้และใช้ฟังก์ชันใด ๆ ได้มากเท่าที่คุณต้องการ คุณสามารถค้นหาโค้ดเต็มรูปแบบสำหรับการเรียนผู้ช่วยเหล่านี้ที่นี่

นี่คือตัวอย่างลักษณะของการเข้าร่วมของคุณ (หวังว่าคุณจะทำได้ดีกว่าที่ฉันทำแม้ว่าการจับคู่ส่วนใหญ่กับเครื่องบินเหล่านี้จะจบลงด้วยการที่พวกเขาบินไปที่กำแพงแม้ว่าพวกเขาจะพยายามหลีกเลี่ยงกำแพง)

package Planes;

public class DumbPlanes extends PlaneControl {

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

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

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

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

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

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

        return moves;
    }

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

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

DumbPlanes จะเข้าร่วมทัวร์นาเมนต์พร้อมกับผลงานอื่น ๆ ดังนั้นหากคุณจบมันเป็นความผิดของคุณเองที่ไม่ได้ทำดีกว่า DumbPlanes

ข้อ จำกัด

ข้อ จำกัด ที่กล่าวถึงในKOTH wikiใช้:

  • ความพยายามที่จะคนจรจัดกับตัวควบคุมรันไทม์หรือการส่งอื่น ๆ จะถูกตัดสิทธิ์ การส่งทั้งหมดควรทำงานกับอินพุตและพื้นที่เก็บข้อมูลที่ได้รับเท่านั้น
  • ไม่ควรเขียนบอทเพื่อเอาชนะหรือสนับสนุนบอทอื่น ๆ โดยเฉพาะ (สิ่งนี้อาจเป็นที่ต้องการในบางกรณีที่หายาก แต่ถ้านี่ไม่ใช่แนวคิดหลักของความท้าทาย
  • ฉันขอสงวนสิทธิ์ในการตัดสิทธิ์การส่งผลงานที่ใช้เวลาหรือหน่วยความจำมากเกินไปในการเรียกใช้การทดลองที่มีทรัพยากรในปริมาณที่เหมาะสม
  • บอตจะต้องไม่ใช้กลยุทธ์เดียวกันกับกลยุทธ์ที่มีอยู่ไม่ว่าจะโดยเจตนาหรือโดยไม่ตั้งใจ

ทดสอบการส่งของคุณ

ดาวน์โหลดรหัสควบคุมจากที่นี่ เพิ่มการส่งของคุณเป็น Something.java แก้ไข Controller.java เพื่อรวมรายการสำหรับเครื่องบินของคุณในรายการ [] และชื่อ [] ทุกอย่างที่คอมไพล์เป็นโครงการคราสหรือแล้วเรียกใช้ควบคุมด้วยjavac -d . *.java java Planes/Controllerบันทึกการแข่งขันจะมีขึ้นtest.txtพร้อมป้ายบอกคะแนนในตอนท้าย นอกจากนี้คุณยังสามารถโทรหาmatchUp()โดยตรงโดยใช้สองรายการเป็นอาร์กิวเมนต์เพื่อทดสอบเครื่องบินสองรายการต่อกัน

ชนะการต่อสู้

ผู้ชนะของการต่อสู้คือผู้ที่มีเครื่องบินลำสุดท้ายบินหากหลังจาก 100 รอบยังมีเหลืออีก 1 ทีมทีมที่มีเครื่องบินมากที่สุดจะเป็นผู้ชนะ ถ้านี่เท่ากันมันคือการจับฉลาก

การให้คะแนนและการแข่งขัน

การแข่งขันอย่างเป็นทางการครั้งต่อไปจะเริ่มขึ้นเมื่อเงินรางวัลปัจจุบันหมด

แต่ละรายการจะต่อสู้กันทุกรายการ (อย่างน้อย) 100 ครั้งผู้ชนะของการแข่งขันแต่ละครั้งจะเป็นรายการที่มีผู้ชนะมากที่สุดจาก 100 รายการและจะได้รับ 2 คะแนน ในกรณีที่เสมอกันทั้งสองรายการจะได้รับ 1 คะแนน

ผู้ชนะการแข่งขันคือผู้ที่มีคะแนนมากที่สุด ในกรณีที่เสมอผู้ชนะคือผู้ที่ชนะในการแข่งขันระหว่างรายการที่เข้าร่วม

ขึ้นอยู่กับจำนวนของรายการจำนวนของการต่อสู้ระหว่างรายการจะเพิ่มขึ้นอย่างมีนัยสำคัญฉันอาจเลือก 2-4 รายการที่ดีที่สุดหลังจากทัวร์นาเมนต์แรกและตั้งค่าทัวร์นาเมนต์ระดับสูงระหว่างรายการเหล่านั้นที่มีการต่อสู้มากขึ้น การต่อสู้)

(เบื้องต้น) กระดานคะแนน

เรามีรายการใหม่ที่มีอันดับสองในการแข่งขันที่น่าตื่นเต้นอีกครั้งดูเหมือนว่า Crossfire นั้นยากที่จะยิงสำหรับทุกคนยกเว้น PredictAndAvoid โปรดทราบว่าทัวร์นาเมนต์นี้ดำเนินการด้วยการต่อสู้เพียง 10 ครั้งระหว่างเครื่องบินแต่ละชุดและไม่ได้มีการแสดงถึงความแม่นยำของสิ่งต่างๆ

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

นี่คือตัวอย่างของเอาต์พุตจาก wrapper ที่ไม่ใช่ Java:

NEW CONTEST 14 20 บ่งชี้ว่าการแข่งขันใหม่เริ่มต้นขึ้นแล้วในเวทีขนาด 14x14x14 และจะมีการเปิด 20 รอบต่อการต่อสู้

NEW OPPONENT 10 บ่งบอกว่าคุณกำลังเผชิญหน้ากับคู่ต่อสู้คนใหม่และคุณจะต้องสู้กับคู่ต่อสู้นี้ 10 ครั้ง

NEW FIGHT 5 3 2 เป็นการบ่งชี้ว่าการต่อสู้ครั้งใหม่กับฝ่ายตรงข้ามปัจจุบันกำลังเริ่มขึ้นและคุณได้ต่อสู้กับฝ่ายตรงข้าม 5 ครั้งจนถึงตอนนี้ชนะ 3 ครั้งและแพ้ 2 ครั้ง

ROUNDS LEFT 19 หมายถึงมี 19 รอบที่เหลืออยู่ในการต่อสู้ปัจจุบัน

NEW TURN บ่งบอกว่าคุณกำลังจะได้รับข้อมูลสำหรับเครื่องบินทั้งสี่ลำสำหรับการต่อสู้รอบนี้

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

สี่บรรทัดเหล่านี้บ่งชี้ว่าเครื่องบินทั้งสองลำของคุณยังมีชีวิตอยู่ที่พิกัด [13,8,13] และ [13,5,13] ตามลำดับทั้งสองหันหน้าไปทางทิศเหนือทั้งคู่มีศูนย์คูลดาวน์ เครื่องบินข้าศึกลำแรกตายไปแล้วและดวงที่สองยังมีชีวิตอยู่ที่ [0,8,0] และหันหน้าไปทางทิศใต้โดยไม่มีคูลดาวน์

ณ จุดนี้โปรแกรมของคุณควรแสดงผลสองบรรทัดคล้ายกับที่แสดงต่อไปนี้:

NW 0 1
SU 1 0

นี่เป็นการระบุว่าเครื่องบินลำแรกของคุณจะเดินทางไปทางทิศตะวันตกเฉียงเหนือโดยไม่ต้องหันหน้าจากหัวปัจจุบันและยิงได้ เครื่องบินลำที่สองของคุณจะเดินทางไปทางทิศตะวันออกเฉียงใต้หันหน้าไปทางทิศตะวันออกเฉียงใต้ไม่ใช่ถ่ายภาพ

ตอนนี้คุณจะได้รับROUNDS LEFT 18ตามมาด้วยNEW TURNฯลฯ นี้ต่อไปจนกว่าจะชนะใครบางคนหรือรอบครั้งจากจุดที่คุณจะได้รับอีกสายที่มีการปรับปรุงการนับคะแนนการต่อสู้และอาจจะนำหน้าด้วยNEW FIGHTNEW OPPONENT


หากใครต้องการความช่วยเหลือเกี่ยวกับความท้าทายนี้คุณสามารถเข้าสู่การแชทที่ฉันสร้างขึ้นสำหรับความท้าทายนี้
overactor

เครื่องบินเริ่มมุ่งหน้าไปทางตะวันออก / ตะวันตกหรือเหนือ / ใต้หรือไม่? หรืออย่างอื่น?
นามแฝง 117

2
@overactor มีข้อผิดพลาดในรหัสคูลดาวน์ คุณกำลังใช้ simulateMove ในส่วน "คำนวณตำแหน่งใหม่" ซึ่งจะลดคูลดาวน์ลงนอกเหนือจากการค้นหาตำแหน่งใหม่ นี่หมายความว่าเครื่องบินสามารถยิงได้ในทุก ๆ ทางหากพวกเขาไม่สนใจคูลดาวน์
Sparr

2
สำหรับผู้ที่อาจพบว่ามีประโยชน์ regex นี้จะค้นหาผ่านบันทึกเพื่อค้นหาตำแหน่งที่เครื่องบินของคุณยิง ^ Move (. *?) shoot: true $ (แทนที่ "ย้าย" ด้วยชื่อของคุณและตรวจสอบให้แน่ใจว่าไม่จับภาพใหม่ บรรทัด)
user2813274

1
นี่คือความมุ่งมั่นสำหรับเสื้อคลุมเครื่องบินของฉันพร้อมกับเครื่องบินหลามโง่ ฉันจะรักถ้ามีใครสักคนจะเขียนสมาร์ทโฟนในรูปแบบ perl / python / lua / bash / อะไรก็ตามและให้ข้อเสนอแนะแก่ฉันเกี่ยวกับ if / how the wrapper ที่เหมาะกับคุณ github.com/sparr/Dogfight-KOTH/commit/…หากผู้คนสามารถ / จะใช้สิ่งนี้เราสามารถเข้าไปที่ repo ของ @ overactor และอนุญาตให้ส่งภาษาโดยพลการ
Sparr

คำตอบ:


5

ลูกหลง

ความคิดเริ่มต้นของฉันคือยิงเครื่องบินศัตรูด้วยเครื่องบินทั้งสองของฉันในเวลาเดียวกัน แต่ฉันไม่สามารถทำได้ ... ดังนั้นนี่คือเครื่องบินที่พยายามอยู่ห่างจากกำแพงและอยู่นอกขอบเขตการยิงของ ศัตรู. เครื่องบินไม่ควรชนหรือยิงเครื่องบินที่เป็นมิตร

แก้ไข:เมธอดpossibleHitsส่งคืน 0 เสมอหลังจากแก้ไขแล้วเพิ่มการปรับปรุงเล็ก ๆ น้อย ๆ หลายครั้งมันจะทำงานได้ดีกว่า แต่ก่อน

package Planes;

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

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

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

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

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

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

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

        return moves;
    }

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

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

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

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

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

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

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

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

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

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

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

1
ขณะนี้คุณเป็นรายการที่ดีที่สุดอันดับสองรองจาก PredictAndAvoid คุณชนะในการเข้าร่วมอื่น ๆ แต่วาดค่อนข้างมาก กับ PredictAndAvoid, Whirligig จะจัดการเพื่อบังคับให้ชนะและดึงมากกว่าที่คุณทำ รายการที่ดีโดยไม่คำนึงถึง!
overactor

1
@overactor ขอบคุณสำหรับการป้อนข้อมูลของคุณ! นั่นหมายความว่าฉันต้องทำงานที่ส่วนถ่ายภาพ ...
ซื้อธรรมดา

1
ฉันเพิ่งทำการทดสอบเพิ่มเติมดูเหมือนว่าคุณจะแพ้ Whirligig น้อยกว่า PredictAndAvoid น้อยลง PredictAndAvoid จัดการวิธีชนะมากกว่าแม้ว่านี่คือข้อมูลสำหรับการต่อสู้ 2000 ครั้ง: PredictAndAvoid: 1560 Whirligig: 138 | PredictAndAvoid: 1564 Crossfire: 125 | Whirligig: 25 Crossfire: 600
overactor

@overactor หาเวลาเพื่อปรับปรุงการส่งของฉัน ตอนนี้บางครั้งก็ชนะเสมอและแพ้กับ PredictAndAvoid
CommonGuy

1
ทำได้ดีมากหลังจาก 10,000 การต่อสู้: SCORE: PredictAndAvoid: 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
ปัจจุบัน Whirligig เต้นเกือบทุกครั้ง จำเป็นต้องติดตามข้อผิดพลาดในรหัสหลีกเลี่ยงศัตรู
Sparr

1
แก้ไขข้อผิดพลาด 0 การสูญเสียแก่คู่ต่อสู้ในขณะนี้
Sparr

2
ทำงานเกี่ยวกับการลดการจับสลากความคืบหน้าสำคัญ ต้องการเครื่องบินศัตรูที่ฉลาดกว่าก่อนที่ฉันจะสามารถก้าวหน้าได้มากขึ้น
Sparr

1
ฉันจะจัดลำดับความสำคัญการย้ายไปยังพื้นที่ปลอดภัยข้างต้นยิงอย่างไม่ปลอดภัย
Cruncher

1
@Cruncher ทำสิ่งนั้นในสำเนาในเครื่องของฉันแล้วและช่วยปรับปรุงประสิทธิภาพเมื่อเทียบกับคู่แข่งปัจจุบันสองสาม% ตอนนี้ยังหลีกเลี่ยงการชนกันเมื่อฉันไม่ได้ไปข้างหน้าบนเครื่องบินที่รอดชีวิต อัพเดทที่จะมา!
Sparr

18

Visualfight 3D Visualizer

ฉันเขียนวิชวลไลเซอร์ขนาดเล็กที่รวดเร็วสำหรับความท้าทายนี้ ไฟล์โค้ดและ jar อยู่ใน repo github ของฉัน: https://github.com/Hungary-Dude/DogfightVisualizer
มันทำโดยใช้ libGDX ( http://libgdx.com ) ตอนนี้ UI ไม่ดีนักฉันรวมตัวกันอย่างรวดเร็ว

ฉันแค่เรียนรู้วิธีใช้ Git และ Gradle ดังนั้นโปรดแสดงความคิดเห็นหากฉันทำอะไรผิด

เรียกใช้dist/dogfight.batหรือdist/dogfight.shดู DumbPlanes พร้อมใช้งาน!

หากต้องการสร้างจากแหล่งที่มาคุณจะต้องรวม Gradle ( http://gradle.org ) และ Gradle สำหรับ IDE ของคุณหากคุณมี จากนั้นโคลน repo gradlew desktop:runและเรียกใช้ หวังว่า Gradle จะนำเข้าห้องสมุดทั้งหมดที่ต้องการ zove.koth.dogfight.desktop.DesktopLauncherชั้นหลักคือ

ทำงานโดยไม่ต้องนำเข้า

คัดลอกไฟล์ระดับเครื่องบินใด ๆ dist/ลงใน จากนั้นรันdist/desktop-1.0.jarด้วยคำสั่งนี้:

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

ฉันจะปรับปรุงเป็นแหล่งข้อมูลสำหรับเครื่องบินควบคุมที่มีการปรับปรุง Planes.Controllerแต่ที่จะปรับปรุงตัวเองคุณจะต้องเพิ่มในรหัสบางอย่างเพื่อ ดู github readme สำหรับข้อมูลเกี่ยวกับเรื่องนี้

นี่คือภาพหน้าจอ: ภาพหน้าจอ

หากคุณมีคำถามหรือข้อเสนอแนะแสดงความคิดเห็นด้านล่าง!


มันยอดเยี่ยมมากฉันมีโครงการที่ฉันเพิ่มคลาสเครื่องบินฉันจะรัน visualizer ด้วยเครื่องบินเหล่านี้ได้อย่างไร นี้อาจจะอธิบายได้ดีขึ้นในการแชทแม้ว่า เป็นข้อเสนอแนะมันจะดีมากถ้าคุณสามารถวางบันทึกการแข่งขันเล็กน้อยแล้วก้าวผ่านการแข่งขันนั้นได้เช่นกันฉันเชื่อว่าคุณอาจเข้าร่วมคนจรจัดขึ้นเครื่องบินควรเริ่มที่พื้นและเพดานตามลำดับ งานยอดเยี่ยมแม้ว่า !!
overactor

ฉันใช้ Point3D ที่แทนตำแหน่งเครื่องบินและลบ 6.5 จากแต่ละพิกัดเพื่อเลื่อนมุมมอง บางสิ่งที่ดูเหมือนplane.transform.setToTranslation(new Vector3(point3d.x-6.5f,point3d.y-6.5f,point3d.z-6.5f))ไม่มีเครื่องบินดูเหมือนจะออกนอกขอบเขตดังนั้นฉันจึงสงสัยว่ามีบางอย่างผิดปกติ
DankMemes

เดี๋ยวก่อนคุณกำลังใช้แกน y เป็นส่วนสูงหรือไม่? (เหมือนในเกมส่วนใหญ่ที่ฉันคิดว่า) ในระบบของฉัน z หมายถึงความสูงไม่ใช่ว่ามันสำคัญมากเพราะมันเป็นแบบสมมาตร
overactor

Ohhhhhhh ฉันเข้าใจแล้ว ขออภัยฉันไม่ได้ดูรหัสของคุณมากนัก ฉันเพิ่งแปล Point3Ds เป็น libgdx Vector3s โดยตรง โดยวิธีการที่ฉันจะออกไปเป็นเวลาหนึ่งสัปดาห์หรือประมาณเริ่มพรุ่งนี้ ขออภัยถ้าฉันไม่ได้อยู่ที่นี่ถ้าคุณต้องการอะไร ฉันจะลองเช็คอินในขณะที่ไม่อยู่
DankMemes

12

EmoFockeWulf

เขากลับมาแล้ว. เขาอดอาหารจนถึง 224 ไบต์ เขาไม่รู้ว่าเขาจะเป็นเช่นนี้ได้อย่างไร

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

13
ตอนนี้กำลังออกจากมืออย่างจริงจัง แล้วเราขับไล่เขาไปที่ช่องโหว่มาตรฐานโพสต์ตลอดไป?
user80551

13
@ user80551 ฉันคิดว่ามันเป็นรูปแบบการเล่นที่ถูกต้องโดยไม่คำนึงถึง ไม่มีเหตุผลที่จะขับไล่มัน
เซย์เรีย

3
เขาอ้วนกว่าเขา 47 ไบต์!
johnchen902

2
เขาสามารถฆ่าตัวตายได้เร็วกว่านี้ emo ไม่ค่อยมีประสิทธิภาพ
Sparr

2
@ Sparr ใช่ แต่แล้วมันจะมีจำนวนไบต์ที่สูงขึ้นและมันจะสูญเสียประชดของการไม่สูญเสียเสมอ : P
cjfaure

10

Weeeeeeeeeeee - 344 ไบต์หลังจากลบช่องว่างออก

การทำลูปและสิ่งต่างๆ ไม่แพ้ถ้าคุณทำลูป

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

แก้ไข:เห็นได้ชัดว่าเมื่อเครื่องบินของฉันเริ่มเป็นทีม 2 พวกเขาชนเข้ากับกำแพงทันที ฉันคิดว่าฉันคงที่ตอนนี้ หวังว่า.


คำชี้แจงการคืนสินค้าของคุณไม่ถูกกฎหมาย ใน Java เพื่อสร้างอาร์เรย์ของวัตถุที่ระบุเนื้อหาทั้งหมดในหนึ่งบรรทัดคุณจำเป็นต้องใช้new Type[]{item1, item2, ...}ดังนั้นในกรณีนี้คุณจะต้องreturn new Move[]{new Move(d,z,a),new Move(d,z,a^=z)};
DankMemes

ลองใช้Browxy.com ด้วยถ้าคุณไม่ได้ดาวน์โหลด IDE (มันไม่ได้ทรงพลังเลย แต่ใช้งานได้)
DankMemes

ขอบคุณฉันลืมไปว่ามันใช้งานได้หรือเปล่า ฉันแค่ไม่ต้องการดาวน์โหลดชั้นเรียนของเขาเพื่อให้การสืบทอดและแพคเกจทำงานได้ทั้งหมด
นามแฝง 117

หลังจากรันเครื่องบินของคุณด้วยรหัสใหม่มันแค่คืนค่า S และ SU และตายในรอบที่ 15 ทุกครั้ง มีความคิดอะไรบ้าง
overactor

อืม ... ไม่ เห็นได้ชัดว่าฉันยุ่งกับการเปลี่ยนแปลงของฉัน หวังว่ามันจะใช้งานได้จริง .... จะยกเลิกการแก้ไข
นามแฝง

6

เครื่องบินย้ายและยิง

หลีกเลี่ยงกำแพงโดยการค้นหาเมื่อใกล้กับกำแพงและหมุนได้เมื่อไหร่ที่ทำได้

    package Planes;

public class MoveAndShootPlane extends PlaneControl {

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

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

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

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

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

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

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

ข้อจำกัดความรับผิดชอบ: ฉันไม่ได้เป็นโปรแกรมเมอร์ Java เลยดังนั้นหากฉันทำอะไรผิดพลาดขึ้นโปรดแก้ไขให้ฉันด้วย!


ฉันยังไม่ได้ทดสอบ แต่ตอนนี้ใช้งานไม่ได้คุณกำลังพยายามหมุน 180 องศาในครั้งเดียว ขอแนะนำให้ลองใช้ N-> NU-> U-> SU-> S แทน N-> S หรือเปลี่ยน U เป็น D หากหลังคาอยู่ใกล้กว่าพื้น
overactor

@overactor: ฉันพลาดไปyou may only change your angle by 45 degreesนิดหน่อย
Kyle Kanos

ไม่มีปัญหาไม่ควรหนักเกินไปที่จะแก้ไข
overactor

คุณควรใช้ HashMap <String> แทน Hashtable มิฉะนั้นnew Direction(wayToGo.get(currentDirection))จะไม่ทำงานเนื่องจากลืมการส่งไปยัง String wayToGo.put หลังจากฟิลด์นั้นไม่ถูกต้องให้ใส่ไว้ในบล็อก {wayToGo.put (blah); blah;} หรือในตัวสร้าง
Luna

7
สำหรับตอนนี้มันชนะทุกอย่างด้วยการไม่บินเข้าไปในกำแพง
overactor

6

whirligig

เครื่องบินทั้งสองมุ่งหน้าไปที่กึ่งกลาง (ish) จากนั้นวนรอบในขณะที่ถ่ายภาพบ่อยที่สุดเท่าที่จะทำได้ หนึ่งในสามแกนถูกเลือกต่อการต่อสู้และทั้งคู่หมุนรอบแกนเดียวกันในทิศทางตรงกันข้ามเสมอ

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 พยายามอย่างหนักที่จะไม่บินเข้าไปในกำแพง แต่พวกมันไม่ค่อยฉลาดนักเกี่ยวกับมันและมักจะจบลงด้วยการชนกำแพง พวกเขายังยิงเป็นบางครั้งถ้าเพียง แต่พวกเขารู้ว่าสิ่งที่พวกเขายิงที่

package Planes;

public class DumbPlanes extends PlaneControl {

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

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

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

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

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

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

        return moves;
    }

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

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

4

Starfox (WIP - ยังไม่ทำงาน):

จริง ๆ แล้วเขาไม่ได้ใช้ประโยชน์จากการเคลื่อนไหวที่มีอยู่ทั้งหมด แต่เขาพยายามยิงศัตรูและไม่ชนเข้ากับกำแพง

package Planes;

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

public class Starfox extends PlaneControl
{

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

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

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

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


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

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

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

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

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

                // remove positions with no future from flight plan

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

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

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

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

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

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

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

            }
        }

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

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

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

7
แต่มันสามารถหมุนม้วนบาร์เรลได้หรือไม่?
Erty Seidohl

1
ฉันได้รับการยกเว้นนี่คือการติดตามสแต็ก: ข้อยกเว้นในเธรด "main" java.lang.NullPointerException ที่ Planes.Starfox $ 2.test (Starfox.java:99) ที่ Planes.Starfox $ 2.test (Starfox.java:1 ) ที่ java.util.ArrayList.removeIf (ไม่ทราบแหล่งที่มา) ที่ Planes.Starfox.act (Starfox.java:90) ที่ Planes.Controller.fight (Controller.java:141) ที่ Planes.Controller.matchUp (Controller.java: 85) ที่ Planes.Controller.main (Controller.java:35) ฉันต้องเพิ่ม Package Planes มิฉะนั้นมันจะไม่คอมไพล์บางทีอาจจะมีบางอย่างที่เกี่ยวข้องกับมัน
overactor

ฉันได้รับมันเพื่อให้ทำงานได้ แต่มันไม่ทำงานตามที่คาดไว้ฉันคิดว่าปัญหาอาจจะดีที่สุดก็เป็นโมฆะบ่อยเกินไป
overactor

ดูเหมือนว่า Starfox จะเคลื่อนเข้าหากองไฟของศัตรูมากกว่าจะเห็นว่าเกิดอะไรขึ้นที่นี่
overactor

3

DangerZoner

  • เขียนใน Python และอินเตอร์เฟสด้วยตัวตัดโค้ดที่ไม่ใช่ Java ซึ่งเขียนโดย Sparr

  • ทำคณิตศาสตร์ทั้งหมดใน Python บริสุทธิ์และไม่ได้เพิ่มประสิทธิภาพอย่างสมบูรณ์ ช้าไปหน่อย

  • กำหนดค่าได้สูงและขยายได้

  • ทำได้ดีมากกับการส่งที่ผ่านมา ชนะ2: 1การต่อสู้สำหรับทุก ๆ คนที่แพ้CrossfireหรือหรือPredictAndAvoidและชนะ98 +%ของการต่อสู้ทั้งหมดกับผู้แข่งขันคนอื่น ๆ

รวมถึงเครื่องมือการสร้างภาพทางเลือกของตัวเอง:

การต่อสู้Crossfire/ PredictAndAvoidด้วยการจัดอันดับเขตอันตรายในบาร์ที่มองเห็นได้ในระดับเสียงโดยรอบ:

วิดีโอการยิงสุนัขสี่ลำในสองรอบด้วยตารางสี voxel ที่แปรเปลี่ยนไปรอบ ๆ พวกเขา

  • มองเห็นการใช้nipy_spectralcolourmap matplotlibจาก มีการแสดงพิกัดที่อันตรายยิ่งขึ้นโดยใช้สีที่ใกล้เคียงกับสีแดง / ขาวในสเปกตรัมแม่เหล็กไฟฟ้าและวาดด้วยจุดที่ใหญ่กว่า

  • อันตราย: สีน้ำเงิน <เขียว <เหลือง <แดง <สีเทาอ่อน

ประสิทธิภาพ:

1,000 รอบกับอัลกอริทึมอื่น ๆ แปดอันดับแรกบนลีดเดอร์บอร์ด:

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

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


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

รหัส:

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

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

import math, random, functools, sys

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

def applyVecRotation(vec, rotation):
    pass

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

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

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

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

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

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

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

if True and __name__ == '__main__' and not sys.flags.interactive:
    import time
    DoPlot = False
    #Use the arena object that visualizes progress every turn.
    DangerPlot = True
    #Compute and render a voxel cloud of danger ratings within the arena each turn if visualizing it.
    SparseDangerPlot = False
    #Use a lower resolution for the voxel cloud if visualizing danger ratings.
    TurntablePlot = True
    #Apply a fixed animation to the interactive visualization's rotation if visualizing the arena.
    with CtrlReceiver(logname='danger_log.txt', arenatype=PlottingDangerZones if DoPlot else DangerZones, arenaconf=dict(disabledplots=None if DangerPlot else ('danger'), plotparams=dict(dangerres=2) if SparseDangerPlot else dict(dangeralpha=0.1), plotautoturn=1 if TurntablePlot else 0, plotsavedir=f'PngFrames') if DoPlot else None, planetype=DangerZoner) as run:
        try:
            run.run()
        except Exception as e:
            run.logerr(stringException(e))
```
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.