|
package myl.micro; import robocode.*; import java.awt.Color; import java.awt.geom.Point2D;
/** * NekoNinja - a ninja by Martin Y Lepsoy at mlepsoy@yahoo.no * * 18.12.2002 - Version 1.30 - codesize 745 * * Now even the gun is based on the, soon to be released, Predator 1.50. A pattern analyzer that actually works * Is extremely hard to hit constantly. * Even SandBoxDT 1.61 and Duelist 0.1.6 will have a hard time predicting where it will go next time * This copy readable, but codesize too big. Download the released version from www.robocoderepository.com * * * 15.12.2002 - Version 1.20 - codesize 618 * * Completely new robot, now only specializes in 1v1 combat * Aim is a random amount of calculated aim at a distance over 400 * No dodging anymore, movement based on Predator's successful movement * * * 09.11.2002 - Version 1.1 - codesize 542 * * New radar management: The radar spins while we are tracking the closest target * Aim with average velocity * New dodging, better and less predictable ( I think ) * Won't get slaughtered in melee like before ( but it's not that good :) * * * 14.10.2002 - Version 1.0 - codesize 534 * * NekoNinja is a 1v1 specialist * */ /** * NekoNinjia是目前世界上公认最好的1v1 microbot,其中1.30版本又是它所有版本中最好的(作者自己都承认) * 他的特点是运动采用随机运动,射击采用模式分析 * 由于是microbot,受codesize<750的限制,所以代码写的很精简(这几乎是所有mini,micro,nano bot的共同特点) * 所以给原码分析带来很多困难,加上分析者水平有有限,分析错误之处在所难免,欢迎读者不吝批评指正。:) * 分析说明:分析中作者原来所带的英文注释均加以保留,一些基础的,浅显易懂的(比如说像setAdjustRadarForGunTurn(true); * setColors( c , c , new Color( 162 , 244 , 89 ) );这样的语句)均没有分析,主要分析的是作者的设计思路。 * ----分析者:iiley */ public class NekoNinjaReadableVersion extends AdvancedRobot { static double nextMove; //the time before our next move
final static int storedInfo = 800; //how many frames we store in our pattern analyzer. //not too large to get fresh details on target quickly, //but large enough to remember the opponents behavior a little while static double[][] pattern = new double[storedInfo][4]; //all stored info about our enemy
public void run() { setAdjustRadarForGunTurn(true); setAdjustGunForRobotTurn(true);
Color c = Color.darkGray.brighter(); setColors( c , c , new Color( 162 , 244 , 89 ) ); //new and better ninja colors:-p
while( true ) { turnRadarRightRadians( Double.POSITIVE_INFINITY ); //turn radar to find a target } }
public void onScannedRobot(ScannedRobotEvent e) { int time = (int) getTime() % storedInfo; // store time to save codesize double targetBearing = getHeadingRadians() + e.getBearingRadians(); double ourX = getX(); //store own coordinates double ourY = getY(); //-"- double firePower = Math.min( 3 , 30 * getEnergy() / e.getDistance() ); //powersaving when far from target and low on energy //movement code /** * 在场地中随机寻找一点作为运动的目标点(这一点要满足离墙不能太近,而且如果要过去这一点,我需要的用方向要满足不能和当前正在运动的方向相差太少 * 也就是要>minimumAngleDifference,也不能于相对敌人的方向相差太少,要>2 * minimumAngleDifference,意思就是说,方向要变多些,不能看起来 * 跟没改变差不多,也不能向着敌人冲过去。minimumAngleDifference是一个自己设定的数,因该是经过测试和塞选出来的,为什么要跟a有关,也就是为什么 * 循环到后面minimumAngleDifference越小?因为万一前面找不到满足条件的角度呢?那么后面,限制就因该小一些) * nextMove = e.getDistance() / ( 29 - 3 * firePower );if ( nextMove-- <= 0 )的意思是在敌人第二发子弹快要发出的时候改变运动目标点 */ if ( nextMove-- <= 0 ) { //it's time to make our next move for ( double a = 0 ; a < 500 ; a++ ) { double testX = Math.random() * getBattleFieldWidth(); //make random test points double testY = Math.random() * getBattleFieldHeight(); //-"- double distToWall = Math.min( testX , getBattleFieldWidth() - testX ) * Math.min( testY , getBattleFieldHeight() - testY ); //check the product of the distance to the shortest walls if ( distToWall > 7600 ) { //make sure the point is within the current wall tolerance (which is always 7600 in this bot which is good for a 800 x 600 battlefield) //use point if angle to point is a certain amount from angle to target double ourAngleToPoint = getAngle( ourX , ourY , testX , testY ); //angle to the testpoint double minimumAngleDifference = ( ( 500 - a ) / 500 ) * Math.PI / 6; //minimumAngleDifference is the smallest angle difference to accept 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() ); //calculate angle to turn to next point //decide which direction to go to reach our point as soon as possible double moveDirection = 1; //use front if next check is false if ( Math.abs( turnAngle ) > Math.PI / 2 ) { //if angle is greater than PI / 2 in either directions moveDirection = - 1; //switch direction turnAngle += Math.acos( moveDirection ); //and change turnangle acordingly } setTurnRightRadians( angle_180( turnAngle ) ); //make turn setAhead( moveDirection * Point2D.Double.distance( ourX , ourY , testX , testY ) ); //move towards point break; } } } //make nextMove a little before a bullet will reach you next time nextMove = e.getDistance() / ( 29 - 3 * firePower ); //use own firePower here to save space. The enemy is likely to use about the same power as us since firepower often is based on own energy } setMaxVelocity( getTurnRemaining() > 45 ? 0.001 : 8 ); //adjust velocity when turning to make sharper turns //end of movement //pattern analyzer /** * 射击策略主要思路是用一个800长度的数组纪录一个直线提前量射击策略的PredictionAngle * 每个记录点都记录从自己子弹到敌人这段时间(子弹假定能量为3,敌人假定为不动,虽然这样不精确,但是也只能这样,你知道敌人怎样动?如果知道你还计算啥?) * 里PredictionAngle的累加 * 需要射击时,寻找一个历史中(所有以前的记录节点中)完成了累加的,而且与目前的PredictionAngle最相似的记录节点(也就是以PredictionAngle最相似 * 就视为运动方式最相似),那么,敌人以后的运动因该和历史中的那段运动情况很相似(也就是历史重现)。 * 找到了历史要重现的地方了,那么用那段历史中的平均PredictionAngle作为射击Angle射击 * * 这种射击方式就是寻找历史中和你现在运动相似的情况,所以如果你的运动有规律的话,他能很好的找出你下一步会怎样走,那么,你被命中的几率就很大 * 这也正是作者的运动采用随机运动的原因。 */ int match = 0; pattern[ time ][0] = e.getDistance() / ( 20 - 3 * firePower ); //store time before we will use this angle in our analyzer pattern[ time ][1] = e.getVelocity() * Math.sin( e.getHeadingRadians() - targetBearing ); //store linear prediction angle
/** * pattern[ time ][1] = e.getVelocity() * Math.sin( e.getHeadingRadians() - targetBearing ); * 正弦定理e.getVelocity()/Math.sin(predictionAngle)==myBulletVelocity/Math.sin(e.getHeadingRadians() - targetBearing); * 所以pattern[ time ][1]==myBulletVelocity*Math.sin(predictionAngle); * 也就是说pattern[ time ][1]记录的是直线提前量的射击角度与自己子弹速度的乘积 */
pattern[ time ][2] = pattern[ time ][3] = 0; //reset values used to store our target's actual move for ( int a = 0; a < storedInfo; a++ ) { //每个记录点pattern[a]中,如果当前时间是在我的子弹到达敌人之前则累加计算到的myBulletVelocity*Math.sin(predictionAngle)值; //同时记录累加了多少次(pattern[ a ][3]的值) if ( pattern[ a ][0] > 0 ) { //if angle is under evaluation pattern[ a ][2] += e.getVelocity() * Math.sin( e.getHeadingRadians() - targetBearing ); //add angle to prediction pattern[ a ][0]--; //decrease time before use pattern[ a ][3]++; //increase counter for number of angles used } } //在历史中(所有记录中)寻找一个和目前的myBulletVelocity*Math.sin(predictionAngle)值最相近的记录点(这个纪录点必须是累加完毕的点pattern[a][0]<0) //把这个纪录点的数组下表记录到match中 for ( int a = 0; a < storedInfo; a++ ) { //cycle all our stored values double testAngle = pattern[ time ][1]; //store the angle this turn to save codesize if ( pattern[ a ][0] < 0 && Math.abs( testAngle - pattern[ a ][1] ) < Math.abs( testAngle - pattern[ match ][1] ) ) { //if this value is better than our current match match = a; //change match to this value } } //计算需要射击的角度linearPredictionAngle //这角度是在我子弹到达敌人这段时间里的predictionAngle的平均值 double linearPredictionAngle = pattern[ match ][2] / ( pattern[ match ][3] * ( 20 - 3 * firePower ) ); //prediction angle with our best match //end of pattern analyzer linearPredictionAngle = linearPredictionAngle >= 0 ? linearPredictionAngle : 0; setTurnGunRightRadians( angle_180( targetBearing - getGunHeadingRadians() + linearPredictionAngle ) ); //turn gun to target with an offset as predicted in our analyzer setTurnRadarRightRadians( Math.tan( targetBearing - getRadarHeadingRadians() ) * 3 ); //turn radar to target enemy if ( getEnergy() > Math.min( e.getEnergy() + firePower + 0.1 , 3.1 ) ) { //make sure we won't kill ourself when shooting, also let the enemy run out of energy before you setFire( firePower ); //fire } scan(); //scan to see if our target is still here }
//helpers /** * 返回角度ang的-PI到PI相对应的值(弧度表示) */ public double angle_180( double ang ) { //get the relative angle where -180 < angle < 180 return Math.atan2( Math.sin( ang ), Math.cos( ang ) ); } /** * 返回点(x2,y2)到(x1,y1)连线的绝对角度(战场场地参照系) */ double getAngle( double x1 , double y1 , double x2 , double y2 ) { //get the angle between two points return Math.atan2( x2 - x1 , y2 - y1 ); } } |
|
|