ОБНОВЛЕНИЕ: isSuicidal () был добавлен к классу самолетов, это позволяет вам проверить, находится ли самолет на необратимом курсе столкновения со стенами !!
ОБНОВЛЕНИЕ: updateCoolDown () отделен от simulateMove ()
ОБНОВЛЕНИЕ: не входящий в Java упаковщик записей, написанный Sparr , доступный для тестирования, см. Комментарии
ОБНОВЛЕНИЕ Zove Games Написал потрясающий 3D-визуализатор для этого KOTH, вот дерьмовый видеоролик на YouTube о том, как PredictAndAVoid сражается с PredictAndAVoid.
Функция simulateMove () класса Plane была слегка изменена, поэтому она больше не обновляет охлаждение, для этого используйте новую функцию updateCoolDown () после съемки. Новый isSuicidal () возвращает true, если самолет должен погибнуть, используйте его, чтобы обрезать движения врага и избегать ударов по стенам. Чтобы получить обновленный код, просто замените классы Controller и Plane классами в репозитории github.
Описание
Цель этой задачи состоит в том, чтобы закодировать два самолета для борьбы с собаками, с которыми другой участник столкнется с двумя самолетами. Каждый ход вы перемещаетесь на одну клетку и имеете возможность стрелять. Вот и все, это так просто.
Ну, почти...
Арена и возможные ходы
Арена 14x14x14 окружена стеной в космосе. Самолеты участника 1 начинаются в местах (0,5,0) и (0,8,0), а плоскости участника 2 в (13,5,13) и (13,8,13). Все самолеты начинаются с полета горизонтально от вертикальных стен, к которым они ближе всего.
Теперь, когда вы летите на самолетах, а не на вертолетах, вы не можете просто изменить направление по желанию или даже прекратить движение, поэтому каждый самолет имеет направление и будет перемещать одну плитку в этом направлении каждый ход.
Возможные направления: Север (N), Юг (S), Восток (E), Запад (W), Вверх (U) и Вниз (D) и любая логическая комбинация этих шести. Где ось NS соответствует оси x, WE - y, а DU - z. NW, SU и NED приходят на ум в качестве возможных примеров направлений; UD является отличным примером неверной комбинации.
Конечно, вы можете изменить направление ваших самолетов, но есть ограничение, вы можете изменить свое направление не более чем на 45 градусов. Чтобы визуализировать это, возьмите кубик вашего рубика (я знаю, у вас он есть) и представьте, что все 26 внешних маленьких кубиков являются возможными направлениями (однобуквенные направления - грани, двухбуквенные направления - ребра, а трехбуквенные направления - углы). Если вы движетесь в направлении, представленном маленьким кубиком, вы можете изменить направление каждого куба, который касается вашего (счет по диагонали, но касается только визуально, то есть не касается куба).
После того, как все самолеты указали, в каком направлении они хотели бы измениться, они делают это и перемещают одну плитку одновременно.
Вы также можете выбрать движение в правильном направлении, но продолжайте лететь в том направлении, в котором вы двигались, вместо того, чтобы изменить свое направление в направлении, в котором вы двигались. Это аналогично разнице между проезжающим за углом автомобилем и полосой смены машины.
Стрельба и умирание
Вы можете стрелять не более одного раза за раунд, и это должно быть решено в то же время, когда вы решаете, в каком направлении лететь, и хотите ли вы, чтобы ваш самолет (и, соответственно, ваш пистолет) указывал в одном направлении или нет. Пуля стреляет сразу после движения вашего самолета. После стрельбы остывает один ход, на третьем ходу ты снова идешь. Вы можете стрелять только в том направлении, в котором летите. Пуля мгновенная и летит по прямой линии, пока не достигнет стены или самолета.
Принимая во внимание то, как вы можете изменить направление, а также «поменять полосы», это означает, что вы можете угрожать колонне размером до 3х3 строк перед вами дополнительно к некоторым диагональным, одиночным линиям.
Если он попадает в самолет, этот самолет умирает и быстро исчезает с доски (потому что он полностью взрывается или что-то в этом роде). Пули могут поразить только один самолет. Пули стреляют одновременно, поэтому два самолета могут стрелять друг в друга. Хотя две пули не могут столкнуться в воздухе (грустно, я знаю).
Однако две плоскости могут столкнуться (если они оказываются в одном и том же кубе и НЕ, если они пересекаются друг с другом, не оказавшись в одной плоскости), и это приводит к гибели (и полному взрыву) обеих плоскостей. Вы также можете влететь в стену, что приведет к гибели рассматриваемого самолета и загнанию в угол, чтобы подумать о его действиях. Столкновения обрабатываются до стрельбы.
Связь с контроллером
Я буду принимать записи на Java, а также на других языках. Если ваша запись в java, вы получите ввод через STDIN и будете выводить через STDOUT.
Если ваша запись в java, запись .your должна расширять следующий класс:
package Planes;
//This is the base class players extend.
//It contains the arena size and 4 plane objects representing the planes in the arena.
public abstract class PlaneControl {
// note that these planes are just for your information, modifying these doesn't affect the actual plane instances,
// which are kept by the controller
protected Plane[] myPlanes = new Plane[2];
protected Plane[] enemyPlanes = new Plane[2];
protected int arenaSize;
protected int roundsLeft;
...
// Notifies you that a new fight is starting
// FightsFought tells you how many fights will be fought.
// the scores tell you how many fights each player has won.
public void newFight(int fightsFought, int myScore, int enemyScore) {}
// notifies you that you'll be fighting anew opponent.
// Fights is the amount of fights that will be fought against this opponent
public void newOpponent(int fights) {}
// This will be called once every round, you must return an array of two moves.
// The move at index 0 will be applied to your plane at index 0,
// The move at index1 will be applied to your plane at index1.
// Any further move will be ignored.
// A missing or invalid move will be treated as flying forward without shooting.
public abstract Move[] act();
}
Экземпляр, созданный для этого класса, будет сохраняться на протяжении всего соревнования, поэтому вы можете хранить любые данные, которые хотите сохранить, в переменных. Прочитайте комментарии в коде для получения дополнительной информации.
Я также предоставил вам следующие вспомогательные классы:
package Planes;
//Objects of this class contain all relevant information about a plane
//as well as some helper functions.
public class Plane {
private Point3D position;
private Direction direction;
private int arenaSize;
private boolean alive = true;
private int coolDown = 0;
public Plane(int arenaSize, Direction direction, int x, int y, int z) {}
public Plane(int arenaSize, Direction direction, Point3D position) {}
// Returns the x coordinate of the plane
public int getX() {}
// Returns the y coordinate of the plane
public int getY() {}
// Returns the z coordinate of the plane
public int getZ() {}
// Returns the position as a Point3D.
public Point3D getPosition() {}
// Returns the distance between the plane and the specified wall,
// 0 means right next to it, 19 means at the opposite side.
// Returns -1 for invalid input.
public int getDistanceFromWall(char wall) {}
// Returns the direction of the plane.
public Direction getDirection() {}
// Returns all possible turning directions for the plane.
public Direction[] getPossibleDirections() {}
// Returns the cool down before the plane will be able to shoot,
// 0 means it is ready to shoot this turn.
public int getCoolDown() {}
public void setCoolDown(int coolDown) {}
// Returns true if the plane is ready to shoot
public boolean canShoot() {}
// Returns all positions this plane can shoot at (without first making a move).
public Point3D[] getShootRange() {}
// Returns all positions this plane can move to within one turn.
public Point3D[] getRange() {}
// Returns a plane that represents this plane after making a certain move,
// not taking into account other planes.
// Doesn't update cool down, see updateCoolDown() for that.
public Plane simulateMove(Move move) {}
// modifies this plane's cool down
public void updateCoolDown(boolean shot) {
coolDown = (shot && canShoot())?Controller.COOLDOWN:Math.max(0, coolDown - 1);
}
// Returns true if the plane is alive.
public boolean isAlive() {}
// Sets alive to the specified value.
public void setAlive(boolean alive) {}
// returns a copy of itself.
public Plane copy() {}
// Returns a string representing its status.
public String getAsString() {}
// Returns a string suitable for passing to a wrapped plane process
public String getDataString() {}
// Returns true if a plane is on an irreversable colision course with the wall.
// Use this along with simulateMove() to avoid hitting walls or prune possible emeny moves.
public boolean isSuicidal() {}
}
// A helper class for working with directions.
public class Direction {
// The three main directions, -1 means the first letter is in the direction, 1 means the second is, 0 means neither is.
private int NS, WE, DU;
// Creates a direction from 3 integers.
public Direction(int NSDir, int WEDir, int DUDir) {}
// Creates a direction from a directionstring.
public Direction(String direction) {}
// Returns this direction as a String.
public String getAsString() {}
// Returns The direction projected onto the NS-axis.
// -1 means heading north.
public int getNSDir() {}
// Returns The direction projected onto the WE-axis.
// -1 means heading west.
public int getWEDir() {}
// Returns The direction projected onto the DU-axis.
// -1 means heading down.
public int getDUDir() {}
// Returns a Point3D representing the direction.
public Point3D getAsPoint3D() {}
// Returns an array of chars representing the main directions.
public char[] getMainDirections() {}
// Returns all possible turning directions.
public Direction[] getPossibleDirections() {}
// Returns true if a direction is a valid direction to change to
public boolean isValidDirection(Direction direction) {}
}
public class Point3D {
public int x, y, z;
public Point3D(int x, int y, int z) {}
// Returns the sum of this Point3D and the one specified in the argument.
public Point3D add(Point3D point3D) {}
// Returns the product of this Point3D and a factor.
public Point3D multiply(int factor) {}
// Returns true if both Point3D are the same.
public boolean equals(Point3D point3D) {}
// Returns true if Point3D is within a 0-based arena of a specified size.
public boolean isInArena(int size) {}
}
public class Move {
public Direction direction;
public boolean changeDirection;
public boolean shoot;
public Move(Direction direction, boolean changeDirection, boolean shoot) {}
}
Вы можете создавать экземпляры этих классов и использовать любые их функции так, как вам нравится. Вы можете найти полный код этих вспомогательных классов здесь .
Вот пример того, как может выглядеть ваша запись (надеюсь, вы добьетесь большего успеха, чем я, хотя, большинство матчей с этими самолетами заканчиваются тем, что они летят в стену, несмотря на все их усилия, чтобы избежать стены.):
package Planes;
public class DumbPlanes extends PlaneControl {
public DumbPlanes(int arenaSize, int rounds) {
super(arenaSize, rounds);
}
@Override
public Move[] act() {
Move[] moves = new Move[2];
for (int i=0; i<2; i++) {
if (!myPlanes[i].isAlive()) {
moves[i] = new Move(new Direction("N"), false, false); // If we're dead we just return something, it doesn't matter anyway.
continue;
}
Direction[] possibleDirections = myPlanes[i].getPossibleDirections(); // Let's see where we can go.
for (int j=0; j<possibleDirections.length*3; j++) {
int random = (int) Math.floor((Math.random()*possibleDirections.length)); // We don't want to be predictable, so we pick a random direction out of the possible ones.
if (myPlanes[i].getPosition().add(possibleDirections[random].getAsPoint3D()).isInArena(arenaSize)) { // We'll try not to fly directly into a wall.
moves[i] = new Move(possibleDirections[random], Math.random()>0.5, myPlanes[i].canShoot() && Math.random()>0.2);
continue; // I'm happy with this move for this plane.
}
// Uh oh.
random = (int) Math.floor((Math.random()*possibleDirections.length));
moves[i] = new Move(possibleDirections[random], Math.random()>0.5, myPlanes[i].canShoot() && Math.random()>0.2);
}
}
return moves;
}
@Override
public void newFight(int fightsFought, int myScore, int enemyScore) {
// Using information is for schmucks.
}
@Override
public void newOpponent(int fights) {
// What did I just say about information?
}
}
DumbPlanes присоединятся к турниру вместе с другими записями, поэтому, если вы закончите в последний раз, вы сами виноваты в том, что не стали лучше, чем DumbPlanes.
ограничения
Применяются ограничения, указанные в вики KOTH :
- Любая попытка повозиться с контроллером, во время выполнения или другими представлениями будет дисквалифицирована. Все представления должны работать только с входами и хранилищами, которые им предоставляются.
- Боты не должны быть написаны, чтобы побеждать или поддерживать определенных других ботов. (Это может быть желательно в редких случаях, но если это не основная концепция задачи, лучше исключить ее.)
- Я оставляю за собой право дисквалифицировать заявки, которые используют слишком много времени или памяти для проведения испытаний с разумным количеством ресурсов.
- Бот не должен реализовывать ту же стратегию, что и существующая, намеренно или случайно.
Тестирование вашего представления
Загрузите код контроллера отсюда . Добавьте ваше представление как Something.java. Измените Controller.java, чтобы включить записи для вашей плоскости в записи [] и имена []. Скомпилируйте все как проект Eclipse или с помощью javac -d . *.java
, затем запустите контроллер с помощью java Planes/Controller
. Журнал конкурса будет test.txt
с табло в конце. Вы также можете вызвать matchUp()
напрямую с двумя записями в качестве аргументов, чтобы просто проверить две плоскости друг против друга.
Выиграть бой
Победителем в сражении становится тот, у кого был последний полет самолета, если после 100 поворотов осталось еще более 1 команды, то побеждает команда с наибольшим количеством оставшихся самолетов. Если это равно, это ничья.
Подсчет очков и соревнования
Следующий официальный турнир будет проведен, когда закончится текущая награда.
Каждая заявка будет бороться с каждой другой записью (как минимум) 100 раз, победитель в каждом матче - тот, у кого больше всего побед из 100, и он получит 2 очка. В случае розыгрыша обе записи получают 1 балл.
Победителем конкурса становится тот, у кого больше всего очков. В случае ничьей победителем становится тот, кто выиграл в матче между ничьими.
В зависимости от количества записей, количество боев между записями может быть значительно увеличено, я мог бы также выбрать 2-4 лучших записи после первого турнира и настроить элитный турнир между этими записями с большим количеством боев (и, возможно, больше раундов на борьба)
(предварительный) Табло
У нас есть новая запись, которая уверенно занимает второе место в еще одном увлекательном турнире , кажется, что 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 ¦
----------------------------
Вот пример вывода из не-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 FIGHT
строку с обновленным счетом и счетами, возможно, с предшествующим NEW OPPONENT
.