用遊戲來學習Java技術(Robocode攻略)

  Robocode(用遊戲來學習Java技術還是用Java來玩遊戲?)

用你的JAVA編程技術來玩遊戲吧!不會JAVA?那就用遊戲來學習JAVA吧!

什麼是Robocode?用遊戲來學習Java技術(Robocode攻略)

其實我對機器人一直很感興趣。我記得在我還是初中的時候,就知道 AplleⅡ上有一個程序,用它來編寫簡單的機器人程序,然後相互作戰。當時自己還完全不懂編程,總是向往著,那神秘的編程高手玩的遊戲是怎樣的?

Robocode就是這樣一個東西,但是更好一些。它是一個基於Java語言的機器人作戰遊戲。 其代碼的編寫和建模都不錯,玩起來也很有趣。Robocode是很多”編程遊戲”軟體中的一個,他們共同的特徵是在沒有用戶輸入的狀態下許多機器人在一個及競技場中比賽,用戶必須編制一個高效的機器人來取勝。Robocode特別的像一場機器人坦克的大混戰,它們互相開火直到只剩一個勝利者。程序完全是由JAVA編寫,並且玩家必須要創造一個繼承自Robot類的類。

你希望在玩遊戲的過程中、在閃躲炮彈、執行精確攻擊的演練中學會Java編程的 繼承、多態性、事件處理以及內部類這些內容嗎?Robocode 這個遊戲為全世界的 Java 開發者做到這個願望,它把遊戲風潮變成了教學工具,人們對它的上癮程度令人吃驚。下面,我參考網友 Sing Li 以前寫的文章,讓我們一起來拆解 Robocode,同時著手建造屬於自己的、定制的、小而精悍的戰鬥機器吧!

Robocode 是一個很容易使用的機器人戰鬥仿真器,可以在所有支持 Java 2 的平台上運行。您創建一個機器人,把它放到戰場上,然後讓它同其他開發者們創建的機器人對手拼死戰鬥到底。Robocode 裡有一些預先做好的機器人對手讓你入門,但一旦您不再需要它們,就可以把您自己創建的機器人加入到正在世界範圍內形成的某個聯盟裡去和世界最強手對陣。

每個 Robocode 參加者都要利用 Java 語言元素創建他或她的機器人,這樣就使從初學者到高級黑客的廣大開發者都可以參與這一娛樂活動。初級的 Java 的開發者們可以學習一些基礎知識:調用 API 代碼、閱讀 Javadoc、繼承、內部類、事件處理等等。高級開發者們可以在構建「最優品種」的軟體機器人全球競賽中提高他們的編程技巧。在本文中,我們將介紹 Robocode,並指導您從構建您平生第一個 Robocode 機器人開始征服世界。我們還將看一下迷人的「後台」機制,正是它使得 Robocode 起作用。

首先當然是下載和安裝 Robocode 啦

Robocode 是 Mathew Nelson 的智慧之作,他是 IBM Internet 部門 Advanced Technology 的軟體工程師。現在Robocode的主頁已經搬遷到sourceforge這個開源網站上了,大家可以在這裡下載RobotCode的最新版http://robocode.sourceforge.net/到3月21日為止最新版本是1.0.7,大小為3.2M。

好了,下載回來後當然還要在你的電腦上安裝JAVA運行庫才行的哦~地址是http://java.sun.com/getjava

1.先安裝好JAVA運行庫,好像需要重啟的?忘記了……

2.把下載回來的robocode-setup.jar復制到c盤根目錄

3.打開 開始菜單 的「運行」,輸入 java -jar “c:\robocode-setup.jar” 進行安裝

4.安裝完後就可以在開始菜單中找到Robocode的菜單了,來~我們進入戰場咯!

安裝完成後,您也可以通過 shell 腳本(robocode.sh)、批處理文件(robocode.bat)或桌面上的圖標來啟動 Robocode 系統。此時,戰場將會出現。在此,您可以通過菜單調用 Robot Editor 和 compiler。

Robocode 系統組件

當您啟動 Robocode 時,將看到兩個GUI窗口,這兩個窗口構成了 Robocode 的 IDE:

圖 1. Robocode IDE

用遊戲來學習Java技術(Robocode攻略)

戰場是機器人之間進行戰鬥直至分出勝負的場地。主要的仿真引擎被置於其中,並且允許您在這裡創建戰鬥、保存戰鬥以及打開新建的或現有的戰鬥。通過界面區域內的控件,您可以暫停或繼續戰鬥、終止戰鬥、消滅任何機器人個體或獲取任何機器人的統計數據。此外,您可以在此螢幕上激活 Robot Editor。

Robot Editor 是一個定制的文本編輯器,它可以用於編輯生成機器人的 Java 源文件。在它的菜單裡集成了 Java 編譯器(用於編譯機器人代碼)以及定制的 Robot 打包器。由 Robot Editor 創建並成功編譯的所有機器人都會處於戰場上一個部署就緒的位置。

Robocode 裡的每個機器人都由一個或多個 Java 類構成。這些類可以被壓縮成一個 JAR 包。為此,Robocode 的最新版本提供了一個可以在戰場 GUI 窗口中激活的「Robot Packager」。

對 Robocode 機器人的詳細分析

在寫這篇文章時,Robocode 機器人是一個圖形化的坦克。圖 2 是一個典型的 Robocode 機器人的圖解。

圖 2. 對 Robocode 機器人的詳細分析

用遊戲來學習Java技術(Robocode攻略)

請注意,機器人有一門可以旋轉的炮,炮上面的雷達也是可以旋轉的。機器人坦克車(Vehicle)、炮(Gun)以及雷達(Radar)都可以單獨旋轉,也就是說,在任何時刻,機器人坦克車、炮以及雷達都可以轉向不同的方向。缺省情況下,這些方向是一致的,都指向坦克車運動的方向。

我們先不考慮怎麼編程來做到機器人戰鬥,我們先用自帶的例子機器人來一場戰鬥吧

單擊菜單上的Battle,然後選New,出現了New Battle對話框

圖 3. New Battle 對話框

用遊戲來學習Java技術(Robocode攻略)

左邊的框是Packages,相當於一個文件夾,裡麵包含多個Robots(機器人)

我們選擇sample這個包,裡面有Corners、Crazy、Fire等等很多例子的機器人了

隨便選擇幾個你喜歡的,然後按Add添加到Selected Robots框,進了這個框就是準備要上戰場的機器人了~選擇好後,按 StartBattle 開戰吧!

現在你已經知道怎樣可以使用機器人去戰鬥並且也構建好你的戰場了,好,下面我們學習怎樣來編寫屬於自己的戰鬥機器人!!

戰場是機器人之間進行戰鬥直至分出勝負的場地。主要的仿真引擎被置於其中,並且允許在這裡創建戰鬥、保存戰鬥以及打開新建的或現有的戰鬥。通過界面區域內的控件,可以暫停或繼續戰鬥、終止戰鬥、消滅任何機器人個體或獲取任何機器人的統計數據。此外,我們可以在此螢幕上的Robot菜單打開 Editor,就是我們機器人的代碼編輯器了!Robot Editor 是一個定制的文本編輯器,它可以用於編輯生成機器人的 Java 源文件。在它的菜單裡集成了 Java 編譯器(用於編譯機器人代碼)以及定制的 Robot 打包器。由 Robot Editor 創建並成功編譯的所有機器人都會處於戰場上一個部署就緒的位置。我們就是要在這裡編寫機器人了。

選擇「File」》「New」》「Robot」來新建一個機器人。它會首先要你輸入這個機器人的名字(注意名字首字母要大寫哦),然後要你輸入包的名字(就是保存這個機器人的文件夾名稱),這樣就生成了一個蠢蠢的機器人XForce的代碼了~因為我們還沒替它加上人工智能,呵呵!

用遊戲來學習Java技術(Robocode攻略)

現在單擊菜單的Complie下的Complie進行編譯,保存好,我們的機器人已經生產出來咯~

現在關閉Editor,在進入New Battle,Pakeage下選擇你剛才的包的名字,Robot下就有了我們新建的XForce機器人了~添加進去吧,然後選擇多幾個其他的機器人,開始戰鬥!

看~我們的XForce在戰鬥了!

用遊戲來學習Java技術(Robocode攻略)

是否覺得它太蠢了點呢?來,繼續來學習~~

Robocode 機器人是一個圖形化的坦克,請注意,機器人有一門可以旋轉的炮,炮上面的雷達也是可以旋轉的。機器人坦克車(Vehicle)、炮(Gun)以及雷達(Radar)都可以單獨旋轉,也就是說,在任何時刻,機器人坦克車、炮以及雷達都可以轉向不同的方向。缺省情況下,這些方向是一致的,都指向坦克車運動的方向。

附:Robot 命令

Robocode 機器人的命令集都收錄在 Robocode API Javadoc 中。這些命令都是 robocode.Robot 類的公共方法。

(1)移動機器人、炮和雷達

移動機器人及其裝備的基本命令:

turnRight(double degree) 和 turnLeft(double degree) 使機器人轉過一個指定的角度。

ahead(double distance) 和 back(double distance) 使機器人移動指定的像素點距離;這兩個方法在機器人碰到牆或另外一個機器人時即告完成。

turnGunRight(double degree) 和 turnGunLeft(double degree) 使炮可以獨立於坦克車的方向轉動。

turnRadarRight(double degree) 和 turnRadarLeft(double degree) 使炮上面的雷達轉動,轉動的方向也獨立於炮的方向(以及坦克車的方向)。

這些命令都是在執行完畢後才把控制權交還給程序。此外,轉動坦克車的時候,除非通過調用下列方法分別指明炮(和雷達)的方向,否則炮(和雷達)的指向也將移動。

setAdjustGunForRobotTurn(boolean flag):如果 flag 被設置成 true,那麼坦克車轉動時,炮保持原來的方向。

setAdjustRadarForRobotTurn(boolean flag):如果 flag 被設置成 true,那麼坦克車(和炮)轉動時,雷達會保持原來的方向。

setAdjustRadarForGunTurn(boolean flag):如果 flag 被設置成 true,那麼炮轉動時,雷達會保持原來的方向。而且,它執行的動作如同調用了 setAdjustRadarForRobotTurn(true)。

(2)獲取關於機器人的信息

getX() 和 getY() 可以捕捉到機器人當前的坐標。

getHeading()、getGunHeading() 和 getRadarHeading() 可以得出坦克車、炮或雷達當前的方向,該方向是以角度表示的。

getBattleFieldWidth() 和 getBattleFieldHeight() 可以得到當前這一回合的戰場尺寸。

(3)射擊命令

一旦掌握了移動機器人以及相關的武器裝備的方法,我們就該考慮射擊和控制損害的任務了。每個機器人在開始時都有一個缺省的「能量級別」,當它的能量級別減小到零的時候,我們就認為這個機器人已經被消滅了。射擊的時候,機器人最多可以用掉三個能量單位。提供給炮彈的能量越多,對目標機器人所造成的損害也就越大。 fire(double power) 和 fireBullet(double power) 用來發射指定能量(火力)的炮彈。調用的 fireBullet() 版本返回 robocode.Bullet 對象的一個引用,該引用可以用於高級機器人。(也就是說,當你確定能擊中對方,火力越大越好咯^_^)

(4)事件

每當機器人在移動或轉動時,雷達一直處於激活狀態,如果雷達檢測到有機器人在它的範圍內,就會觸發一個事件。作為機器人創建者,我們有權選擇處理可能在戰鬥中發生的各類事件。基本的 Robot 類中包括了所有這些事件的缺省處理程序。但是,們可以覆蓋其中任何一個「什麼也不做的」缺省處理程序,然後做到自己的定制行為。下面是一些較為常用的事件:

ScannedRobotEvent。通過覆蓋 onScannedRobot() 方法來處理 ScannedRobotEvent;當雷達檢測到機器人時,就調用該方法。

HitByBulletEvent。通過覆蓋 onHitByBullet() 方法來處理 HitByBulletEvent;當機器人被炮彈擊中時,就調用該方法。

HitRobotEvent。通過覆蓋 onHitRobot() 方法來處理 HitRobotEvent;當您的機器人擊中另外一個機器人時,就調用該方法。

HitWallEvent。通過覆蓋 onHitWall() 方法來處理 HitWallEvent;當您的機器人撞到牆時,就調用該方法。

很多研究Robocode的 玩家都被其中的方向及坐標弄糊塗了。整個螢幕哪個是0度角,整個是坐標原點呢? 順時針與逆時針的方向如何區分?

一段英文的翻譯及說明:

heading – absolute angle in degrees with 0 facing up the screen, positive clockwise. 0 <= heading < 360.

bearing – relative angle to some object from your robots heading, positive clockwise. -180 < bearing <= 180

heading:是機器人方向與螢幕正上方的角度差,方向在0到360之間.

bearing:是機器人的某個部件如雷達發現的目標與方向的角度差,順時針為正角度在-180到180之間

幾個在Robocode中很重要的概念:

坐標系:Robocode整個坐標系都是戰場螢幕以左下角為原點

絕對方向系:Robocode中不管機器人在哪個方向都是以靜態戰場螢幕為參照的絕對角度(也即大家說的Heading),正上方為0度角。也即不管是Robot,Gun,Radar向北為0,向東為90,向南為180,向西為270。

相對方向系:相對方向是Robot,Gun,Radar以機器人的動態heading角度為參照的角度差不再以整個靜態螢幕為參照了,叫它相對因為機器人的heading是隨著機器人移動而不停的在改變,heaing只是個相對物體。

順時針和逆時針是看另一機器人是在你的Heading角度的(0,180)還是(-180,0)之間。

再次提醒:Heading是個靜態角度,正上方總為0.不管是取Heading,還是取方向。Bearing是個角度差值,是由參照的Heading和發現時的Heading的差值。方向的問題就說到這,歡迎大家討論。

我看了Robocode的基礎知識,自己寫了個bot,放到BattleField上卻是屢戰屢敗……傷心ing。

Bot對於周圍環境的了解非常有限。它可以知道其它機器人的距離、方位、方向、速度和能量等級。但是,它看不到子彈。怎麼才可以有效的躲避對方的子彈呢?

Bot雖然看不到子彈,但是對方的能量等級還是可以scan到了。對方只要發射子彈就會耗損能量,並且耗損的能量介於0和3之間。根據這些線索,如何發現其它機器人正向它開炮對於「笨笨」的Bot不就易如反掌了? ^_^

當Bot檢測到對方發射子彈的信息時,向左或向右移動一小步,嘿嘿,子彈就打不到咯~並且大多數Bot的瞄準方法是要麼直接向目標開炮,要麼試著根據Bot的速度和方向來推算位置。如果我的Bot不移動,兩種算法都會正好沖著這個Bot的當前位置開炮。哈哈哈,這時我的Bot再移動,不就全部都打不到啦。(是不是頗有武俠小說裡以靜制動的高手味道?^_^)

下面是部分代碼和註釋:

double previousEnergy = 100;//初始狀態對方能量為100

int movementDirection = 1;//移動方向

int gunDirection = 1;//炮管方向

/**

* 當檢測到對方Bot,觸發事件

* @param e

*/

public void onScannedRobot(ScannedRobotEvent e) {

//調整自己和對方之間的角度

setTurnRight(e.getBearing()+90-30*movementDirection);

//如果對方的能量損耗一定值,進行躲避動作

double changeInEnergy = previousEnergy – e.getEnergy();

if (changeInEnergy>0 && changeInEnergy<=3) {

//躲避!

movementDirection = -movementDirection;//和上次的躲避方向相反

setAhead((e.getDistance()/4+25)*movementDirection);

}

//將炮管指向對方當前位置

gunDirection = -gunDirection;

setTurnGunRight(99999*gunDirection);

//射擊

fire(1);

//重新設置對方能量

previousEnergy = e.getEnergy();

}

是不是很簡單?這個技巧還存在問題。子彈一發射,我的Bot就移動,所以它最終可能會移回炮彈軌跡之內。最好是在可能子彈要到達時再移動。

我有個更大膽的假設:因為現在我的Bot命中率還不高,那麼如果我的Bot一直不開火,只是躲避對方的子彈的話,能不能拖到對方的能量為0呢?確實存在一點問題。對方法彈一發射,我的Bot就移動,並且這個移動是規律的來回移動。如果移動距離短了,就可能在回來的時候撞到對方的子彈;如果移動距離長了,就等於做一個直線運動,對方很容易計算得到Bot的運動軌跡。還有一個問題,躲避的時候很有可能撞到牆上……(撞牆是要減energy的:~()

針對以上的問題,我另寫了一個Bot。代碼如下:

import robocode.*;

public class HanicBot extends AdvancedRobot{

private double eDist; //對方的距離

private double move; //移動的距離

private double radarMove = 45; //雷達移動的角度

private double dFirePower; //火力

/**

* main func run()

*/

public void run() {

eDist = 300;

while(true){

//每過一個周期,運動隨機的距離

double period = 4*((int)(eDist/80)); //周期;敵人越接近,周期越短,移動越頻繁

//周期開始,則移動

if(getTime()%period == 0){

move = (Math.random()*2-1)*(period*8 – 25);

setAhead(move + ((move >= 0) ? 25: -25));

}

//避免撞牆

double heading = getHeadingRadians(); //取得bot方向的弧度數

double x = getX() + move*Math.sin(heading); //移動move後將要達到的x坐標

double y = getY() + move*Math.cos(heading); //移動move後將要達到的y坐標

double dWidth = getBattleFieldWidth(); //戰場的寬度

double dHeight = getBattleFieldHeight(); //戰場的長度

//當(x,y)超過指定的範圍,則反向移動move

if(x < 30 || x > dWidth-30 || y < 30 || y > dHeight-30){

setBack(move);

}

turnRadarLeft(radarMove); //轉動雷達

}

}//end run()

/**

* 當檢測到對方Bot,觸發事件

* @param e

*/

public void onScannedRobot(ScannedRobotEvent e) {

eDist = e.getDistance(); //取得對方距離

radarMove = -radarMove; //設置雷達

double eBearing = e.getBearingRadians(); //取得和對方相對角度的弧度數

//將bot轉動相對的角度,以後bot的運動將是以對方為圓心的圓周運動

setTurnLeftRadians(Math.PI/2 – eBearing);

//轉動炮管指向對方

setTurnGunRightRadians(robocode.util.Utils.normalRelativeAngle(

getHeadingRadians() + eBearing – getGunHeadingRadians()));

//根據對方距離射擊

dFirePower = 400/eDist;

if (dFirePower > 3){

dFirePower = 3;

}

fire(dFirePower);

}

}

首先,為了迷惑對方,不讓對方容易的得到Bot的移動規律,Bot就要在一定的時間內做出隨機的運動,這個很容易辦到。並且,我給Bot的運動改變時間規定了周期。這個周期隨離對方的距離改變,敵人越接近,周期越短,移動越頻繁。

double period = 4*((int)(eDist/80));

if(getTime()%period == 0){

move = (Math.random()*2-1)*(period*8 – 25);

setAhead(move + ((move >= 0) ? 25: -25));

}

其次,Bot的運動不是呈直線的。而是以對方為圓心的圓周運動。

setTurnGunRightRadians(robocode.util.Utils.normalRelativeAngle(

getHeadingRadians() + eBearing – getGunHeadingRadians()));

最後是如何避免撞牆。這裡要用到點三角函數-_-!! 原理就是,計算Bot一次運動後將要達到的坐標是不是位於規定的危險區域。如果是,則立即反方向運動。

double heading = getHeadingRadians();

double x = getX() + move*Math.sin(heading);

double y = getY() + move*Math.cos(heading);

double dWidth = getBattleFieldWidth();

double dHeight = getBattleFieldHeight();

if(x < 30 || x > dWidth-30 || y < 30 || y > dHeight-30){

setBack(move);

}

這個Bot的威力如何?呵呵,我去測試一下先~

好了,就說到這裡了,歡迎各大高手來踩……

關於其它的一些”編程遊戲”

有許多軟體是基於這種思想的,Robocode它自己就是來源於機器人大戰Robot Battle(http://www.robotbattle.com/)這款軟體。其它的編程遊戲還包括:

· AI Fleet Commander

· AI Wars

· AT-Robots

· Bolo

· BotWarz

· C-Robots

· Cadaver

· CodedWombat

· Colobot

· Corewars

· CybWar

· GRobots

· DroidBattles

· Karel the Robot

· Mindrover

· IntelliBots

· Omega

· RealTimeBattle

· Robot Wars

· RoboWar

· SRobots

· VBRobots

就我所看過的”編程遊戲”,Robocode是最簡單上手的。

· 它非常容易上手,是特別為教學而設計的

· 它具有平滑且吸引人的圖形

· 它完全地將編輯器,編譯器和運行環境集成在了一起。

· 它是由JAVA編寫的,且JAVA非常適合當作初學語言