反重力移動は、パターン分析型のロボットを欺くのに役立つ高度に柔軟な技法であり、バトルフィールド上で避けるべき特定の地点 (重力ポイント という) を定義することができます。重力ポイントにはそれぞれ、強さ が割り当てられます。この強さのx方向とy方向の成分を分析することにより、すべての敵ロボットを効果的に回避できます。(このヒントで使われる用語を理解する助けとして、「反重力の用語」という補足記事をご覧ください。)
この記事の前半では、基本的な反重力の技法を紹介し、後半では、この基本的なコードの根本的な限界を回避するためのアイデアについて説明します。
三角法の実用的な知識を持っていれば、反重力の背後にある数学はごく簡単に理解できます。
図1で、"F" という矢印は、CrazyからAntiGravityBotに対する力の方向を示しています。力は、x方向とy方向の成分として考えるとことができます。これは、図にある他の2つの矢印で示されています。力を分析すると、すべての重力ポイントからの力をx方向とy方向で単純に加算することができ、x方向とy方向における合計の力を算出できます。
図1. 力を分析する
遠く離れたロボットから自分のロボットへの影響を小さくするには、重力ポイントから自分のロボットに対する力の計算に、
force = strength/Math.pow(distance,2) という関数を使用しなければなりません。この関数で、strength は重力ポイントのパワー、
distance は重力ポイントと自分のロボットの距離です。2というべき乗値は、固定ではありません。重力ポイントに非常に近づいたときにのみそのポイントを避けるようにするには、3という値を使用することもできます。
この後のリストは、基本的な反重力システムのコードを示しています。リスト1は、メインの反重力関数です。この関数は、ベクトル内のすべての重力ポイントをたどり、力を解分析し、ロボットを正しい方向へ移動させます。敵ロボットを反発ポイントにすることをお勧めします。そのためには、バトルフィールドについてかなり最新の全体像を保持しなければならず、レーダーをかなり頻繁に回転させる必要があります。
リスト 1. The anti-gravity workhorse: antiGravMove()
void antiGravMove() {
double xforce = 0;
double yforce = 0;
double force;
double ang;
GravPoint p;
for(int i = 0;i<gravpoints.size();i++) {
p = (GravPoint)gravpoints.elementAt(i);
//Calculate the total force from this point on us
force = p.power/Math.pow(getRange(getX(),getY(),p.x,p.y),2);
//Find the bearing from the point to us
ang =
normaliseBearing(Math.PI/2 - Math.atan2(getY() - p.y, getX() - p.x));
//Add the components of this force to the total force in their
//respective directions
xforce += Math.sin(ang) * force;
yforce += Math.cos(ang) * force;
}
/**The following four lines add wall avoidance. They will only
affect us if the bot is close to the walls due to the
force from the walls decreasing at a power 3.**/
xforce += 5000/Math.pow(getRange(getX(),
getY(), getBattleFieldWidth(), getY()), 3);
xforce -= 5000/Math.pow(getRange(getX(),
getY(), 0, getY()), 3);
yforce += 5000/Math.pow(getRange(getX(),
getY(), getX(), getBattleFieldHeight()), 3);
yforce -= 5000/Math.pow(getRange(getX(),
getY(), getX(), 0), 3);
//Move in the direction of our resolved force.
goTo(getX()-xforce,getY()-yforce);
}
|
リスト2に示したヘルパー・メソッドを利用すると、自分のロボットと敵ロボットの距離を保つために最も効率の良い位置へ移動できます。
リスト 2. Helper methods
/**Move in the direction of an x and y coordinate**/
void goTo(double x, double y) {
double dist = 20;
double angle = Math.toDegrees(absbearing(getX(),getY(),x,y));
double r = turnTo(angle);
setAhead(dist * r);
}
/**Turns the shortest angle possible to come to a heading, then returns
the direction the bot needs to move in.**/
int turnTo(double angle) {
double ang;
int dir;
ang = normalisebearing(getHeading() - angle);
if (ang > 90) {
ang -= 180;
dir = -1;
}
else if (ang < -90) {
ang += 180;
dir = -1;
}
else {
dir = 1;
}
setTurnLeft(ang);
return dir;
}
/**/Returns the distance between two points**/
double getRange(double x1,double y1, double x2,double y2) {
double x = x2-x1;
double y = y2-y1;
double range = Math.sqrt(x*x + y*y);
return range;
}
|
最後に、リスト3の GravPoint クラスは、重力ポイントについて必要なすべてのデータを保持します。反発させるためには、
power を負の値にしなければならないことに注意してください。
リスト 3.GravPoint class
class GravPoint {
public double x,y,power;
public GravPoint(double pX,double pY,double pPower) {
x = pX;
y = pY;
power = pPower;
}
}
|
このヒントのソース全体は、 参考文献からダウンロードできます。
リスト1~3のコードは、理にかなった振る舞いをロボットにさせますが、戦闘におけるパフォーマンスはあまり良くありません。ロボットは全般的に他のロボットから離れた位置を保ちますが、壁ぎわで立ち往生してしまう傾向があります。その理由は、たとえばロボットが下側の壁に達すると、自分より下にロボットは1台もないからです。したがって、ロボットを壁から押し戻す力は、壁そのものによる反発力しかありません。壁の反発力には限られた範囲しかないため、貧弱な振る舞いになってしまうわけです。
この問題と闘うために、私は、競技場中の一連のポイントにおける力を合計するシステムを使用しています。その後、力の総合計の平均値より力が大きいポイント (つまり、近くにロボットがいるポイント) に反発力の値を割り当て、平均値より力が小さいポイントに誘引力の値を割り当てます。その上で、あらためて、これらの新しいポイントから自分のロボットに対する力を分析します。誘引ポイントを割り当てる際には、きわめて慎重にしなければなりません。自分のロボットが誘引ポイントに近づくと、ロボットはその周囲をうろつくだけで、決して離れなくなってしまうからです。その理由から、これらの中間ポイントの位置をランダムに割り当てることと、その位置を定期的に変更することをお勧めします。
この拡張のためのコードを作成することは、読者にお任せします。基本的な原理はまったく同じままで、上記のコードに少しの変更を加えるだけですので、安心してください。もう少しのヒントとして、中間ポイントから自分のロボットへの力を計算する際に、
force = strength/Math.pow(distance,1.5)を使うことをお勧めします。
反重力は、非常に柔軟性のある技法です。そのため、実現できる振る舞いすべてを説明することは実際的でありません。しかし、興味深い拡張点のいくつかを、ここで紹介しておきます。
標的の選択: 攻撃しやすい標的、または弱っている標的の反発値を低く設定すれば、それらの標的の近くに移動して、弱い相手を仕留めることができます。
ランダム化: かなり定期的に、xおよびy方向の力からランダムな値を加算または減算することにより、いっそうランダムな動きを実現できます。時には停止することさえ可能でしょう。そうすれば、敵の標的合わせシステムを欺くことができます。この振る舞いを実装することを、強くお勧めします。
混戦で弾丸をかわす: 敵から自分に向けて弾丸が発射された時点を検知できるのであれば、発射された弾丸を反重力ポイントとしてモデル化できます。たとえば、敵の弾丸が直線方式の標的合わせで発射されたと想定される場合は、その重力ポイントの位置を各動作 (turn) ごとに更新することにより、弾丸をかわすことができます。しかし、この拡張を完全に実現したロボットはまだありません。
リーダーに従う: この拡張には、自分のロボットに誘引ポイントを設定して従わせることが関係しています。こうして、そのポイントを移動することにより、自分の望むパターンを (Robocodeの物理法則の範囲内で) 生み出すことができます。しかも、標準的な反重力の壁反発には従います。
| 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|---|---|---|
| Source code | j-antigrav.zip | 12KB | HTTP |
- 反重力の技法を実際の戦闘でテストしてみるには、この完全に機能するロボットをご利用ください。
- alphaWorksから最新版のRobocode をダウンロードできます。
- Christian Schnellによる RoboLeague は、Robocodeのためのリーグおよびシーズン・マネージャーです。これを利用すると、どんなグループ分けでも実際に試合を実行し、その結果を管理し、HTMLの状況報告書を生成できます。
- 「闘え、Robocode (ロボコード)」(developerWorks、2002年1月) では、Robocodeの武装を解きほぐし、カスタマイズされた軽量で平均的な戦闘マシンの構築を始める手ほどきをします。
- 「闘え、Robocode (ロボコード): 第2ラウンド」(developerWorks、2002年5月)では、Sing Li氏が、高度なロボットの構築とチーム・プレイに目を向けます。
- Javaの初心者にとっては、
Introduction to Java programming" (developerWorks、2004年11月) が、Java言語プログラミングの基礎を順を追って学習できるチュートリアルになっています。
- その他のJava関連の参考資料は、
developerWorks の Java technologyゾーンで参照できます。
Alisdair Owensは、英国サウサンプトン大学のコンピューター・サイエンスの学生です。Java言語によるプログラミングに2年間携わっており、Robocodeに特に関心を寄せています。彼の連絡先は、awo101@ecs.soton.ac.uk.com です。