坦克机器人编程大赛具体实现
方法
快递客服问题件处理详细方法山木方法pdf计算方法pdf华与华方法下载八字理论方法下载
java项目计科三班19号廖彬彬
HUNAN UNIVERSITY
科目: 坦克机器人编程
院系: 信息科学与工程学院
专业: 计算机科学与技术
学号: 20110801319
姓名: 廖彬彬
2013年 12月23日
1.Robocode简介:
Robocode 是一位IBM的工程师 Mat Nelson 用Java 语言所创造的机器人战斗仿真引擎。Robocode不是一个完整游戏,它是个半成品,一个专为程序员们准备的游戏。它使得你不用不面对一些无聊琐碎但又不得不做的工作,编写界面、处理图像、处理音频。而是用一种更简单的办法,让我们享受游戏开发的乐趣。 Robocode 它为你处理好一切细节。你所做就是为你的机器人坦克编写智能程序,让它能够移动、进攻、防御、躲避、开火。而它的对手就是跟你一样其他程序员编写机器人程序。
这就是Robocode魅力所在,最简单,只用几十行代码,就能立刻创造出一个简单但完整机器人。你可以不停的修改你的程序,设计新射击模型、躲避模型、移动模型,还可以在网上下载由其他程序员编写的水平更高的机器人,与它们比试一下,看看自己的水平到底如何。
从2001年7 月诞生之日算起,Robocode发展到如今,已经吸引了全世界无数爱好者参与其中。不仅出现了许多交流Robocode 技术的网站,也有各种规模不等的Robocode 大赛在各地进行。还等什么,赶快进入Robocode 这个有趣的世界。
2.准备知识
Robocode坐标系统
1) 坐标系:Robocode整个坐标系都是战场屏幕以左下角为原点。
2) 绝对方向系:Robocode中不管机器人在哪个方向都是以静态战场屏幕为参照
物的绝对角度(Heading),正上方为0 度角。即是向北为0,向东为90,向
南为180,向西为270。
3) 相对方向系:相对方向是以机器人的动态heading角度为参照物的角度差,
不再以整个静态屏幕为参照了。叫它相对是因为机器人的heading 是随着机
器人移动而不停的在改变,heading 只是个相对物体。
4) Heading:是机器人方向与屏幕正上方的角度差,方向在0 到360 之间。
5) Bearing:是机器人的某个部件如雷达发现的目标与方向的角度差,顺时针
为正角度在-180 到180 之间。
2.1坦克结构
机器人的结构包含三个部分:Body 车身,Gun 炮,Radar雷达。如图所示:
, 车身:
是一个比较笨重的部分, 负责移动,它的基本操作有:turnLeft(double degree)
和turnRight(double degree)分别是使机器人(左右)转过一个指定的角度。 ahead(double distance)和back(double distance) )分别是使机器人(前后)移动指定的像素点距离,;这两个方法在机器人碰到墙或碰到另外一个机器人时即告完成。
, 炮:
是用来发射炮弹攻击敌人的武器,它的基本操作有:turnGunLeft(double degree)
和turnGunRight(doubledegree)分别是使炮可以独立于坦克车的方向(左右)转动指定的角度。fire(double power)发射指定能量的炮弹,能量范围是(0.1, 3)。
, 雷达:
是机器人取得敌人信息的主要器官,它的基本操作有:
turnRadarLeft(double degree)和turnRadarRight(double degree)分别是使炮上面的雷达(左右)转动指定的角度,转
动的方向也独立于炮的方向(以及坦克车的方向)。
2.2.坦克位置操作
, 运动:
默认情况下三个部分的运动是相对锁住的,要使他们能独立运动,Robocode 提供了三个方法来解锁:
setAdjustGunForRobotTurn(boolean flag):如果flag被设置成true,那么坦克车转动时,炮保持原来的方向。
setAdjustRadarForRobotTurn(boolean flag):如果flag被设置成true,那么坦克车(和炮)转动时,雷达会保持原来的方向。
setAdjustRadarForGunTurn(boolean flag):如果flag 被设置成true,那么炮转动时,雷达会保持原来的方向。而且,它执行的动作如同调用了
setAdjustRadarForRobotTurn(true)。
, 获取位置信息
getX()和getY():可以捕捉到机器人当前所在战场中的坐标。
getHeading()、getGunHeading()和getRadarHeading():分别可以得出坦克车、炮或雷达当前的方向,该方向是以角度表示的。
getBattleFieldWidth()和getBattleFieldHeight():可以得到当前这一回合的战场尺寸。
2.3高级机器人
高级机器人AdvancedRobot,它与的区别在于:Robot 是线程阻塞式的,AdvancedRobot是可以使用非阻塞式的调用。使用高级机器人,父类为AdvancedRobot。我们本项目将主要介绍高级机器人的编程实现。
, 阻塞调用与非阻塞调用的对应关系:
turnRight()与setTurnRight();
turnLeft() 与setTurnLeft() ;
turnGunRight()与setTurnGunRight();
turnGunLeft() 与setTurnGunLeft() ;
turnRadarRight()与setTurnRadarRight();
turnRadarLeft() 与setTurnRadarLeft() ;
ahead() 与setAhead() ;
back()与 setback() ;
3.编程实现
上述的知识是我们机器人编程所需要的基础知识,在这些基础知识上面我们将进行自己的机器人编程。
3.1设置外形:
setColors(Color.red,Color.blue,Color.green);分别设置车身红色,炮台蓝色,雷达绿色。
3.2确定炮台,雷达需要旋转的角度
用到勾股定理,以及反正弦知识。首先将敌人和自己的位置看为两个坐标。然后依照直角坐标系建立一个三角形。求出两个点相对直角坐标的角度。同时,将这个相对的角度化为圆周坐标系的绝对角度,从而防止当相对角度大于360时旋转不必要的整圆周。
, 勾股定理求斜边函数
//returns the distance between two x,y coordinates '**' //以两边长求得与对手之间的距离
public double getrange( double x1,double y1, double x2,double y2 )
{
double xo = x2-x1;
double yo = y2-y1;
double h = Math.sqrt( xo*xo + yo*yo );
return h;
}
, 在直角坐标系中中求相对角度,圆坐标系求绝对角度
//gets the absolute bearing between to x,y coordinates //根据x,y的坐标求出绝对角度,见"坐标锁定"利用直角坐标系来反求出角度。,,,
public double absbearing( double x1,double y1, double x2,double y2 )
{
double xo = x2-x1;
double yo = y2-y1;
double h = getrange( x1,y1, x2,y2 );
if( xo > 0 && yo > 0 )
{
//反正弦定义,对边除斜边得弧度.以robocode中的绝对方向系及坐标系参照 //x,y为正右上角为0-90,x正y负右下角为90-180,x,y负左下角180-270,x负,y正右上角270-360
//此处要理解robocode中的绝对角度是上为0,下为180,如以中心为点划分象限则得到下面的结果
return Math.asin( xo / h );
}
if( xo > 0 && yo < 0 )
{
return Math.PI - Math.asin( xo / h ); //x为正,y为负第二象限角
}
if( xo < 0 && yo < 0 )
{
return Math.PI + Math.asin( -xo / h ); //第三象限内180+角度
}
if( xo < 0 && yo > 0 )
{
return 2.0*Math.PI - Math.asin( -xo / h ); //四象限360-角度
}
return 0;
}
3.3扫描敌人位置
void doScanner()
{
double radarOffset; //雷达偏移量
if (getTime() - target.ctime > 4) //???why来回扫了4个回合都没扫到意味失去了目标,再全扫一遍
{
radarOffset = 360; }
else
{
//通过扫描决定雷达旋转的弧度,"见基本原理方向剖析及目标锁定.雷达弧度-敌人角度得到两者相差为旋转值
radarOffset=getRadarHeadingRadians()-absbearing(getX(),getY
(),target.x,target.y);
//this adds or subtracts small amounts from the bearing
for the radar to produce the wobbling
//and make sure we don't lose the target
//在得到的角度中加或减一点角度,让雷达很小的范围内摆而不失去目标
if (radarOffset < 0)
radarOffset -= PI/8; //(0.375)
else
radarOffset += PI/8;
}
//turn the radar
setTurnRadarLeftRadians(NormaliseBearing(radarOffset));
//左转调整转动角度到PI内
}
3.4确定开火方向
void doGun()
{
//计算子弹到达目标的时间长speed = 20 - 3 * power;有计算公式,距离除速度=时间
Longtime = getTime() + (int)(target.distance/(20-(3*firePower))); //offsets the gun by the angle to the next shot based on linear
targeting provided by the enemy class
//以直线为目标,偏移子弹下一次发射的角度。(这样让子弹射空的几率减少。但对付不动的和做圆运动的机器人有问题)
//target.guesssX(),target.guessY()为目标移动后的坐标 doublegunOffset=getGunHeadingRadians()-absbearing(getX(),getY(
),target.guessX(time),target.guessY(time));
setTurnGunLeftRadians(NormaliseBearing(gunOffset)); //调整相对角度到2PI内
}
3.4敌人类
由于我们在战斗时要不断获得扫描到的敌人的信息从而用一个敌人类来保存我们获取的信息。这将会送到我们的监听器不断处理
class Enemy
{
String name;
public double bearing;
public double head;
public long ctime; public double speed;
public double x,y;
public double distance;
public double guessX(long when)
{
//以扫描时和子弹到达的时间差 , 最大速度=距离, 再用对手的坐标加上移动坐标得到敌人移动后的坐标
long diff = when - ctime;
return x+Math.sin(head)*speed*diff; //目标移动后的坐标
}
public double guessY(long when)
{
long diff = when - ctime;
return y+Math.cos(head)*speed*diff;
}
}
3.5监听器
public void onScannedRobot(ScannedRobotEvent e)
{
if ((e.getDistance() < target.distance)||(target.name == e.getName()))
{
//求得对手的绝对弧度
double absbearing_rad =
(getHeadingRadians()+e.getBearingRadians())%(2*PI);
target.name = e.getName();
//求得对手的x,y坐标,见"robocode基本原理之坐标锁定"文章
target.x =
getX()+Math.sin(absbearing_rad)*e.getDistance(); //works out the x coordinate of where the target is
target.y =
getY()+Math.cos(absbearing_rad)*e.getDistance(); //works out the y coordinate of where the target is
target.bearing = e.getBearingRadians();
target.head = e.getHeadingRadians();
target.ctime = getTime(); //game time at which
this scan was produced 扫描到机器人的游戏时间
target.speed = e.getVelocity(); //得到敌人速度
target.distance = e.getDistance();
}
}
public void onRobotDeath(RobotDeathEvent e)
{
if (e.getName() == target.name)
target.distance = 10000;
}
}
4.导入坦克程序
当我们编好机器人代码之后直接运行将出现下面界面:
导入到我们原来就有的Robcode.jar包,包中包含了所有的曾经编程过得机器人
运行我们的战斗平台startRobcoe.bat
平台如下图:
通过菜单栏中的“选项”->“系统设置”->“开发者”然后找到我们源程序所在包的bin的路径之后我们的机器人就可以加入对战了~~
选择菜单栏“战斗”然后加入战斗如下:
开始战斗:
战斗结果:Ben是我们的机器人
5.源代码
注明:在我们设计代码之前必须导入我们的Robcode.jar负责我们的
Robocode类将无法使用
package wind;
import robocode.*;
import java.awt.Color;
public class Ben extends AdvancedRobot
{
Enemy target; //our current enemy 代表对手,包括了
对手的所有有用参数
final double PI = Math.PI; //just a constant
int direction = 1; //direction we are heading...1 = forward, -1 = backwards
//我们坦克车头的方向
double firePower; //the power of the shot we will be using
- set by do firePower() 设置我们的火力
public void run()
{
target = new Enemy(); //实例化Enemy()类
target.distance = 100000; //initialise the distance so that we can select a target
setColors(Color.red,Color.blue,Color.green); //sets the colours of the robot
//the next two lines mean that the turns of the robot, gun and radar
are independant
//让gun,radar独立于坦克车
setAdjustGunForRobotTurn(true); //当坦克转动时让炮台保持原
来的方向
setAdjustRadarForGunTurn(true);
//当坦克转动时让雷达保持原来的方向
turnRadarRightRadians(2*PI); //turns the radar right
around to get a view of the field 以弧度计算旋转一周
while(true)
{
doMovement(); //Move the bot 移动机器人
doFirePower(); //select the fire power to use 选择火力
doScanner(); //Oscillate the scanner over the bot
扫描
doGun(); //move the gun to predict where the
enemy will be 预测敌人,调整炮管
out.println(target.distance);
fire(firePower); //所有动作完成后,开火
execute();//execute all commands 上面使用的都为
AdvancedRobot类中的非阻塞调用
//控制权在我们,所有这里用阻塞方法返回控制给机器人
}
}
void doFirePower()
{
firePower = 400/target.distance;//selects a bullet power based on
our distance away from the target //根据敌人距离来选择火力,因为本身前进,后退为300,所以火力不会过大
}
//以目标主中心来回摆动
void doMovement()
{
if (getTime()%50 == 0) //?过20的倍数时间就反转方向
{
//every twenty 'ticks'
direction *= -1; //reverse direction
setAhead(direction*300); //move in that direction
}
setTurnRightRadians(target.bearing + (PI/2)); //every turn move
to circlestrafethe enemy
//每一时间周期以敌人为中心绕圆运动
}
//information on our target 雷达锁定目标
void doScanner()
{
double radarOffset; //雷达偏移量
if (getTime() - target.ctime > 4) //???why来回扫了4个回
合都没扫到意味失去了目标,再全扫一遍
{
//if we haven't seen anybody for a bit....
radarOffset = 360; //rotate the radar to find a
target
}
else
{
//next is the amount we need to rotate the radar by to scan
where the target is now
//通过扫描决定雷达旋转的弧度,"见基本原理方向剖析及目标锁定
www.robochina.org".雷达弧度-敌人角度得到两者相差为旋转值
radarOffset = getRadarHeadingRadians() - absbearing(getX(),getY(),target.x,target.y);
//this adds or subtracts small amounts from the bearing for
the radar to produce the wobbling
//and make sure we don't lose the target
//在得到的角度中加或减一点角度,让雷达很小的范围内摆而不失
去目标
if (radarOffset < 0)
radarOffset -= PI/8; //(0.375)
else
radarOffset += PI/8;
}
//turn the radar
setTurnRadarLeftRadians(NormaliseBearing(radarOffset)); //左转
调整转动角度到PI内
}
/*
* This simple method moves the gun to the bearing that we predict the
* enemy will be by the time our bullet will get there.
* the 'absbearing' method can be found in the helper functions section
* the nextX and nextY method can be found in the 'Enemy' class description
*/
void doGun()
{
//works out how long it would take a bullet to travel to where the enemy is *now*
//this is the best estimation we have
//计算子弹到达目标的时间长speed = 20 - 3 * power;有计算公式,距离除速
度=时间
long time = getTime() +
(int)(target.distance/(20-(3*firePower)));
//offsets the gun by the angle to the next shot based on linear targeting provided by the enemy class
//以直线为目标,偏移子弹下一次发射的角度。(这样让子弹射空的几率减少。
但对付不动的和做圆运动的机器人有问题)
//target.guesssX(),target.guessY()为目标移动后的坐标
double gunOffset = getGunHeadingRadians() -
absbearing(getX(),getY(),target.guessX(time),target.guessY(time));
setTurnGunLeftRadians(NormaliseBearing(gunOffset)); //调整相
对角度到2PI内
}
/*
* This set of helper methods. You may find several of these very useful
* They include the ability to find the angle to a point.
*/
//if a bearing is not within the -pi to pi range, alters it to provide the shortest angle
double NormaliseBearing(double ang)
{
if (ang > PI)
ang -= 2*PI;
if (ang < -PI)
ang += 2*PI;
return ang;
}
//if a heading is not within the 0 to 2pi range, alters it to provide the
shortest angle
double NormaliseHeading(double ang)
{
if (ang > 2*PI)
ang -= 2*PI;
if (ang < 0)
ang += 2*PI;
return ang;
}
//returns the distance between two x,y coordinates '**'
//以两边长求得与对手之间的距离
public double getrange( double x1,double y1, double x2,double y2 )
{
double xo = x2-x1;
double yo = y2-y1;
double h = Math.sqrt( xo*xo + yo*yo );
return h;
}
//gets the absolute bearing between to x,y coordinates
//根据x,y的坐标求出绝对角度,见"坐标锁定"利用直角坐标系来反求出角度。,,,
public double absbearing( double x1,double y1, double x2,double y2 )
{
double xo = x2-x1;
double yo = y2-y1;
double h = getrange( x1,y1, x2,y2 );
if( xo > 0 && yo > 0 )
{
//反正弦定义,对边除斜边得弧度.以robocode中的绝对方向系及坐标系参照 //x,y为正右上角为0-90,x正y负右下角为90-180,x,y负左下角180-270,x负,y正右上角270-360
//此处要理解robocode中的绝对角度是上为0,下为180,如以中心为点划分象
限则得到下面的结果
return Math.asin( xo / h );
}
if( xo > 0 && yo < 0 )
{
return Math.PI - Math.asin( xo / h ); //x为正,y为负第二象
限角
}
if( xo < 0 && yo < 0 )
{
return Math.PI + Math.asin( -xo / h ); //第三象限内180+角度
}
if( xo < 0 && yo > 0 )
{
return 2.0*Math.PI - Math.asin( -xo / h ); //四象限360-角度
}
return 0;
}
/**
* onScannedRobot: What to do when you see another robot
* 扫描事件,也是初始化目标数据的过程
*/
public void onScannedRobot(ScannedRobotEvent e)
{
//if we have found a closer robot....
if ((e.getDistance() < target.distance)||(target.name == e.getName()))
{
//the next line gets the absolute bearing to the point where
the bot is
//求得对手的绝对弧度
double absbearing_rad =
(getHeadingRadians()+e.getBearingRadians())%(2*PI);
//this section sets all the information about our target
target.name = e.getName();
//求得对手的x,y坐标,见"robocode基本原理之坐标锁定"文章
target.x = getX()+Math.sin(absbearing_rad)*e.getDistance();
//works out the x coordinate of where the target is
target.y = getY()+Math.cos(absbearing_rad)*e.getDistance(); //works out the y coordinate of where the target is
target.bearing = e.getBearingRadians();
target.head = e.getHeadingRadians();
target.ctime = getTime(); //game time at which this scan was produced 扫描到机器人的游戏时间
target.speed = e.getVelocity(); //得到敌人速度
target.distance = e.getDistance();
}
}
public void onRobotDeath(RobotDeathEvent e)
{
if (e.getName() == target.name)
target.distance = 1000000; //this will effectively make it search for a new target
}
}
/*
* 保存我们扫描到的目标的所有有用数据,也可用hashtable,vector方法处理
所有和我们有关的目标数据(用于群战)
* 中间的guessX,guessY方法是针对做直线均速运动机器人一个策略
*/
class Enemy
{
/*
* ok, we should really be using accessors and mutators here,
* (i.e getName() and setName()) but life's too short.
*/
String name;
public double bearing;
public double head;
public long ctime; //game time that the scan was produced public double speed;
public double x,y;
public double distance;
public double guessX(long when)
{
//以扫描时和子弹到达的时间差 , 最大速度=距离, 再用对手的坐标加上移动坐标得到敌人移动后的坐标
long diff = when - ctime;
return x+Math.sin(head)*speed*diff; //目标移动后的坐标
}
public double guessY(long when)
{
long diff = when - ctime;
return y+Math.cos(head)*speed*diff;
}
}