软件工程设计课程设计报告
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
2013-2014学年第二学期
《软件工程设计》
课程设计报告
题目:
自主模式匹配攻击双坦克运动规划程序设计专业:计算机科学与技术
班级:10级(2)班
:项莹莹
学号:1008210138
指导教师:王源
成绩:
计算机与信息工程系
2014年5月24日
目录
1.绪论 (3)
2. 开发环境介绍 (3)
3.课程设计的目的与要求 (4)
3.1 课程设计目的 (4)
3.2 课程设计的实验环境 (4)
3.3 课程设计的预备知识 (4)
3.4 课程设计主要容 (4)
4.系统分析 (5)
4.1可行性分析 (5)
4.1.1技术可行性 (5)
4.1.2经济可行性 (5)
4.2需求分析 (5)
5.课程设计容 (6)
5.1 程序中的主要方法、变量和函数 (6)
5.2程序具体设计 (7)
5.3游戏主窗口 (11)
5.3.1游戏数据的输入以及运行 (12)
5.3.2游戏数据的输出以及保存 (15)
6.设计体会与小结 (15)
7.参考文献 (16)
8.源程序 (16)
1.绪论
Java是一种简单的,跨平台的,面向对象的,分布式的,解释的,健壮的安全的,结构的中立的,可移植的,性能很优异的多线程的,动态的语言。
当1995年SUN推出Java语言之后,全世界的目光都被这个神奇的语言所吸引。
Java是一个纯的面向对象的程序设计语言,它继承了 C++ 语言面向对象技术的核心,Java舍弃了 C ++语言中容易引起错误的指针(以引用取代)、运算符重载(operator overloading)、多重继承(以接口取代)等特性,增加了垃圾回收器功能用于回收不再被引用的对象所占据的存空间,使得程序员不用再为存管理而担忧。
Java 不同于一般的编译执行计算机语言和解释执行计算机语言。
它首先将源代码编译成二进制字节码(bytecode),然后依赖各种不同平台上的虚拟机来解释执行字节码,从而实现了“一次编译、到处执行”的跨平台特性。
目前,Java语言不仅是一门被广泛使用的编程语言,而且已成为软件设计开发者应当掌握的一门基础语言。
因为很多新的技术领域都涉及到Java语言,国外许多大学已将Java语言列入本科教学计划,而IT行业对Java人才的需求也在不断的增长,因此,掌握Java已经成为共识。
2. 开发环境介绍
Robocode 是2001年7月在美国IBM 的Web 站点alphaWorks 上公开的机器人(其图形为坦克的形状) 战斗仿真引擎。
与通常玩的游戏不同的是:你必须利用Java 对机器人动作进行编程,给机器人设计的智能来自动指挥它, 而不是由键盘鼠标来控制它。
不管你是初学者还是顶级程序员,你都可在Robocode 的世界中找到旗鼓相当的对手。
把你写的机器人放到战场中,在与别人编写的机器人的战斗中考验自己的编程水平。
在不断的完善过程中你将体会到学习所带来的无穷乐趣。
当你的机器人融入了你的思想你将会发现你已经打开了Java编程甚至任何编程语言的大门。
由于Robocode 是基于Java 而产生的软件,所以它也具备了Java“一处编写,到处运行”的特点。
Robocode 这个游戏为全世界的Java 开发者实现这个愿望,它把游戏风潮变成了教学工具,Robocode的小东西有着巨大的能量,Robocode 是一个比较上瘾的游戏,人们对它的上瘾程度令人吃惊。
战场是机器人之间进行战斗直至分出胜负的场地。
主要的仿真引擎被置于其中,并且允
许您在这里创建战斗、保存战斗以及打开新建的或现有的战斗。
通过界面区域的控件,您可以暂停或继续战斗、终止战斗、消灭任何机器人个体或获取任何机器人的统计数据。
图2中三个机器人活动的地方就是战场。
此外,在编译器界面下您可以通过菜单调用Robot Editor,它是Robocode本身自带的Java 语言编辑器,这个编辑器是发挥你创意的所在。
它可以用于编辑生成机器人的 Java 源文件。
3.课程设计的目的与要求
3.1 课程设计目的
《JAVA面向对象程序设计》是计算机科学与技术专业的必修专业基础课程,其实践性、应用性很强。
实践教学环节是必不可少的一个重要环节。
本课程的程序设计专题实际是计算机相关专业学生学习完《JAVA面向对象程序设计》课程后,进行的一次全面的综合训练,JAVA 程序设计的设计目的是加深对理论教学容的理解和掌握,使学生综合运用所学知识,利用软件工程为基础进行软件开发、并在实践应用方面打下一定基础。
3.2 课程设计的实验环境
JAVA程序设计语言及相应的集成开发环境,J2SDK和Robocode开发工具。
3.3 课程设计的预备知识
熟悉JAVA语言以及Robocode软件。
3.4 课程设计主要容
坦克大战游戏是在Robocode环境下编程的游戏,其中包括对坦克移动策略的编写、坦克射击策略的编写、坦克模式匹配策略的编写、构造函数的编写等等。
游戏主要实现的功能有:
(1).坦克可以随机移动;
(2).坦克可以根据模式匹配策略射击敌方坦克;
(3).坦克可以按照一定的策略的发子弹;
(4).我方坦克打败敌方坦克时,会显示爆炸效果并消失;
(5).我方坦克被击中后,会显示爆炸效果;
4.系统分析
4.1可行性分析
4.1.1技术可行性
Robocode软件使用面向对象设计语言JAVA技术实现,这已是非常成熟的技术,之前已有相当多使用这些技术的成功案例,故现使用这些技术是可行的。
Robocode软件为你处理好一切细节。
你所做就是为你的机器人坦克编写智能程序,让它能够移动、进攻、防御、躲避、开火。
而它的对手就是跟你一样其他程序员编写机器人程序。
这就是Robocode魅力所在,最简单,只用几十行代码,就能立刻创造出一个简单但完整机器人,你可以立即将它装入Robocode 引擎中,再从Robocode 自带的那些水平不一的示例机器人中选取一个进行一番对战。
你可以不停的修改你的程序,设计新射击模型、躲避模型、移动模型,当你打败了那些示例机器人,你还可以在网上下载由其他程序员编写的水平更高的机器人,与它们比试一下,看看自己的水平到底如何。
开发Robocode,也是一个极佳的学习Java 语言的过程。
对于初学者来说,这是学习各种基本语法好途径:类,方法,事件处理等等。
当你入门之后,需要进一步提高机器人的“智力”水平,就需要使用更好的策略:如何预测对手的行动路线,如何躲避对手的攻击......你不得不创建更多类来应对更多情况,你将学习使用接口、继承等等Java 高级语言特性。
随着你的机器人的“智力”水平的提高,你的编程能力也就跟着水涨船高了。
4.1.2经济可行性
本软件为免费软件,将免费提供软件的下载、运行和维护服务,软件暂无收入,待公布之后随着使用者越来越多,将添加相应的广告、服务,从而增加收入,若使用者寥寥无几,将再次分析是否重新策划软件或者放弃开发,以减少损失。
硬件要求也比较低,系统安装最小环境要求:CPU:Pentium2/400MHz 以上存:64MB 以上硬盘:10M 以上
4.2需求分析
利用Robocode软件的编译器,将所有游戏的元素都在此界面上表现出来。
界面中包含我方坦克、敌人坦克、双方坦克的生命值、比赛的速度、比赛的回合等。
坦克:坦克分为两种:我方坦克和敌方坦克。
我方和敌方坦克均可以发射子弹,可以改
变路径的行走,且在行走过程中遇到墙等阻碍物和游戏边界时要改变方向,而不能一直顶着障碍物不放,这些都可以在程序中设计。
坦克之间不能穿越,碰撞到后自动调换方向。
墙:游戏中的边界王家的坦克和敌人的坦克都不能越过,坦克的子弹也不能穿过。
我方的坦克要满足离墙不能太近,而且如果要过去这一点,我需要的用方向要满足不能和当前正在运动的方向相差太少,意思就是说,方向要变多些,不能看起来跟没改变差不多,也不能向着敌人冲过去。
运动方式:随机运动,这种运动方式主要用来混乱敌人的预测。
爆炸:当子弹射击到对方坦克身上时,要产生爆炸效果。
子弹:子弹可以由敌方和用户方发射,且发射出去的子弹可以根据雷达觉察到敌人时有所动作。
机器人保持与敌人成30度倾向角。
自身成 90 度角静止并逐渐接近目标。
如果机器人觉察到能量下降介于 0.1 和 3.0 之间(火力围),那么机器人就立即切换方向,向左或向右移动。
直到碰到障碍物就消失。
敌方坦克受到子弹多次攻击后会爆炸从而导致死亡,死亡后坦克消失。
我方受到子弹攻击后会减少寿命,此时如还有敌方坦克存在,则我方输掉本次游戏。
战场:战场是机器人之间进行战斗直至分出胜负的场地。
主要的仿真引擎被置于其中,并且允许您在这里创建战斗、保存战斗以及打开新建的或现有的战斗。
通过界面区域的控件,您可以暂停或继续战斗、终止战斗、消灭任何机器人个体或获取任何机器人的统计数据。
getBattleFieldWidth()和getBattleFieldHeight()可以得到当前这一回合的战场尺寸。
射击策略:射击策略主要思路是用一个800长度的数组来纪录一个直线提前量每个记录点都记录从自己子弹到敌人这段时间(子弹假定能量为3,敌人假定为不动,虽然这样不精确,但是也只能这样,你知道敌人怎样动?如果知道你还计算啥?)。
需要射击时,寻找一个历史中(所有以前的记录节点中)完成了累加的,而且与目前的预测角度值最相似的记录节点(也就是以预测角度值最相似就视为运动方式最相似),那么,敌人以后的运动因该和历史中的那段运动情况很相似(也就是历史重现)。
找到了历史要重现的地方了,那么用那段历史中的平均预测角度值作为射击角度射击,这种射击方式就是寻找历史中和你现在运动相似的情况,所以如果我方坦克的运动有规律的话,敌方坦克能很好的找出我方坦克下一步会怎样走,那么,我方坦克被命中的几率就很大,这也正是采用随机运动的原因。
5.课程设计容
5.1 程序中的主要方法、变量和函数
5.2程序具体设计
因为Robocode软件自带图形用户界面的游戏窗口,所以就不需要另外设计游戏窗口
了。
游戏的运行也是自动完成的,只需要在游戏开始前设置下游戏的速度即可,也可以
在游戏运行时通过窗口下方的速度标尺来自行调整。
Robocode平台的坐标系统和我们数学书中的坐标系统有较大区别,首先要搞清楚几个概念:
1、【坐标系】:Robocode整个坐标系都是战场屏幕以左下角为原点。
getX()和getY()
可以捕捉到机器人当前所在战场中的坐标。
2、【绝对方向系】:Robocode中不管机器人在哪个方向都是以静态战场屏幕为参照物的绝对
角度(Heading),正上方为0 度角。
即是向北为0,向东为90,向南为180,向西为270。
3、【相对方向系】:相对方向是以机器人的动态heading角度为参照物的角度差,不再以整
个静态屏幕为参照了。
叫它相对是因为机器人的heading 是随着机器人移动而不停的在改变,heading 只是个相对物体。
4、【Heading】:是机器人方向与屏幕正上方的角度差,方向在0 到360 之间。
getHeading()、getGunHeading()和getRadarHeading()分别可以得出坦克车、炮或
雷达当前的方向,该方向是以角度表示的。
5、【Bearing】:是机器人的某个部件如雷达发现的目标与方向的角度差,顺时针为正
角度在- 180 到180 之间。
大家都知道,坦克有很多种,但是每一种坦克都离不开三个部分,车身,炮,雷达。
Robocode中的机器人也有这三个部件。
车身是一个比较笨重的部分,它的基本操作有:turnLeft(double degree)和
turnRight(double degree)分别是使机器人(左右)转过一个指定的角度。
在AdvancedRobot 坦克中可以使用setTurnRightRadians(double degree)函数使机器人转
过一定的弧度。
setAhead(double distance)和setBack(double distance)分别是
使机器人(前后)移动指定的像素点距离;这两个方法在机器人碰到墙或碰到另外一个
机器人时即告完成。
本设计中的设置车身的参数如下:
double ourX = getX();
double ourY = getY();
double testX = Math.random() * getBattleFieldWidth();
double testY = Math.random() * getBattleFieldHeight();
setTurnRightRadians( angle_180( turnAngle ) );
setAhead( moveDirection * Point2D.Double.distance( ourX , ourY , testX , testY ) );
炮是用来发射炮弹攻击敌人的武器,它的基本操作有:TurnGunLeft(double degree)
和TurnGunRight(double degree)分别是使炮可以独立于坦克车的方向(左右)转动指
定的角度。
在AdvancedRobot 坦克中,通过设置setTurnGunRightRadians(double degree)
函数来让炮按一定的弧度转动。
利用setFire(double power)发射指定能量的炮弹。
本设计中的设置炮的参数如下:
setTurnGunRightRadians( angle_180( targetBearing - getGunHeadingRadians() + linearPredictionAngle ) );
double firePower = Math.min( 3 , 30 * getEnergy() / e.getDistance() );
if ( getEnergy() > Math.min( e.getEnergy() + firePower + 0.1 , 3.1 ) ) { setFire( firePower );
雷达是机器人取得敌人信息的主要器官,它的基本操作有:turnRadarLeft(double degree)和turnRadarRight(double degree)分别是使炮上面的雷达(左右)转动指定的角度,转动的方向也独立于炮的方向(以及坦克车的方向)。
在AdvancedRobot 坦克中,通过设置setTurnRadarRightRadians(double degree)函数来让雷达按一定的弧度转动。
本设计中的设置雷达的参数如下:
setTurnRadarRightRadians( Math.tan( targetBearing - getRadarHeadingRadians() ) * 3 );
本设计中最重要的两个策略是模式匹配策略和坦克的移动策略。
坦克的模式匹配策略的代码如下所示:
int match = 0;
pattern[ time ][0] = e.getDistance() / ( 20 - 3 * firePower );
pattern[ time ][1] = e.getVelocity() * Math.sin( e.getHeadingRadians() - targetBearing );
//也就是说pattern[ time ][1]记录的是直线提前量的射击角度与自己子弹速度的乘积
pattern[ time ][2] = pattern[ time ][3] = 0;
for ( int a = 0; a < storedInfo; a++ ) {
//每个记录点pattern[a]中,如果当前时间是在我的子弹到达敌人之前则累加计算到的myBulletVelocity*Math.sin(predictionAngle)值;
//同时记录累加了多少次(pattern[ a ][3]的值)
if ( pattern[ a ][0] > 0 ) {
pattern[ a ][2] += e.getVelocity() * Math.sin( e.getHeadingRadians() -targetBearing );
pattern[ a ][0]--;
pattern[ a ][3]++;
}
}
//在历史中(所有记录中)寻找一个和目前
myBulletVelocity*Math.sin(predictionAngle)值最相近的记录点(这个纪录点必须是累加完毕的点pattern[a][0]<0)
//把这个纪录点的数组下表记录到match中
for ( int a = 0; a < storedInfo; a++ ) {
double testAngle = pattern[ time ][1];
if ( pattern[ a ][0] < 0 && Math.abs( testAngle - pattern[ a ][1] ) < Math.abs( testAngle - pattern[ match ][1] ) )
{ match = a; }
}
坦克的移动策略的代码如下所示:
if ( nextMove-- <= 0 ) {
for ( double a = 0 ; a < 500 ; a++ ) {
double testX = Math.random() * getBattleFieldWidth();
double testY = Math.random() * getBattleFieldHeight();
double distToWall = Math.min( testX , getBattleFieldWidth() - testX )* Math.min( testY , getBattleFieldHeight() - testY );
if ( distToWall > 7600 ) {
double ourAngleToPoint = getAngle( ourX , ourY , testX , testY );
double minimumAngleDifference = ( ( 500 - a ) / 500 ) * Math.PI / 6; if ( Math.abs( angle_180( targetBearing ) - ourAngleToPoint ) > 2 * minimumAngleDifference&& Math.abs( angle_180( getHeadingRadians() ) ourAngleToPoint ) > minimumAngleDifference ) {
double turnAngle = angle_180( getAngle( ourX , ourY , testX , testY )
-getHeadingRadians() );
double moveDirection = 1;
if ( Math.abs( turnAngle ) > Math.PI / 2 ) {
moveDirection = - 1;
turnAngle += Math.acos( moveDirection ); }
setTurnRightRadians( angle_180( turnAngle ) );
setAhead( moveDirection * Point2D.Double.distance( ourX , ourY , testX , testY ) );
break;
}
}
}
nextMove = e.getDistance() / ( 29 - 3 * firePower );
}
setMaxVelocity( getTurnRemaining() > 45 ? 0.001 : 8 );
5.3游戏主窗口
游戏主窗口是人机交互的窗口,对玩家而言,他们只和游戏主窗口打交道,并不关心游戏部是如何实现的,所以游戏主窗口部分最重要的便是游戏数据的输入、输出。
Robocode的游戏主窗口如图所示:
5.3.1游戏数据的输入以及运行
第一步:双击桌面上的Robocode图标,运行Robocode。
第二步:在Robocode窗口中,选择“Robot”菜单中的额“Editor”选项,打开Robot Editor窗口,然后执行“File”→“New”→“Java File”选项,在弹出的窗口中编写Java文件。
如下图所示:
第三步:Java文件编写完成后执行“Compiler”→“Compile”选项,或者按组合键Ctrl+B执行编译命令。
如果没有出项错误或警告,将会显示Compiled successfully.
第四步:关闭“Compiled successfully.”窗口,打开Robocode窗口,然后执行“Battle”→“New”命令,弹出“New Battle”窗口,在“Battle”选项卡中选择自己创建的机器人以及系统自带的两个机器人,最后单击“Start Battle”按钮开始比赛,界面如下图所示:
第五步:单击“Start Battle”按钮后将进入比赛场地,其中红色的是我方坦克,如下图所示:
5.3.2游戏数据的输出以及保存
比赛结束后,会弹出一个表格样式的窗口(图4),这就是记分牌,也即你设置的N 个回合比赛结束后的成绩单。
它可是你分析数据及参加联赛的重要依据。
此时可以单击“Save”按钮保存比赛结果,如下图:
6.设计体会与小结
Robocode魅力所在是只要用几十行的Java代码,就能立刻创造出一个简单但完整机器人,还可以立即将它装入Robocode 引擎中,再从Robocode 自带的那些水平不一的示例机器人中选取一个进行一番对战。
你可以不停的修改你的程序,设计新射击模型、躲避模型、移动模型,当你打败了那些示例机器人,你还可以在网上下载由其他程序员编写的水平更高的机器人,与它们比试一下,看看自己的水平到底如何。
开发Robocode,也是一个极佳的学习Java 语言的过程。
对于初学者来说,这是学
习各种基本语法好途径:类,方法,事件处理等等。
当你入门之后,需要进一步提高机器人的“智力”水平,就需要使用更好的策略:如何预测对手的行动路线,如何躲避对手的攻击......你不得不创建更多类来应对更多情况,你将学习使用接口、继承等等Java 高级语言特性。
随着你的机器人的“智力”水平的提高,你的编程能力也就跟着水涨船高了。
7.参考文献
[1].Java程序设计与应用开发(第2版)於东军、静宇、千目、王国全著清华大学
[2].Java语言实用教程(第2版) 丁振凡著邮电大学
[3].Java语言学习利器超鸿、周小刚著中国水利水电
8.源程序
程序源代码:
package xyy;
import robocode.*;
import java.awt.Color;
import java.awt.geom.Point2D;
public class moshipipei extends AdvancedRobot
{ static double nextMove;
final static int storedInfo = 800;
static double[][] pattern = new double[storedInfo][4];
public void run() {
setBodyColor(Color.red);
setRadarColor(Color.yellow);
setGunColor(Color.blue);
setBulletColor(Color.yellow);
setAdjustRadarForGunTurn(true);
setAdjustGunForRobotTurn(true);
while( true ) {
turnRadarRightRadians( Double.POSITIVE_INFINITY );
}
}
public void onScannedRobot(ScannedRobotEvent e) {
int time = (int) getTime() % storedInfo;
double targetBearing = getHeadingRadians() + e.getBearingRadians();
double ourX = getX();
double ourY = getY();
double firePower = Math.min( 3 , 30 * getEnergy() / e.getDistance() );
//下面是移动策略
if ( nextMove-- <= 0 ) {
for ( double a = 0 ; a < 500 ; a++ ) {
double testX = Math.random() * getBattleFieldWidth();
double testY = Math.random() * getBattleFieldHeight();
double distToWall = Math.min( testX , getBattleFieldWidth() - testX ) * Math.min( testY , getBattleFieldHeight() - testY );
if ( distToWall > 7600 ) {
double ourAngleToPoint = getAngle( ourX , ourY , testX , testY );
double minimumAngleDifference = ( ( 500 - a ) / 500 ) * Math.PI / 6;
if(Math.abs(angle_180(targetBearing)-ourAngleToPoint)>2*minimumAngleDifference&&Math.ab
s(angle_180(getHeadingRadians())-ourAngleToPoint)>minimumAngleDifference ) { double turnAngle = angle_180( getAngle( ourX , ourY , testX , testY ) - getHeadingRadians() );
double moveDirection = 1;
if ( Math.abs( turnAngle ) > Math.PI / 2 ) {
moveDirection = - 1;
turnAngle += Math.acos( moveDirection );
}
setTurnRightRadians( angle_180( turnAngle ) );
setAhead( moveDirection * Point2D.Double.distance(ourX ,ourY , testX , testY ) ); break; } } }
nextMove = e.getDistance() / ( 29 - 3 * firePower ); }
setMaxVelocity( getTurnRemaining() > 45 ? 0.001 : 8 );
// 移动算法结束
// 以下是模式匹配算法
int match = 0;
pattern[time][0]=e.getDistance()/( 20 - 3 * firePower );
pattern[time][1]=e.getVelocity()*Math.sin(e.getHeadingRadians()
-targetBearing);
//也就是说pattern[ time ][1]记录的是直线提前量的射击角度与自己子弹速度的乘积
pattern[ time ][2] = pattern[ time ][3] = 0;
for ( int a = 0; a < storedInfo; a++ ) {
//每个记录点pattern[a]中,如果当前时间是在我的子弹到达敌人之前则累加计算到的myBulletVelocity*Math.sin(predictionAngle)值;
//同时记录累加了多少次(pattern[ a ][3]的值)
if ( pattern[a][0] > 0 ) { pattern[a][2]+=e.getVelocity()*Math.sin(e.getHeadingRadians()
-targetBearing );
pattern[ a ][0]--; pattern[ a ][3]++; }
}
//在历史中(所有记录中)寻找一个和目前的myBulletVelocity*Math.sin(predictionAngle)值最
相近的记录点(这个纪录点必须是累加完毕的点pattern[a][0]<0)
//把这个纪录点的数组下表记录到match中
for ( int a = 0; a < storedInfo; a++ ) {
double testAngle = pattern[ time ][1];
if ( pattern[ a ][0] < 0 && Math.abs( testAngle - pattern[ a ][1] ) < Math.abs( testAngle
- pattern[ match ][1] ) ) {
match = a; }
}
//模式匹配分析结束
Double linearPredictionAngle = pattern[ match ][2] / ( pattern[ match ][3] * ( 20 - 3 *
firePower ) );
linearPredictionAngle=linearPredictionAngle>= 0? linearPredictionAngle: 0 ;
setTurnGunRightRadians( angle_180( targetBearing - getGunHeadingRadians() + linearPredictionAngle ) );
setTurnRadarRightRadians( Math.tan( targetBearing - getRadarHeadingRadians() ) * 3 );
if ( getEnergy() > Math.min( e.getEnergy() + firePower + 0.1 , 3.1 )
{ setFire( firePower );
}
scan();
}
public double angle_180( double ang ) {
return Math.atan2( Math.sin( ang ), Math.cos( ang ) );
}
double getAngle( double x1 , double y1 , double x2 , double y2 )
{ return Math.atan2( x2 - x1 , y2 - y1 );
}
}。