更新: isSuicidal()が飛行機クラスに追加されました。これにより、飛行機が壁との不可逆的な衝突コースにあるかどうかを確認できます!!
更新: simulateMove()から分離されたupdateCoolDown()
更新:Sparrによって書かれた非Javaエントリラッパー、テストに利用可能、コメントを参照
UPDATE Zoveゲームが書いた素晴らしい3Dビジュアライザこのケートのために、ここでくだらないのユーチューブのビデオだ PredictAndAVoidを戦っPredictAndAVoidのは。
PlaneクラスのsimulateMove()関数はわずかに修正されたため、クールダウンが更新されなくなり、撮影後に新しいupdateCoolDown()関数が使用されます。新しいisSuicidal()は、飛行機が死んでしまう場合にtrueを返し、それを使用して敵の動きを取り除き、壁にぶつからないようにします。更新されたコードを取得するには、ControllerクラスとPlaneクラスをgithubリポジトリ内のクラスに置き換えるだけです。
説明
この課題の目標は、別の競技者が2機の飛行機と対決する2機のドッグファイティング機をコーディングすることです。ターンごとに1つのスペースを移動し、撮影する機会があります。それだけです、それはそれと同じくらい簡単です。
よくほとんど...
アリーナと可能な動き
アリーナは、スペースで囲まれた14x14x14です。競技者1の面は(0,5,0)と(0,8,0)の位置から始まり、競技者2の面は(13,5,13)と(13,8,13)で始まります。すべての飛行機は、最も近い垂直な壁から水平方向に離れることから始まります。
ヘリコプターではなく飛行機を飛ばしているので、自由に方向を変えたり、移動を止めたりすることはできません。したがって、各飛行機には方向があり、毎回その方向に1タイル移動します。
可能な方向は、北(N)、南(S)、東(E)、西(W)、上(U)および下(D)およびこれら6つの任意の論理的組み合わせです。NS軸はx軸に対応し、WEはyに、DUはzに対応します。NW、SU、およびNEDは、方向の可能な例として思い浮かびます。UDは、無効な組み合わせの良い例です。
もちろん、飛行機の方向を変更することはできますが、制限があります。方向は最大で45度しか変更できません。これを視覚化するには、ルービックキューブ(1つあることを知っています)をつかみ、26個の外側の小さなキューブすべてが可能な方向(1文字の方向が面、2文字の方向がエッジ、3文字の方向がコーナー)であると想像してください。小さな立方体で表される方向に向かっている場合は、自分に触れている各立方体の方向を変えることができます(斜めに触れているが、目に見えるように触れているだけで、立方体に触れていない)。
すべてのプレーンがどの方向に変更したいかを示した後、それらを変更し、1つのタイルを同時に移動します。
有効な方向に移動することもできますが、方向を移動先の方向に変更する代わりに、目的の方向に飛行し続けることができます。これは、角を曲がる車と車線を変更する車の違いに似ています。
射撃と死
ラウンドごとに最大1回撃つことができます。これは、飛行する方向を決定すると同時に、飛行機(ひいては銃)を同じ方向に向けるかどうかを決定すると同時に決定する必要があります。弾丸は、飛行機が移動した直後に撃たれます。撮影後、1ターンのクールダウンがあり、3ターン目には、もう一度行くことができます。飛んでいる方向にのみ撃つことができます。弾丸は瞬時に発生し、壁や飛行機に当たるまで一直線に飛びます。
方向を変える方法と「車線を変える」方法を考慮して、これは、斜めの単一線に加えて、あなたの前に最大3x3行の列を脅かすことができることを意味します。
飛行機にぶつかると、この飛行機は死に、ボードからすぐに消えます(完全に爆発するなどの理由で)。弾丸は、せいぜい1機しか攻撃できません。弾丸は同時に撃たれるので、2つの飛行機が互いに撃つことができます。ただし、2つの弾丸は空中で衝突することはありません(悲しいことですが)。
ただし、2つの平面は衝突する可能性があり(同じ立方体になり、同じ平面にならずに互いに交差しない場合)、両方の平面が死ぬ(および完全に爆発する)ことになります。また、問題の飛行機が死に、その行動を考えるために隅に置かれることになります壁に飛ぶことができます。衝突は、撮影前に処理されます。
コントローラーとの通信
私は、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 wikiに記載されている制限が適用されます。
- コントローラー、ランタイム、またはその他のサブミットをいじくり回そうとする試みはすべて失格となります。すべての提出は、与えられた入力とストレージでのみ機能します。
- ボットは、特定の他のボットを倒したりサポートしたりするために書かれてはなりません。(これはまれなケースで望ましい場合がありますが、これがチャレンジの中心概念でない場合は、除外する方が良いでしょう。)
- 私は、合理的な量のリソースでトライアルを実施するのに時間がかかりすぎる、またはメモリを使用する提出物を失格にする権利を留保します。
- ボットは、意図的または偶然に既存の戦略とまったく同じ戦略を実装してはなりません。
提出物のテスト
ここからコントローラーコードをダウンロードします。提出物をSomething.javaとして追加します。Controller.javaを変更して、entries []およびnames []に飛行機のエントリを含めます。Eclipseプロジェクトとして、またはですべてをコンパイルしjavac -d . *.java
、でControllerを実行しますjava Planes/Controller
。コンテストのログはにありtest.txt
、最後にスコアボードがあります。またmatchUp()
、2つのエントリを引数として直接呼び出して、2つのプレーンを互いにテストすることもできます。
戦いに勝つ
戦いの勝者は、最後の飛行機が飛んでいる人です。100ターン後、まだ1つ以上のチームが残っている場合、残っている飛行機が最も多いチームが勝ちます。これが等しい場合、それは引き分けです。
得点と競争
次の公式トーナメントは、現在の賞金がなくなると実行されます。
各エントリーは、他のすべてのエントリー(少なくとも)を100回戦います。各マッチの勝者は、100の中で最も勝ったものであり、2ポイントが与えられます。引き分けの場合、両方のエントリーに1ポイントが与えられます。
競争の勝者は、ほとんどのポイントを持つものです。引き分けの場合、勝者は描かれたエントリー間のマッチで勝ったものです。
エントリーの量に応じて、エントリー間の戦いの量を大幅に増やすことができます。最初のトーナメント後に2〜4個のベストエントリーを選択し、より多くの戦い(および場合によってはより多くのラウンド戦い)
(予備)スコアボード
さらにもう1つのエキサイティングなトーナメントで2位を獲得した新しいエントリーがあります。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回戦ったことを示します。
ROUNDS LEFT 19
現在の戦闘に19ラウンド残っていることを示します
NEW TURN
このラウンドの戦闘で4機すべてのデータを受信しようとしていることを示します
alive 13 8 13 N 0
alive 13 5 13 N 0
dead 0 0 0 N 0
alive 0 8 0 S 0
これらの4本の線は、両方の飛行機がそれぞれ座標[13,8,13]と[13,5,13]で生きており、両方とも北を向いており、両方とも冷却がゼロであることを示しています。最初の敵機は死んでおり、2番目の機体は[0,8,0]で生き残り、クールダウンなしで南向きです。
この時点で、プログラムは次のような2行を出力するはずです。
NW 0 1
SU 1 0
これは、最初の飛行機が現在の方位から向きを変えずに北西に移動し、可能であれば撮影することを示しています。2番目の飛行機がSouthUpを移動し、撮影ではなく、SouthUpに向かいます。
これがROUNDS LEFT 18
続きますNEW TURN
。これは、誰かが勝つかラウンドがタイムアウトするまで続きます。その時点NEW FIGHT
で、更新されたファイトカウントとスコアを備えた別の行を取得しますNEW OPPONENT
。