ОНОВЛЕННЯ: isSuicidal () додано до класу площин, це дозволяє перевірити, чи перебуває літак на незворотному зіткненні зі стінами !!
ОНОВЛЕННЯ: updateCoolDown () відокремлено від simulateMove ()
ОНОВЛЕННЯ: обгортка для введення не на Java, написана Sparr , доступна для тестування, дивіться коментарі
ОНОВЛЕННЯ Ігри Zove Написав дивовижний 3D-візуалізатор для цієї КОТИ, ось відео з лайном на YouTube, що передбачає боротьбу з PredictAndAVoid PredictAndAVoid.
Функція simulateMove () класу Plane була трохи змінена, тому вона більше не оновлює охолодження, використовуйте для цього нову функцію updateCoolDown () після зйомки. Новий isSuicidal () повертає справжнє значення, якщо літак повинен закінчитися мертвим, використовуйте його для підрізання рухів противника та уникнення ударів по стінах. Щоб отримати оновлений код, просто замініть класи Controller і Plane на ті, які містяться в github repo.
Опис
Мета цього виклику - кодувати два літаки боротьби з собаками, які зіткнуться з двома літаками іншим учасником. З кожним ходом ви переміщаєте один простір і маєте можливість стріляти. Це все, це так просто.
Ну, майже ...
Арена та можливі ходи
Арена являє собою стіни розміром 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, ваш запис повинен поширити наступний клас:
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
Це вказує на те, що ваш перший літак буде подорожувати Північно-Заходом, не відвертаючись від поточного заголовку, і, якщо зможе, стріляти. Ваш другий літак буде подорожувати SouthUp, повернувшись до SouthUp, не стріляючи.
Тепер за вами ROUNDS LEFT 18
слідують і NEW TURN
т. Д. Це триває, поки хтось не виграє або не виграє раунд, і тоді ви отримаєте ще один NEW FIGHT
рядок з оновленими підрахунками бою та результатами, можливо, передуючи а NEW OPPONENT
.