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 18
theo dõi, NEW TURN
v.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 FIGHT
dò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
.