有可能在閃躲炮彈和執(zhí)行精確攻擊的演練中學會繼承、多態(tài)性、事件處理以及內(nèi)部類這些內(nèi)容嗎?Robocode 這個游戲即將為全世界的 Java 開發(fā)者實現(xiàn)這個愿望,它把游戲風潮變成了教學工具,人們對它的上癮程度令人吃驚。請跟隨 Sing Li 一起拆解 Robocode,同時著手建造屬于自己的、定制的、小而精悍的戰(zhàn)斗機器。
Robocode 是一個很容易使用的機器人戰(zhàn)斗仿真器,可以在所有支持 Java 2 的平臺上運行。您創(chuàng)建一個機器人,把它放到戰(zhàn)場上,然后讓它同其他開發(fā)者們創(chuàng)建的機器人對手拼死戰(zhàn)斗到底。Robocode 里有一些預先做好的機器人對手讓你入門,但一旦您不再需要它們,就可以把您自己創(chuàng)建的機器人加入到正在世界范圍內(nèi)形成的某個聯(lián)盟里去和世界最強手對陣。
每個 Robocode 參加者都要利用 Java 語言元素創(chuàng)建他或她的機器人,這樣就使從初學者到高級黑客的廣大開發(fā)者都可以參與這一娛樂活動。初級的 Java 的開發(fā)者們可以學習一些基礎(chǔ)知識:調(diào)用 API 代碼、閱讀 Javadoc、繼承、內(nèi)部類、事件處理等等。高級開發(fā)者們可以在構(gòu)建“最優(yōu)品種”的軟件機器人全球競賽中提高他們的編程技巧。在本文中,我們將介紹 Robocode,并指導您從構(gòu)建您平生第一個 Robocode 機器人開始征服世界。我們還將看一下迷人的“后臺”機制,正是它使得 Robocode 起作用。
Robocode 是 Mathew Nelson 的智慧之作,他是 IBM Internet 部門 Advanced Technology 的軟件工程師。請首先訪問 IBM alphaWorks
Robocode
頁面。在這個頁面上,您可以找到 Robocode 系統(tǒng)最新的可執(zhí)行文件。這個分發(fā)包是一個自包含的安裝文件,在下載該分發(fā)包之后,您就可以使用下面的命令行在您的系統(tǒng)上安裝這個軟件包(當然,我們假定您的機器上已經(jīng)預安裝了 Java VM(JDK 1.3.x)):
java -jar robocode-setup.jar
?在安裝過程中,Robocode 將問您是否要使用這個外部的 Java VM 來編譯機器人。您也可以選擇使用作為 Robocode 分發(fā)包一部分而提供的 Jikes 編譯器。
安裝完成后,您可以通過 shell 腳本(robocode.sh)、批處理文件(robocode.bat)或桌面上的圖標來啟動 Robocode 系統(tǒng)。此時,戰(zhàn)場將會出現(xiàn)。在此,您可以通過菜單調(diào)用 Robot Editor 和 compiler。
![]() ![]() |
![]()
|
當您激活 Robocode 時,將看到兩個相關(guān)的 GUI 窗口,這兩個窗口構(gòu)成了 Robocode 的 IDE:
- 戰(zhàn)場
- Robot Editor
圖 1 展示了處于工作狀態(tài)的戰(zhàn)場和 Robot Editor。
圖 1. Robocode IDE

戰(zhàn)場是機器人之間進行戰(zhàn)斗直至分出勝負的場地。主要的仿真引擎被置于其中,并且允許您在這里創(chuàng)建戰(zhàn)斗、保存戰(zhàn)斗以及打開新建的或現(xiàn)有的戰(zhàn)斗。通過界面區(qū)域內(nèi)的控件,您可以暫停或繼續(xù)戰(zhàn)斗、終止戰(zhàn)斗、消滅任何機器人個體或獲取任何機器人的統(tǒng)計數(shù)據(jù)。此外,您可以在此屏幕上激活 Robot Editor。
Robot Editor 是一個定制的文本編輯器,它可以用于編輯生成機器人的 Java 源文件。在它的菜單里集成了 Java 編譯器(用于編譯機器人代碼)以及定制的 Robot 打包器。由 Robot Editor 創(chuàng)建并成功編譯的所有機器人都會處于戰(zhàn)場上一個部署就緒的位置。
Robocode 里的每個機器人都由一個或多個 Java 類構(gòu)成。這些類可以被壓縮成一個 JAR 包。為此,Robocode 的最新版本提供了一個可以在戰(zhàn)場 GUI 窗口中激活的“Robot Packager”。
![]() ![]() |
![]()
|
在寫這篇文章時,Robocode 機器人是一個圖形化的坦克。圖 2 是一個典型的 Robocode 機器人的圖解。
圖 2. 對 Robocode 機器人的詳細分析

請注意,機器人有一門可以旋轉(zhuǎn)的炮,炮上面的雷達也是可以旋轉(zhuǎn)的。機器人坦克車(Vehicle)、炮(Gun)以及雷達(Radar)都可以單獨旋轉(zhuǎn),也就是說,在任何時刻,機器人坦克車、炮以及雷達都可以轉(zhuǎn)向不同的方向。缺省情況下,這些方向是一致的,都指向坦克車運動的方向。
![]() ![]() |
![]()
|
Robocode 機器人的命令集都收錄在 Robocode API Javadoc 中。您將會發(fā)現(xiàn)這些命令都是 robocode.Robot
類的公共方法。在這一部分,我們將分類討論每個可用的命令。
讓我們從移動機器人及其裝備的基本命令開始:
-
turnRight(double degree)
和turnLeft(double degree)
使機器人轉(zhuǎn)過一個指定的角度。
-
ahead(double distance)
和back(double distance)
使機器人移動指定的像素點距離;這兩個方法在機器人碰到墻或另外一個機器人時即告完成。
-
turnGunRight(double degree)
和turnGunLeft(double degree)
使炮可以獨立于坦克車的方向轉(zhuǎn)動。
-
turnRadarRight(double degree)
和turnRadarLeft(double degree)
使炮上面的雷達轉(zhuǎn)動,轉(zhuǎn)動的方向也獨立于炮的方向(以及坦克車的方向)。
這些命令都是在執(zhí)行完畢后才把控制權(quán)交還給程序。此外,轉(zhuǎn)動坦克車的時候,除非通過調(diào)用下列方法分別指明炮(和雷達)的方向,否則炮(和雷達)的指向也將移動。
-
setAdjustGunForRobotTurn(boolean flag)
:如果 flag 被設(shè)置成 true,那么坦克車轉(zhuǎn)動時,炮保持原來的方向。
-
setAdjustRadarForRobotTurn(boolean flag)
:如果 flag 被設(shè)置成 true,那么坦克車(和炮)轉(zhuǎn)動時,雷達會保持原來的方向。
-
setAdjustRadarForGunTurn(boolean flag)
:如果 flag 被設(shè)置成 true,那么炮轉(zhuǎn)動時,雷達會保持原來的方向。而且,它執(zhí)行的動作如同調(diào)用了setAdjustRadarForRobotTurn(true)
。
有許多方法可以得到關(guān)于機器人的信息。下面簡單列舉了常用的方法調(diào)用:
-
getX()
和getY()
可以捕捉到機器人當前的坐標。
-
getHeading()
、getGunHeading()
和getRadarHeading()
可以得出坦克車、炮或雷達當前的方向,該方向是以角度表示的。
-
getBattleFieldWidth()
和getBattleFieldHeight()
可以得到當前這一回合的戰(zhàn)場尺寸。
一旦您掌握了移動機器人以及相關(guān)的武器裝備的方法,您就該考慮射擊和控制損害的任務(wù)了。每個機器人在開始時都有一個缺省的“能量級別”,當它的能量級別減小到零的時候,我們就認為這個機器人已經(jīng)被消滅了。射擊的時候,機器人最多可以用掉三個能量單位。提供給炮彈的能量越多,對目標機器人所造成的損害也就越大。 fire(double power)
和 fireBullet(double power)
用來發(fā)射指定能量(火力)的炮彈。調(diào)用的 fireBullet()
版本返回 robocode.Bullet
對象的一個引用,該引用可以用于高級機器人。
每當機器人在移動或轉(zhuǎn)動時,雷達一直處于激活狀態(tài),如果雷達檢測到有機器人在它的范圍內(nèi),就會觸發(fā)一個事件。作為機器人創(chuàng)建者,您有權(quán)選擇處理可能在戰(zhàn)斗中發(fā)生的各類事件。基本的 Robot
類中包括了所有這些事件的缺省處理程序。但是,您可以覆蓋其中任何一個“什么也不做的”缺省處理程序,然后實現(xiàn)您自己的定制行為。下面是一些較為常用的事件:
-
ScannedRobotEvent
。通過覆蓋onScannedRobot()
方法來處理ScannedRobotEvent
;當雷達檢測到機器人時,就調(diào)用該方法。
-
HitByBulletEvent
。通過覆蓋onHitByBullet()
方法來處理HitByBulletEvent
;當機器人被炮彈擊中時,就調(diào)用該方法。
-
HitRobotEvent
。通過覆蓋onHitRobot()
方法來處理HitRobotEvent
;當您的機器人擊中另外一個機器人時,就調(diào)用該方法。
-
HitWallEvent
。通過覆蓋onHitWall()
方法來處理HitWallEvent
;當您的機器人撞到墻時,就調(diào)用該方法。
我們只需要知道這些就可以創(chuàng)建一些相當復雜的機器人了。您可以通過戰(zhàn)場的幫助菜單或 Robot Editor 的幫助菜單訪問 Javadoc 中其余的 Robocode API。
現(xiàn)在,我們該把學到的知識付諸實踐了。
![]() ![]() |
![]()
|
要創(chuàng)建一個新的機器人,請啟動 Robot Editor 并選擇 File-> New-> Robot。系統(tǒng)將會提示您輸入機器人的名稱,這個名稱將成為 Java 類名。請您在提示符處輸入 DWStraight。接下來,系統(tǒng)還會提示您輸入一個獨一無二的包前綴,它將用作存放機器人(還可能有相關(guān)的 Java 文件)的包的名稱。請在該提示符處輸入 dw。
Robot Editor 就會顯示您要控制這個機器人需要編寫的 Java 代碼。清單 1 是您將會看到的代碼的一個示例:
清單 1. Robocode 生成的 Robot 代碼
|
突出顯示的區(qū)域就是我們添加控制機器人的代碼的地方:
Area 1
我們可以在這片空白里聲明類作用域變量并設(shè)置這些變量的值。這些變量可以在機器人的 run()
方法內(nèi)以及其他一些您可能創(chuàng)建的助手方法內(nèi)使用。
Area 2
戰(zhàn)斗管理器調(diào)用 run()
方法激活機器人。典型情況下,run() 方法包括兩個區(qū)域(即在清單 1 中指出的 Area 2 和 Area 3),您可以在這兩塊空白里添加代碼。您在 Area 2 中加入的代碼每個機器人實例只運行一次。這部分代碼通常用于使機器人先處于一種預設(shè)狀態(tài)后再開始執(zhí)行重復行為。
Area 3
這是典型的 run()
方法實現(xiàn)的第二部分。在此,我們將在無限 while
循環(huán)內(nèi)對機器人可能執(zhí)行的重復行為進行編程。
Area 4
您可以在這一區(qū)域內(nèi)添加機器人在 run()
邏輯內(nèi)使用的助手方法。您也可以在此添加您想要覆蓋的任何事件處理程序。例如,清單 1 里的代碼處理 ScannedRobot
事件,每當雷達檢測到機器人的時候,只是直接向其發(fā)射炮彈。
我們對第一個機器人(DWStraight)的代碼的更新如清單 2 中紅色標記所示。
清單 2. DWStraight 機器人代碼的增加部分
|
下面我們逐區(qū)地描述這個第一個機器人將做些什么:
Area 1我們沒有在這個機器人的程序中指定任何類作用域變量。
Area 2
為了使機器人處于已知的狀態(tài),我們通過 turnLeft(getHeading())
使它轉(zhuǎn)到 0 度的方向。
Area 3
在這個重復性的部分,我們使用語句 ahead(1000)
讓機器人盡其所能向前移動到最遠的地方。當機器人撞到墻或其他機器人時,就會停下來。接著,我們通過 turnRight(90)
使它向右轉(zhuǎn)。在重復執(zhí)行這一行為時,機器人基本上是在沿著墻按順時針方向移動。
Area 4
在此,除處理自動生成的 ScannedRobot
事件并向被發(fā)現(xiàn)的機器人直接射擊之外,我們還會檢測 HitByBullet
事件,并且讓機器人在被擊中的時候轉(zhuǎn)過 180 度(沿順時針方向或逆時針方向)。
![]() ![]() |
![]()
|
在 Robot Editor 菜單上選擇 Compiler-> Compile編譯您的機器人代碼。現(xiàn)在我們可以嘗試第一回合的戰(zhàn)斗了。切換回戰(zhàn)場并選擇菜單上的 Battle-> New,將會出現(xiàn)一個類似于圖 3 中所示的對話框。
圖 3. New Battle 對話框

請先將我們的機器人 dw.DWStraight 添加到戰(zhàn)斗中,然后再添加一個對手機器人,比如 sample.SittingDuck。單擊 Finish,戰(zhàn)斗就開始了。不可否認,同 SittingDuck 戰(zhàn)斗并不怎么有趣,但是您可以目睹這個叫做 DWStraight 的機器人在缺省情況下的行為。試試 sample 文件夾里的其他機器人,看看 DWStraight 同這些機器人的戰(zhàn)斗情況如何。
當您準備開始研究另外一個機器人的代碼時,請先看看隨 參考資料 中的代碼分發(fā)包一起提供的 dw.DWRotater 這個機器人的代碼。在缺省情況下,這個機器人將會:
- 移動到戰(zhàn)場中心
- 一直轉(zhuǎn)動它的炮,直到檢測到機器人
- 每次嘗試以不同的角度在離被檢測到的機器人前方不遠的地方射擊
- 每當它被另外一個機器人擊中時,它都會迅速的來回移動
這段代碼簡單易懂,所以我們在這里就不做分析了,但是我鼓勵您試驗一下。Robocode 中的 sample 包還提供了許多其他機器人的代碼。
隨著您設(shè)計機器人的水平的提高,機器人的代碼主體將充分增長。對這些代碼的一種模塊化處理方法是把代碼分解成獨立的 Java 類,然后通過打包器把這些 Java 類打包成一個單獨的包(JAR 文件),并將它包括在您的機器人分發(fā)包內(nèi)。Robocode 將自動在它的 robots 目錄下的包里找到 robot 類。
任何人都可以創(chuàng)建 Robot
子類并添加用于構(gòu)建機器人的新功能。Robocode 提供了一個叫做 AdvancedRobot
的 Robot
子類,它允許異步 API 調(diào)用。雖然對 AdvancedRobot
類的描述超出了本文的范圍,但我鼓勵您在掌握了基本的 Robot
類的操作后,試驗一下這個高級類。
![]() |
|
![]() ![]() |
![]()
|
通過“在后臺”對 Robocode 進行分析,我們發(fā)現(xiàn)復雜的仿真引擎既具高性能(為了以現(xiàn)實的速度生成戰(zhàn)斗)又具靈活性(使創(chuàng)建復雜的機器人邏輯不存在障礙)。特別感謝 Robocode 的創(chuàng)建者 Mathew Nelson 無私的提供了仿真引擎體系結(jié)構(gòu)的內(nèi)部信息。
圖 4 中所示的仿真引擎利用的是大多數(shù)現(xiàn)代的 Java VM 都提供的非搶占式線程技術(shù),并結(jié)合使用了 JDK GUI 和 2D 圖形庫提供的生成功能。
圖 4. Robocode 仿真引擎體系結(jié)構(gòu)

請注意,所仿真的每個機器人都在它自己的 Java 線程上,它可以在任何可適用的地方利用 VM 本地線程映射機制。戰(zhàn)斗管理器線程是系統(tǒng)的控制器:它安排仿真并驅(qū)動圖形化的生成子系統(tǒng)。圖形化的生成子系統(tǒng)本身是基于 Java 2D 和 AWT 的。
為了減少共享資源可能帶來的問題(以及有可能隨之出現(xiàn)的死鎖或阻塞仿真引擎),戰(zhàn)斗管理器線程和機器人線程之間的耦合應(yīng)當非常松散。為了實現(xiàn)這種松散耦合,每個機器人線程都將有屬于自己的事件隊列。獲取及處理這些事件都是在每個機器人自己的線程內(nèi)進行。這種基于線程的隊列有效地消除了戰(zhàn)斗管理器線程和機器人線程之間(或機器人線程本身之間)可能存在的任何爭用。
您可以把 Robocode 仿真器引擎看作是一個仿真器程序,該程序在運行時會使用一些插件(定制機器人);這些插件可以利用已有的 API( robocode.Robot
類的方法)。實際上,每個機器人都是一個獨立的 Java 線程,同時 run()
方法內(nèi)包含了每個線程上將要執(zhí)行的邏輯。
在任何時候,機器人線程都可以調(diào)用由它的父類 robocoode.Robot
類所提供的 API。典型情況下,這將通過調(diào)用 Object.wait()
阻塞機器人線程。
戰(zhàn)斗管理器線程管理機器人、炮彈及它們在戰(zhàn)場上的生成。仿真“時鐘”是根據(jù)戰(zhàn)場上生成的幀的數(shù)目來標記的。用戶可以調(diào)整真實的幀的速度。
在一個典型的回合中,戰(zhàn)斗管理器線程喚醒每個機器人線程,然后等待機器人完成它的一輪戰(zhàn)斗(即,再次調(diào)用一個阻塞 API)。等待的間隔時間通常是幾十毫秒,即使是最復雜的機器人,使用現(xiàn)今典型的系統(tǒng)速度進行策略安排和計算,也只要 1 到 2 毫秒的時間。
以下是戰(zhàn)斗管理器線程執(zhí)行的邏輯的偽代碼:
清單 3. 戰(zhàn)斗管理器的邏輯的偽代碼
|
請注意,在 for 循環(huán)內(nèi)部,戰(zhàn)斗管理器線程的等待時間不會超過最大的時間間隔。如果機器人線程沒有及時調(diào)用阻塞 API(典型情況下是由于一些應(yīng)用程序邏輯錯誤或無限循環(huán)),那么,它將繼續(xù)進行戰(zhàn)斗。生成一個 SkippedTurnEvent
并將其加入機器人事件隊列中,用來通知高級機器人。
AWT 和 Java 2D 線程就是當前實現(xiàn)中的生成子系統(tǒng),它從戰(zhàn)斗管理器中獲取命令并生成戰(zhàn)場。它同系統(tǒng)的其余部分是完全分離的。我們可以預見到,在這個生成子系統(tǒng)將來的修訂版中,它可以被替換掉(比如,用 3-D 生成器)。在當前的實現(xiàn)中,只要 Robocode 應(yīng)用程序被最小化,生成就禁用了,這可以以更快的速度進行仿真。
![]() ![]() |
![]()
|
通過 alphaWorks Robocode 站點上的一個討論組(請參閱 參考資料 ),Mathew Nelson 可以同 Robocode 用戶社區(qū)保持緊密的反饋聯(lián)系。許多反饋都并入了真實的代碼中。Mathew 已計劃即將要進行的一些改進有:
- 通過不同的物體和障礙來定制戰(zhàn)場地圖
- 基于團隊的戰(zhàn)斗
- 對聯(lián)賽或聯(lián)盟的集成支持
- 用戶可選擇坦克車體/炮/雷達/武器的樣式
![]() ![]() |
![]()
|
對于一個從 2001 年 7 月 12 日出現(xiàn)在公眾面前的項目,Robocode 的出名簡直讓人吃驚。盡管最新的可用版本還不到 1.0(在寫這篇文章時是版本 0.98.2),但它已經(jīng)是全世界的大學校園以及公司的 PC 機上頗受歡迎的娛樂活動了。Robocode 聯(lián)盟(或 roboleagues)正如雨后春筍般出現(xiàn),在這些聯(lián)盟里,人們通過因特網(wǎng)讓自己定制的作品相互較量。大學教授們一直在挖掘 Robocode 的教育特性,并且已經(jīng)把它納入了大學里的計算機科學課程。在因特網(wǎng)上,Robocode 用戶組、討論列表、FAQ、教程和 Webring 隨處可見。
顯然,Robocode 已經(jīng)填補了大眾化的寓教于樂領(lǐng)域的空白 ― 它為學生們和熬夜的工程師們提供簡便、有趣、非脅迫卻富競爭力的方式,釋放他們的創(chuàng)造力,而且有可能實現(xiàn)他們征服世界的夢想。