前文了解 Robocode 絕對方向、相對方向及整個方向系統。相信大家深有體會。但問題來了:單知方向似乎不能完全達到了解敵人目的。怎樣探測敵人距離?怎樣精確鎖定目標?對移動目標如何處理?我們將利用 Java.lang 基礎類別庫 Math 類別及基本三角函數方法揭開迷霧。對快被遺忘三角幾何知識,文末有詳細講解。
先看 Robocode API 文字翻譯:
圖 1 顯示 Robocode 座標系統,詳細說明請看前文 "Robocode 基本原理之方向剖析"。

知道整個座標系統,一切問題好辦。進行有趣實驗。用「動靜機器人」方法測試。這是測試機器人方向、座標參數好辦法。設計兩機器人,任意命名 Geny 和 GenyTrack。Geny 靜止,主要任務列印自己當前座標,驗證 GenyTrack 追蹤位置是否正確。GenyTrack 顧名思義,研究追蹤目標機器人。負責鎖定 Geny 座標、距離並列印探測到 Geny X、Y 座標及距離,使用 Java.lang 類別庫 Math.round 方法四捨五入取得 double 類型數據,方便對比。最後用表格對比驗證方法正確性。
還有很多有趣測試方法等待驗證。如測速度、加速度可用「龜兔賽跑」;測砲管、雷達坦克車旋轉相互影響度可用「離心重力」。相信從測試方法名字聰明的你們知道用法。
開始前,建議下載原始碼 先看 GenyTrack 表演。當然也可參考文內輔助說明 Robocode 座標系統程式碼。
package test;
import robocode.*;
public class Geny extends AdvancedRobot {
public void run() {
while (true) {
out.println("x:" + Math.round(getX()));
out.println("y:" + Math.round(getY()));
}
}
}
package test;
import robocode.*;
public class GenyTrack extends AdvancedRobot {
public void run() {
while (true) {
turnRadarRight(400);
}
}
public void onScannedRobot(ScannedRobotEvent e) {
double bearing = (getHeading() + e.getBearing()) % 360;
double distance = e.getDistance();
bearing = Math.toRadians(bearing);
double genyX = getX() + Math.sin(bearing) * distance;
double genyY = getY() + Math.cos(bearing) * distance;
out.println("genyX:" + Math.round(genyX));
out.println("genyY:" + Math.round(genyY));
}
}
注意兩機器人都用 AdvancedRobot 類別,這是高級機器人說明。相關說明可查 Robocode API 或看 Sing Li "Rock 'em, sock 'em Robocode: Round 2"。
獲取目標座標先要知道雙方距離。距離探測簡單,運用 GenyTrack 機器人 ScannedRobotEvent 事件 getDistance() 方法獲取 Geny 機器人與您距離差。注意機器人存在寬高,可用 Robocode API getWidth() 和 getHeight() 取得。兩機器人距離以雙方中心點為終點。如圖 2,L 才是它們距離,A 距離錯誤。

知道對方距離、整個座標系統,鎖定目標 Geny。看圖 3:


Geny GenyTrack
X:303 genyX:303
Y:128 genyY:128
列表 1 是「動靜機器人」測試法得出數據。成功探測可憐 Geny 座標。驚喜後不解:怎麼實現?為什麼程式碼用非 Robocode 類別庫 Math,似乎用正餘弦求解、還有弧度?沒關係,文末 三角函數基礎 將學習這些,勾起中學記憶。
分析 GenyTrack 做了什麼:
在 GenyTrack 的 ScannedRobotEvent 事件中,首先獲取 Geny 絕對角度 bearing (相對螢幕角度)。從 ScannedRobotEvent 掃描事件大量資訊分析提煉出 Geny 和 GenyTrack 距離 distance。有 Geny 角度、距離,根據三角學基礎 (詳見 三角函數基礎) 求出 Geny 精確座標。
Java 類別庫正弦 sin 餘弦 cos 以弧度制為角度參數。用 Math.toRadians 將 Geny 絕對角度轉換弧度。見列表 2:
double bearing = (getHeading() + e.getBearing()) % 360;
double distance = e.getDistance();
bearing = Math.toRadians(bearing);
double genyX = getX() + Math.sin(bearing) * distance;
double genyY = getY() + Math.cos(bearing) * distance;
out.println("genyX:" + Math.round(genyX));
out.println("genyY:" + Math.round(genyY));
注意三角函數基礎:對邊長 = sin a * 斜邊長,鄰邊長 = cos a * 斜邊長。但 Robocode 三角座標系統 sin 和 cos 與數學三角座標系統有差別,即 sin a 和 cos a 要對調,對邊長 = cos a * 斜邊長。圖 4 畫出 Geny 和 GenyTrack 角度距離關係及 Robocode 採用三角座標系統。

黑線條為 GenyTrack X、Y 座標,藍線條以 Geny 距離 distance 和絕對角度 bearing 求得 X、Y 座標,兩者相加得 Geny X、Y 座標。
Math 類別庫使用不詳細說明。讀者可從 IBM Java 專區連結找到相關知識,或參考 Java 類別庫書籍。設計高級 Robocode 機器人時發現,Math 類別庫是不可缺少知識。此處簡單介紹正弦、餘弦函數使用。
public static double sin(double a)
// Returns the trigonometric sine of an angle.
// Parameters: a - an angle, in radians.
// Returns: the sine of the argument
Sin 回傳三角正弦函數,參數 a 為 double 類型以弧度表示角度值,回傳類型 double。
public static double cos(double a)
// Returns the trigonometric cosine of an angle.
// Parameters: a - an angle, in radians.
// Returns: the cosine of the argument
Cos 回傳三角餘弦函數,參數 a 為 double 類型弧度表示角值,回傳類型 double。
有人問為什麼不使用 ScannedRobotEvent 中 getRadarHeadingRadians() 直接得弧度。看 Robocode 中華聯盟 iiley 說明:
public void onScannedRobot(ScannedRobotEvent event) {
enemyX = Math.sin(Util.standardMathDirRadians(getRadarHeadingRadians())) * event.getDistance();
enemyY = Math.cos(Util.standardMathDirRadians(getRadarHeadingRadians())) * event.getDistance();
}
看似正確,但實踐發現不準確。原因在於 getRadarHeadingRadians() 函數,調用時雷達已非剛掃描敵人角度,已轉十幾度甚至更多。雷達預設轉動速度 45 度/robocode 單位時間,實際上用 getRadarHeadingRadians() 得值總是 45 度整數倍 (除用 turnRadarLeft(11) 類似語句)。
Robocode 遵循數學應用基本法則用兩種方法表示方向角度:角度制和弧度制。本文程式碼及前文一直用角度制。另種方法用 ScannedRobotEvent.getBearingRadians() + robot.getHeadingRadians() 獲敵人以弧度表示方向,本文未說明,有興趣可自己試試用 Java.util 類別庫實現,或參考文件 "精確計算敵人座標"。大家可比較兩方法各自特點,很有趣過程。
即使最簡單機器人不會坐著不動等您消滅。會躲避進攻和掃描,當向原座標開火,說不定跑老遠了,非我們所希望。目的是消滅它:不管移動或靜止。結合方向系統與座標系統,鎖定移動目標。建立我們自己的高級掃描機器人。建議下載原始碼 看演示效果再回文章。顯示如圖 5:


對比數據,不管目標 GenyMove 在哪 GenyRadar 都得精確座標。成就感!敵人完全掌握中。即使移動中也無法擺脫雷達掃描控制。這裡簡單舉例,GenyMove 每時間週期移動位置並列印移動後座標,GenyRadar 掃描系統不停掃描目標、一直追蹤,同時列印掃描到 GenyMove 方位。關鍵部分在 ScannedRobotEvent 事件如列表 3:
public void onScannedRobot(ScannedRobotEvent e) {
double heading = e.getBearing() + getHeading();
double distance = e.getDistance(); // 求得距離
double ager_bearing = Math.toRadians(heading % 360); // 角度轉弧度
double genyX = getX() + Math.sin(ager_bearing) * distance;
double genyY = getY() + Math.cos(ager_bearing) * distance;
out.println("genyX:" + Math.round(genyX));
out.println("genyY:" + Math.round(genyY));
if(heading >= 360) heading = heading - 360;
if(heading < 0) heading = heading + 360;
double bearing = getRadarHeading() - heading;
double radar_degree;
boolean radar_direction;
if(0 <= bearing && bearing <= 180) { radar_direction = LEFT; }
else if(bearing <= -180) { radar_direction = LEFT; bearing = (360 + bearing); }
else if(bearing < 0) { radar_direction = RIGHT; bearing = (-bearing); }
else { radar_direction = RIGHT; bearing = (360 - bearing); }
radar_degree = bearing * 1.3; // 加大每時間週期掃描範圍
if(radar_direction == RIGHT) {
setTurnRadarRight(radar_degree);
execute();
} else {
setTurnRadarLeft(radar_degree);
execute();
}
}
程式碼首先求 GenyMove 絕對角度,用掃描時雷達絕對角度減目標角度求角度差 (雷達要旋轉角度)。利用小技巧 radar_degree = bearing * 1.3 使雷達在目標範圍左右擺動擴大掃描區域。不管目標往哪移動都在雷達掃描區內。
此處未詳細講解,憑學到方向座標知識很快明白原理並設計高級掃描機器人。聰明您可能想:哈,砲管用相同辦法鎖定目標,敵人不就沒辦法跑了。答案錯誤。雷達掃描是條長線能直接定位目標,到目標時間差近乎零,掃描範圍比砲管大且精確。砲管每時間週期只有 20 度,定位目標靠子彈,只有子彈擊中目標才能說砲管計算座標精確。但子彈到達目標需時間差,子彈有速度值 (20-3*power),想砲管鎖定目標讓子彈擊中,需經過精確計算、預測目標可能行動:直線前進、圓周運動、隨機運動等。這些都是充分考慮因素。很有挑戰性!一切在 Robocode 世界等待您創造!
簡單介紹與 Robocode 相關三角函數知識,詳細可從家中高中代數幾何書獲得。
平面內,角可視為一條射線繞端點旋轉成圖形。射線由原位置 OA,繞端點 O 逆時針旋轉到 OB 形成角 a。旋轉開始射線 OA 叫角 a 始邊,終止射線 OB 叫終邊,端點 O 叫頂點。習慣上,逆時針旋轉成角叫正角;順時針叫負角。所有與 a 終邊相同角 (含 a) 可用式子表示:a + K*360°。對應 Robocode 方向系統,以機器人 heading 方向做射線,延長到螢幕交點處角度即機器人 heading 角度。

△ABC 中,∠a 為直角。銳角 A 對邊與斜邊比叫 ∠A 正弦,記作 sin A;銳角 a 鄰邊與斜邊比叫 ∠a 餘弦,記作 cos a。即:
sin A = 對邊 BC / 斜邊 AB
cos A = 鄰邊 AC / 斜邊 AB

半徑 1 圓叫單位圓。設單位圓圓心與座標原點重合,單位圓與 x 軸交點分別 A(1,0)、A'(-1,0),y 軸交點 B(0,1)、B'(0,-1)。設角 a 頂點在圓心 O,始邊與 x 軸正半軸重合,終邊與單位圓相交於點 P,過 P 作 PM 垂直 x 軸於 M,由直角三角函數定義可知:OM = cos a,MP = sin a,點 P 座標 (cos a, sin a)。其中 cos a = OM*1,sin a = MP*1。Robocode 所有相關座標可用此法求得。
用度做單位度量角制度叫角度制。數學和其他科學常用另一制度 — 弧度制。以角頂點為圓心,任意長半徑作圓,把角所對弧長與半徑比衡量角制度叫弧度制。長度等於半徑弧長叫 1 弧度。這段弧所對圓心角大小也是 1 弧度。通常單位「弧度」省略。例:弧長 1.3325,單位即弧度。由角度弧度關系:2π 弧度 = 360°,2/3π 弧度 = 270°,π 弧度 = 180°,1/2π 弧度 = 90°,可推 1 弧度 = 360°/2π = 57° 即 1 弧度 = 角度 * 180 / Math.PI。
一般規定:正角弧度數為正數,負角弧度數為負數,零角弧度數為零。角集合與實數集合元素建立「一一對應」關系。
到此學習 Robocode 基本原理方向系統和座標系統。對這兩概念有了解,可創建機器人,在戰爭洗禮中不斷完善,適當加入經典戰鬥策略。會越來越完美。別忘了加入機器人智能學習能力,唯有超強學習能力機器人才能無敵天下。下文將見高智能機器人。