征服程序世界—我的AI-CODE

原创 2006年05月22日 12:21:00

本文发表于程序员杂志2006年4月杂志,全面介绍了AI-TANK的应用和方法。
    至今还记得进入程序设计领域第一次在屏幕上用程序语言成功输出“Hello World”的喜悦。至今还记得无数日日夜夜枯燥的代码编写。却不记得一个小小的Bug伴随着自己渡过了多少的通宵。每每灯下感叹,这些代码能是游戏就好了。
    终于过了编程设计的第一关,能独立写一些程序了。却要面对外面世界无数语言的选择,因为这些选择很可能就决定了自己未来的工作,甚至幸福。然而铺天盖地的各方支持者都在自己的阵营摇旗呐喊。Java好,C好,C++好,C#......。于是每种语言都尝试了一下,几年也就过去了,仍然分不同谁好谁不好。现在终于明白扎实的基础、精通一门足已。但心中还是偶尔会拿这些编程语言出来比比,有时还和其他阵营的高手过几招,但环境不同怎么也说不上谁写的程序更好。最后相互还是不服。
    是否有一种软件能让大家在边学边玩?是否有一种软件能让各语言高手同台竞技?实现我们征服程序世界的梦想!

    AI-CODE就是在这种思想是设计出来的。它以竞技游戏和比赛、任务驱动、寓教于乐的方式的方式进行学习和思维训练。打破了传统教育的模式,利用游戏的这种互动性和操作性,让使用者在玩游戏、学编程中运用知识、提高能力。并且实现了Java、C、C++、C#......等各种编程语言同台竞技。你有可能在闪躲炮弹和执行精确攻击的演练中学会数组、函数、对象、类…这个游戏即将为全世界的开发者实现这个愿望,它把游戏风潮变成了教学工具。
    AI-CODE为使用者提供了一个虚拟机器人的制作环境和竞技环境。首先,使用者利用自己编程、数学、物理等相关学科知识建立一个虚拟机器人。这些知识通过图形化编写方式(机器人快车)或者纯代码编写方式(CodeCanvas)编写的机器人控制代码来体现。编写好的机器人控制代码经过编译以后,就可以在虚拟机器人运行平台(AIRobot)竞技环境下和其它的机器人拼死战斗到底。在这个过程中,为了取得胜利,您不断学习程序设计和机器人策略算法,通过学到的程序不断完善自己的机器人。在学习的同时您感到了快乐,可是在娱乐的同时您发现自己目前的知识存储不能给您更高的支持,所以为了得到更多的快乐,您又投入学习。AI-CODE就是这样一种系统,让你在玩,学习,玩,学习的循环过程中不断提高自己程序设计和策略算法设计的水平。AI-CODE 里有一些预先做好的机器人对手让你入门,但一旦您不再需要它们,就可以把您自己创建的机器人加入到正在世界范围内形成的某个联盟里去和世界最强手对阵。
AI-CODE以不同的外在表现形式与侧重点分为两大主题:侧重于教学、竞技适合中小学信息技术教育的AI-RCJ(智能足球); 侧重于程序、策略适合大中院校程序设计教育的AI-TANK(智能坦克)。下面请跟随 skyala.Li 以AI-TANK为例,着手建造属于自己的、定制的、小而精悍的战斗机器人。

下载并安装AI-TNAK
    您可以到网站
http://www.ai-code.org“下载中心”下载免费版安装版,如果您下载的是AI-TNAK 3.0免费版(带图形编辑器),直接点setup.exe就可安装到本地机器上。AI-CODE如果您下载的是不带图形编辑器,直接运行AI-TANK.exe。支持简体、繁体、英文三个版本,您可根据不同的地区选择不同的语言版本。

 

AI-TANK运行平台
    AI-TANK是集虚拟机器人运行平台、机器人程序图形编辑平台、机器人程序代码编辑平台于一体的软件系统。


虚拟机器人运行平台----AIRobot
    AIRobot是AI-TANK的核心平台,只有通过它您的代码才能以虚拟机器人的形象表示出来。您可在此平台下完成选择机器人、设置比赛环境等各种操作,并可在此平台下参看比赛过程和比赛结果。
 

虚拟机器人编辑平台----机器人快车 (图形化编程平台),CodeCanvas
    此平台主要用于编写机器人控制程序。其中机器人快车通过图形化的编程方式,使用户可以方便,快速的建立起自己的机器人,给入门用户提供了极大的控制方便。而CodeCanvas是程序代码编辑器,用户通过手工编写代码来生成机器人程序,给中高级用户提供了更大的灵活性。
 
CodeCanvas
 
机器人快车

快速体验
    在开始编写程序之前,我们来体验一场机器人比赛。真正感受AI-CODE给我们带来的无穷魅力,也借此体验了解虚拟动作平台-AIRobot下的一些操作。
    运行AI-TANK,选择“比赛”->“新建”,弹出“新建比赛”对话框,在“机器人选择”页上,先选择包“Javabook”,然后选择机器人“Walls”,然后按“添加”按钮,这样机器人“Walls”就被选择为战斗机器人了。用同样的方法添加机器人“PatternFire”。点击“完成”按钮运行比赛,见下图。注:AIRobot是以Java为开发基础,所以用到了包的概念,即机器人存入的目录。
 
    这样机器人就会按默认的规则比赛。好好欣赏你这一小段时间的操作果实吧,是不是手马上痒了,想自己写个机器人试一试了,别急,我们将在后续的章节中陆续讲解机器人的编写和所需的相关知识。

AI-TANK 各种参数
机器人结构
    机器人有两个部分组成:机身和炮管。这里要注意的是这两部分的控制是独立的,且它们的转动结果不会相互影响。举个例子:假设当前炮管的方向为0,机身的方向也为0。当我们用机身控制模块使机身的方向变为10时,炮管此时的方向仍为0,并不会随着机身的转动而转动。机器人本身就是有大小的,并不是一点。无论机器人外观图片怎样,在碰撞、中弹监测中机器人都视为直径为20系统单位长度的圆。在机器人运算中都是以中心点(圆心)来运算的。

坐标系统
    AI-TANK的坐标系统是一个标准的笛卡尔坐标,战场地图的左下角坐标为(0,0),右下角为(地图宽,0),左上角为(0,地图高),右上角为(地图宽,地图高),场地中水平向右为0度方向,水平向上为90度方向等。如下图:


 
系统时钟
    在现实生活中有一个时钟的概念,同样,在AI-TANK中也有一个时钟的概念,称为系统时钟。在AI-TANK中,系统单位时间指系统战斗中计时的度量单位,其意义为虚拟系统中的最小单位时间,一个时钟周期可以看成为现实世界中的一秒也完全可以。例如设定机器人转动速度为10度/系统单位时间,那么在AIRobot中,每个系统单位时间,机器人车身方向会转10度。
机器人可以通过getTime函数得到当前的时间,系统根据这个时钟调度来控制机器人运行。在每一个时钟周期,系统会根据当时的情况有选择的调用机器人的Action 处理函数,让机器人执行各种操作。我们可以在Action处理函数中加入控制机器人的各种语句,指挥机器人做各种动作(如前进,转动),在Action处理函数结束后,系统就会根据机器人所执行的动作更新机器人的状态:如方向、坐标。

其他系统设定
    移动速度:前进和后退的最大速度为8。加速时的加速度为1,减速时的加速度为-4。机器人撞到墙或与别的机器人相撞时速度根据碰撞的角度和碰撞时机器人的速度进行动量计算而得,碰撞不会损失能量。
    转动速度:也不考虑加速度,设定转速为多少立即生效,转动速度最大为10。正数代表逆时针旋转,负数代表顺时针旋转。 
    子弹能量:子弹的最大能量为3。子弹速度=20-3*子弹能量。能量为power的子弹击中敌人的话自己的能量加4*power, 被击中的机器人的能量将减6*power。每发射一发炮弹之后,要隔一段时间之后才能发射出另外一发炮弹,这段时间称为炮管准备时间,可以通过getFirePrepareTime函数获取当前需要的准备时间,当准备时间小于等于0的时候,发射炮弹才会成功。炮管准备时间跟上次发射的炮弹能量有关,能量越大,准备时间越多,目前版本的具体公式为:准备时间=10 + (炮弹能量/0.6)。 
    比赛得分:每轮比赛中,每死亡一个机器人,当时存活的机器人都各得一分。团队得分为团队成员的平均分。通常一场比赛都不只一轮对战,最后的胜负视机器人的总分而定,也就是看比赛结束时弹出的得分表。  

常用类与函数
    AI-TNAK 机器人的命令集都收录在AI-TANK API Javadoc 中。您将会发现这些命令都是 org.robochina.airobot.tank 类和org.robochina.math的公共函数。在这一部分,我们将分类讨论常用函数。
移动机器人
    让我们从移动机器人的基本命令开始:
    move(double velocity)设置车身的移动速度,用于控制车身的移动;turn(double velocity)设置车身的转动速度,用于控制车身的转动;moveTo(java.awt.geom.Point2D destination)向指定的目标点移动。根据当前的车身方向,会自动选择前进或后退,注意,这个函数会同时控制车身的转动和移动,不能和其他控制车身的函数(如turnTo)混用;turnTo(double heading)转动车身到指定的方向上。

获取关于机器人的信息
    有许多方法可以得到关于机器人的信息。下面简单列举了常用的方法调用:
getSelfBot()得到自己机器人的信息;getName()得到自己的名字;getEnergy()得到自己的能量;getHeading()得到自己的方向;getX()得到自己的X坐标;getY()得到自己的Y坐标。

获取其他有用的信息
    getFirstOpponent()得到第一个活着的对手的信息;getLocation()得到自己的坐标;getCourt()得到场地的信息;getCourtHeight()得到场地的高;getCourtWidth()得到场地的宽。

射击命令
    一旦您掌握了移动机器人以及相关的武器装备的方法,您就该考虑射击和控制损害的任务了。每个机器人在开始时都有一个缺省的“能量级别”,当它的能量级别减小到零的时候,我们就认为这个机器人已经被消灭了。射击的时候,机器人最多可以用掉三个能量单位。提供给炮弹的能量越多,对目标机器人所造成的损害也就越大。fire(double heading, double power)向指定的方向开火。

Action处理函数与onTick函数
    如果打开代码编辑器,我们会看到系统内置定义了一系列以on开头的函数,这些函数叫作Action处理函数。这些函数在特定的时刻由系统调用: onRoundBegin (RoundBeginAction action)函数在一轮比赛开始的时候由系统调用;onHitWall (HitRobotAction action)函数在机器人撞倒场地边缘的时候由系统调用;onHitRobot(HitRobotAction action) 当撞到其它机器人时触发; onHitedByBullet(HitedByBulletAction action) 当被别人的子弹击中时触发。
    系统的运行和状态的变化都是离散的,拿机器人的方向来说,如果机器人的转动速度是10度/单位时间,现在的方向是10度,那么在下一个单位时间机器人的方向才会发生变化,变成20度,之所以这是离散的是因为方向没有连续的变化,而是直接从10度变到了20度。
    你会发现每个机器人最开始都有一个onTick (TickAction action) 函数,onTick函数是一个比较特殊的Action处理函数,这个函数在每个时钟周期都会由系统调用,而其他的Action处理函数并不是每个时钟周期都会由系统调用的,拿onHitWall来说,只有当机器人撞倒场地边缘的时候才会由系统调用。在一个时钟周期当中,可能会有多个Action处理函数被系统调用,在普通情况下,只有一个onTick函数会被系统调用。当有多个Action处理函数被系统调用的时候,onTick总是最后一个被调用的。由于onTick函数的这些特点,在一般的情况下,都是把控制机器人的各种语句(如move)放在onTick函数中,其他的Action处理函数只是起到辅助的作用。
    我们只需要知道这些就可以创建一些相当复杂的机器人了。您可以通过战场的帮助菜单或 CodeCanvans的帮助菜单访问Javadoc中其余的 AI-TANK API。


机器人配置文件
    每一个机器人都拥有一个配置文件,这个配置文件以机器人名开头“.participant.xml”结尾。这个配置文件记录了和这个机器人相关的一系列信息(机器人名,包名,作者等),在系统启动的时候,就是通过这个配置文件中的信息来装载机器人的。
这是一个C++例子机器人samples.cpp.Circle的配置文件内容:
(robots/samples.cpp/Circle.participant.xml):
<map>
  ......
  <entry>
    <string>package</string>  <!-- 机器人所在的包 -->
    <string>samples.cpp</string>
  </entry>
  <entry>
    <string>name</string>   <!-- 机器人名 -->
    <string>Circle</string>
  </entry>
  <entry>
    <string>startupCommand</string>   <!-- 机器人的启动命令 -->
    <string>${WORKING_DIR}/Circle.exe ${SERVER_IP} ${SERVER_PORT}</string>
  </entry>
  ......
</map>
    这个配置文件是一个XML文件,在根元素map下面定义了一系列的entry元素,每一个entry元素都描述了机器人某一方面的属性,entry的第一个元素是该属性的名字,第二个元素是该属性的值。在上面的例子中,名为package和name的属性定义了机器人所在的包samples.cpp和机器人名Circle,在新建比赛的时候,我们就是通过这两个信息来选择机器人的。
 
    我们通过菜单“工具”-〉“机器人管理”来编辑机器人属性的时候,实际上就是在操作这个配置文件中的内容,当然我们也可以根据需要手工修改这个配置文件。

机器人内部运行机制
框架

    在AI-TANK中,机器人程序作为一个单独的程序运行,机器人程序与AIRobot运行平台之间通过socket建立连接,数据以XML的形式在AIRobot运行平台和机器人程序之间传输。从理论上说,只要能够建立socket连接,解析XML数据的编程语言,就可以用来编写机器人程序。
    在机器人的一个运行周期中,首先是AIRobot将比赛信息发送给机器人,这些信息包括所有机器人的状态(如坐标,运动速度)、当前所发生的事件(各种Action处理函数)、比赛场地的大小等。机器人程序接收到这些信息后,经过对这些数据的分析和处理,就可以依据分析的结果来控制机器人运动。在下达机器人控制命令后,机器人程序将把这些命令发送给AIRobot,AIRobot将根据这些命令来更新比赛信息,控制比赛的过程。整个比赛的过程就是连续的由这样的运行周期组成。
 
    机器人程序是一个独立运行的应用程序,这个程序实际上是由两部分代码组成的,一部分是适配器代码,另一部分是用户代码。用户代码的主要功能就是控制机器人的运动,这也是编程的焦点。适配器代码对用户来说是透明的,它向用户隐藏了机器人程序和AIRobot交互的细节,并对用户代码呈现一个简单的编程接口,这部分代码有以下的几个重要的功能:
* 通过socket和AIROBOT建立连接。
* 接收AIRobot发送过来的比赛信息,并对其进行解析:将XML形式的数据转换为用户代码容易使用的形式,如Java,C++中的对象。
* 调度用户代码的运行:根据当前的Action,触发用户代码的Action处理函数。
* 对用户代码设置的机器人控制命令加以编码:将命令转换为XML形式,并将其发送给AIRobot。
 
    用户代码必须和适配器代码连接在一起,才能生成一个完整的应用程序。拿C++机器人程序来说,适配器代码以“.lib”的形式存放在c/lib下,系统为不同的编译器提供了相应的版本,在创建机器人程序的时候,就要和这些.lib进行连接。
 
数据传输
    数据以XML的形式(UTF-8编码)在AIRobot运行平台和机器人程序之间传输,这个过程是通过对socket进行读写来完成的。实际上XML数据就是一种有着特殊格式的字符串,在AIRobot和机器人之间的数据传输就是通过socket传输字符串数据。在一般情况下,socket都会提供读写字节的操作,字符串数据可以通过字节数组的形式在socket上传输。下面将介绍AIRobot和机器人在传输数据时的约定。
在接收字符串的时候
    1.读入字符串的长度。这个长度描述了字符串数据所占的字节数。这个长度占用两个字节,通过socket提供的读字节操作读出这两个字节,假设第一个字节为a,第二个字节为b,那么这个长度的计算公式为length=(((a & 0xff) << 8) | (b & 0xff))。注意,这个长度只是描述了字符串的长度,它并不属于字符串的内容。
    2.读入字符串数据。根据字符串的长度length,从socket中读入length字节的数据,这些数据就是我们真正需要的XML数据。
这个过程用伪代码实现就像是这样:
/* 从socket中读入一个字符串 */
String readString(Socket socket){
byte a = socket.readByte(); //读入第一个字节
byte b = socket.readByte(); //读入第二个字节
int length = (((a & 0xff) << 8) | (b & 0xff)); //计算字符串长度
 
byte[] strbuff = new byte[length]; //创建用于存放字符串数据的缓存
socket.readBytes(strbuff, length); //将字符串数据读入缓存中(length个字节)
 
return new String(strbuff); //将缓存中的内容作为一个字符串返回
}
 
在发送字符串的时候
    1.写入字符串的长度。这个长度占用两个字节,假设字符串的长度为length,那么要写入的第一个字节a=(byte)(0xff & (length >> 8)),第二个字节b=(byte)(0xff & length)。
    2.写入字符串数据。将字符串作为字节数组写入socket。
这个过程用伪代码实现就像是这样:
/* 将一个字符串string写入socket */
void readString(Socket socket, String string){
int length = string.length(); //字符串的长度
byte a = (byte)(0xff & (length >> 8)); //计算要写入的第一个字节
byte b = (byte)(0xff & length); //计算要写入的第二个字节
 
socket.writeByte(a); //写入长度的第一个字节
socket.writeByte(b); //写入长度的第二个字节
 
byte[] strbuff = string.toBytes(); //将字符串转换为字节数组
socket.writeBytes(strbuff, length); //写入字符串数据(length个字节)
}
 
    从上面的细节中我们可以看到,读写字符串的操作是相对的。对于这两个操作,不同的语言有不同的实现,具体细节和语言提供的socket和字符串实现有关。在C++机器人适配器代码中,commons/NetTransporter.hpp中定义的类NetTransporter实现了这两个操作(readString,writeString),大家可以作为参考。
    通过以上的分析我们可以看到,适配器代码对Robot隐藏了机器人程序和AIROBOT交互的细节,并以抽象类的形式对其呈现一个简单的编程接口。在对机器人编程的过程中,用户只需实现自己的Action处理函数就够了。
     好了,我们已经对AI-TANK有了一个长足的了解。是不是有点手痒痒的,想办上自己写一个机器人程序出来放到战场上一较高低。不急,在下面的章节中我们将分用图形拖拉的方式、C语言、C++语言、Java语言、C#语言教大家各种不同的机器人策略。让您真正体会到编程的乐趣、编程的简单。到最后你将不知不觉中踏入程序设计的殿堂。实现征服程序世界的梦想!

用遗传算法加强足球游戏的人工智能

转自:http://blog.csdn.net/popkiler/article/details/1773935 用遗传算法加强足球游戏的人工智能 终于等够了三个月,杂志的约定已经到期,可以把这篇文...

从作弊到机器学习——足球AI概况

从作弊到机器学习——足球AI概况作者: alexjc译者: 赖勇浩(恋花蝶)原文地址:http://aigamedev.com/questions/football-ai-cheating-machi...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

让世界充满AI—时代的开拓者(程序员)

让世界充满AI—时代的开拓者(程序员)    人工智能是一门涵盖多学科知识,而又被当今社会广泛应用于多领域,给人带来便捷、高效的同时,又让业界为其担心的交叉学科知识的综合产物。随着各种智能机器人...

大数据&AI智能—物联网世界对职业发展的影响

基于电脑的服务叫做互联网 基于智能手机的服务叫做移动互联网 基于所有智能终端的服务叫做物联网   物联网在实际应用上的开展需要各行各业的参与,在参与的同时,改变着行业对人才的需...
  • cpless
  • cpless
  • 2017年11月06日 10:06
  • 58

Ada Lovelace—世界上首位“程序媛”

当今互联网技术快速发展,但我们甚少听到女性在科技圈的发声,却越来越多听到对女性的负面评价:抗压能力差、很难独当一面、代码水平一般等。 更甚者有企业在招聘时暗文规定:不招女程序员,或者给她们开出低的薪资...

多重继承不好的观点是错误的 — 小评<松本行弘的程序世界>

首先得说, 一般某种语言的发明人写的关于自己语言的东西都是非常值得阅读的, 从别的牛人那里你也许能学会很多奇技淫巧, 但从语言发明人那里你能学到语言发明人本身设计的初衷, 以及设计时的一些抉择. 这种...
  • vagrxie
  • vagrxie
  • 2013年01月08日 01:35
  • 17382
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:征服程序世界—我的AI-CODE
举报原因:
原因补充:

(最多只允许输入30个字)