NekoNinja1.3 原码分析

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 );
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: SIFT(尺度不变特征变换)是一种在计算机视觉中常用的特征提取算法,用于检测图像中的关键点并描述它们的局部特征。SIFT主要包括尺度空间极值检测、关键点定位、关键点方向计算和局部特征描述四个步骤。 在OpenCV中,SIFT算法的原始代码位于"opencv-2.4.9/modules/nonfree/src/sift.cpp"文件中。该文件中定义了SIFT类,并且实现了该类的成员函数和成员变量。SIFT算法中的各个步骤都在该文件中有相应函数的实现。 首先,尺度空间极值检测使用DoG(差分高斯)金字塔来寻找关键点位置。其原理是比较不同尺度下的图像差分,找到局部极值点。该步骤的实现函数是"Sift::buildDoGPyramid"。 接下来,在关键点定位阶段,使用了极值点的插值方法来精确定位关键点。该步骤的实现函数是"Sift::findScaleSpaceExtrema"。 然后,计算每个关键点的主方向,以确保特征具有旋转不变性。主方向计算基于关键点周围的梯度信息,进而确定最具代表性的方向。该步骤的实现函数是"Sift::calcOrientationHist"。 最后,在局部特征描述阶段,利用关键点周围的图像区域计算局部特征描述子,以描述关键点周围的外观信息。该步骤的实现函数是"Sift::compute"。 在SIFT算法的实现中,还涉及到许多辅助函数和数据结构,如图像金字塔构建函数、关键点类、尺度空间类等。这些函数和类主要用于SIFT算法的各个步骤,并且相互调用实现整个算法。 总结来说,SIFT算法的原始代码实现了尺度空间极值检测、关键点定位、关键点方向计算和局部特征描述四个主要步骤。通过该算法,可以在图像中提取出具有尺度不变性的关键点,并生成描述子用于图像匹配或目标识别等应用。 ### 回答2: OpenCV是一个开源的计算机视觉库,其中2.4.9版本是其中的一个旧版本。SIFT(尺度不变特征变换)是一种用于计算机视觉中目标检测和图像匹配的算法。下面将简要分析OpenCV 2.4.9中SIFT算法的原码。 SIFT算法主要包含以下几个步骤: 1. 尺度空间极值检测(Scale Space Extrema Detection):通过构建图像的高斯金字塔来检测图像中的极值点,这些点可能代表着图像的关键特征。 2. 关键点定位(Key Point Localization):在尺度空间极值点的基础上,通过比较尺度空间图像的相邻像素值来排除低对比度和边缘响应较高的关键点。 3. 方向分配(Orientation Assignment):为每个关键点分配一个主方向,来使得特征具有旋转不变性。 4. 特征描述(Feature Description):使用关键点的局部图像窗口来计算具有尺度和旋转不变性的特征描述子。 在OpenCV 2.4.9版本中,SIFT算法的实现位于modules/nonfree/src/sift.cpp文件中。该文件中实现了SiftFeatureDetector和SiftDescriptorExtractor两个类,分别负责关键点的检测和特征描述的计算。 在SiftFeatureDetector类的实现中,主要包括构建高斯金字塔、检测关键点、定位关键点等步骤。而SiftDescriptorExtractor类的实现主要包括计算关键点的梯度方向直方图和生成特征描述子等步骤。 通过阅读源码可以深入理解SIFT算法的原理和实现细节,包括高斯模糊、尺度空间极值检测、定位关键点、方向分配、特征描述等关键步骤的具体实现方式。 需要注意的是,由于SIFT算法的专利限制,OpenCV的最新版本中已经移除了SIFT算法的实现。因此,如果需要使用SIFT算法,建议考虑使用非专利限制的开源实现,如VLFeat等。 ### 回答3: SIFT(尺度不变特征变换)是一种在计算机视觉领域中广泛应用的特征提取算法,它具有尺度不变性和旋转不变性等特点。而OpenCV则是一个开源的计算机视觉库,提供了许多图像处理和计算机视觉算法的实现。 要分析SIFT在OpenCV 2.4.9中的原码,首先需要了解SIFT算法的基本原理和步骤。SIFT算法包括关键点检测、关键点描述子计算和匹配等步骤。 在OpenCV代码中,SIFT算法主要位于`SIFT.cpp`文件中。该文件中实现了SIFT算法的各个功能函数,包括`calcOrientationHist`、`detectAndCompute`、`buildGaussianPyramid`等。其中,`detectAndCompute`函数负责检测关键点和计算关键点描述子。 在`detectAndCompute`函数中,首先通过调用`calcOrientationHist`函数计算每个关键点的方向直方图。然后,通过调用`buildGaussianPyramid`函数构建高斯金字塔,用于检测关键点。接下来,通过调用`findScaleSpaceExtrema`函数,在不同尺度和空间上寻找关键点。然后,通过调用`calcDescriptors`函数计算关键点的描述子。 `findScaleSpaceExtrema`函数通过迭代计算高斯差分金字塔,检测关键点并筛选出稳定的关键点。`calcDescriptors`函数则通过计算关键点周围像素的梯度和方向,生成关键点的描述子。 需要注意的是,OpenCV 2.4.9中的SIFT实现是基于非专利版本的SIFT算法,采用了DoG(高斯差分)方法来检测关键点,而不是原始的Harris角点检测。 通过分析SIFT在OpenCV 2.4.9的原码,可以深入了解SIFT算法的实现细节,理解各个函数的作用和调用关系。这有助于对SIFT算法进行深入研究和优化,以满足具体应用需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值