NekoNinja1.3 原码分析

来源:互联网 发布:2016年网络病毒 编辑:程序博客网 时间:2024/05/21 03:27

NekoNinja1.3 原码分析

作者:iiley    
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 );
    }
}


原创粉丝点击