Can you explain what this code from robocode means?

2.2k Views Asked by At

I do not understand what this method exactly is doing. Could someone explain this trig to me?

public void onScannedRobot(ScannedRobotEvent e)
  {
    setTurnRadarLeftRadians(getRadarTurnRemaining());

    setTurnRight(e.getBearing() + 90.0D - dir / 33.0D);
    if (((Guppy.var = var - e.getEnergy()) <= 3) && (var > 0.0D))
    {
      setMaxVelocity(Math.random() * 9.0D + 3);
      setAhead(Guppy.dir = dir * type);
    }
    setTurnGunRightRadians(Math.sin((Guppy.var = e.getBearingRadians() + getHeadingRadians()) - getGunHeadingRadians() + Math.asin((vel * 0.4D + e.getVelocity() * 0.6D) / 14.0D * Math.sin(e.getHeadingRadians() - var))));
    if (getGunHeat() == 0.0D)
    {
      setFire(2.9D);
      vel = e.getVelocity();
    }
    var = e.getEnergy();
  }
2

There are 2 best solutions below

0
On BEST ANSWER

Of note to those familiar with trigonometry but not Robocode: Robocode's angular values are nonnegative and increase clockwise, rather than zero-centered and increasing counterclockwise as in trigonometric convention.

Holy cow, that's a hairball. Painful style, too, assigning a variable and then using it within the same expression and randomly reusing static variables for different purposes. (EDIT: I've been out of robocode too long; obfuscated-looking messes are likely the result of hand-optimizing for code size.) Let's try to unpick that one messy line, starting here:

setTurnGunRightRadians(Math.sin((Guppy.var = e.getBearingRadians() + getHeadingRadians()) - getGunHeadingRadians() + Math.asin((vel * 0.4D + e.getVelocity() * 0.6D) / 14.0D * Math.sin(e.getHeadingRadians() - var))));

Looks like var is being used to hold the result of the subexpression e.getBearingRadians() + getHeadingRadians(). Since e.getBearingRadians() returns the target's bearing relative to your heading, var is the absolute target's bearing. Refactored:

double targetBearing = e.getBearingRadians() + getHeadingRadians();
setTurnGunRightRadians(Math.sin(targetBearing - getGunHeadingRadians() + Math.asin((vel * 0.4D + e.getVelocity() * 0.6D) / 14.0D * Math.sin(e.getHeadingRadians() - targetBearing))));

Ugh, still a mess. We also have the variable vel unaccounted for, but based on the rest of the code, it seems to be the target's velocity when last scanned (and under the rash assumption that only one-on-one matches will be played); it's combined into a weighted average with the current velocity to give a rough predicted velocity, so there's probably some target-leading logic going on here.

double targetBearing = e.getBearingRadians() + getHeadingRadians();
double predictedVelocity = vel * 0.4D + e.getVelocity() * 0.6D;
setTurnGunRightRadians(Math.sin(targetBearing - getGunHeadingRadians() + Math.asin(predictedVelocity / 14.0D * Math.sin(e.getHeadingRadians() - targetBearing))));

Since robocode appears to neglect the difference between course and heading, the innermost sine Math.sin(e.getHeadingRadians() - targetBearing) gives a signed coefficient indicating what component of the target's velocity is perpendicular to its bearing and therefore requires firing-angle adjustment.

double targetBearing = e.getBearingRadians() + getHeadingRadians();
double predictedVelocity = vel * 0.4D + e.getVelocity() * 0.6D;
double perpendicularMotionCoefficient = Math.sin(e.getHeadingRadians() - targetBearing);
setTurnGunRightRadians(Math.sin(targetBearing - getGunHeadingRadians() + Math.asin(predictedVelocity / 14.0D * perpendicularMotionCoefficient)));

The arc-sine expression seems to be intended to turn the perpendicular component of the target's motion back into an angular adjustment. This bit of math is outright wrong because arc-sine doesn't work that way: it turns a coefficient in the interval [-1, 1] back into an angle in the interval [-pi/2, pi/2], and dividing predicted velocity by 14 is probably just an attempt to bound the argument to arc-sine to within its domain. Then again, neither distance to the target nor projectile speed enter the computation either, so the best I can say about this adjustment is that it's vaguely in the right direction.

double targetBearing = e.getBearingRadians() + getHeadingRadians();
double predictedVelocity = vel * 0.4D + e.getVelocity() * 0.6D;
double perpendicularMotionCoefficient = Math.sin(e.getHeadingRadians() - targetBearing);
double firingAngleAdjustment = Math.asin(predictedVelocity / 14.0D * perpendicularMotionCoefficient);
setTurnGunRightRadians(Math.sin(targetBearing - getGunHeadingRadians() + firingAngleAdjustment));

That last sine makes no sense to me at all. The argument is already the angle in radians the gun needs to be moved by: the target's absolute bearing minus the current gun heading (which is how far the gun would need to turn to point exactly at the target), plus the firing angle adjustment for the target's movement. I'll just remove the sine function there entirely.

double targetBearing = e.getBearingRadians() + getHeadingRadians();
double predictedVelocity = vel * 0.4D + e.getVelocity() * 0.6D;
double perpendicularMotionCoefficient = Math.sin(e.getHeadingRadians() - targetBearing);
double firingAngleAdjustment = Math.asin(predictedVelocity / 14.0D * perpendicularMotionCoefficient);
setTurnGunRightRadians(targetBearing - getGunHeadingRadians() + firingAngleAdjustment);

Of course, that also depends on whether setting the gun to turn more than a half-turn (pi radians) is automatically understood to turn it less than a half-turn the other direction instead... you may end up doing lots of pointless gun-spinning otherwise.

1
On
    public void onScannedRobot(ScannedRobotEvent e) // for every robots that your robot see {
setTurnRadarLeftRadians(getRadarTurnRemaining()); // Sets the robot's radar to turn left by radians 
setTurnRight(e.getBearing() + 90.0D - dir / 33.0D); //   Sets the robot's body to turn right by degrees, basically you are depending it in your enemy
if (((Guppy.var = var - e.getEnergy()) <= 3) && (var > 0.0D))
{
  setMaxVelocity(Math.random() * 9.0D + 3); //Sets the maximum velocity of the robot measured in pixels/turn if the robot should move slower than Rules.MAX_VELOCITY (8 pixels/turn).
  setAhead(Guppy.dir = dir * type);
}
setTurnGunRightRadians(Math.sin((Guppy.var = e.getBearingRadians() + getHeadingRadians()) - getGunHeadingRadians() + Math.asin((vel * 0.4D + e.getVelocity() * 0.6D) / 14.0D * Math.sin(e.getHeadingRadians() - var))));
if (getGunHeat() == 0.0D)
{
  setFire(2.9D); // how big is the damage of the bullet. The bigger the damage= bigger energy consumed
  vel = e.getVelocity(); //get the enemy velocity
}
var = e.getEnergy(); // get the enemy energy

}

If you want you can play around with these mini project of mine:

                    package sample;
                import robocode.*;
                import java.awt.Color;
                import java.awt.geom.*;
                import java.lang.*;         // for Double and Integer objects
                import java.util.*; // for collection of waves
                import robocode.util.Utils;

                import robocode.RobotDeathEvent;
                import robocode.Rules;
                import robocode.ScannedRobotEvent;
                import robocode.util.Utils;

                /*
            A Robocode that implements the smart targeting(always attack on the nearest opponent) and circles around them
                */

                public class Tutorial extends AdvancedRobot
                {   boolean movingForward;
                    boolean peek; // Don't turn if there's a robot there
                    double moveAmount; // How much to move
                    byte moveDirection= 1;
                    int tooCloseToWall = 0;
                    int wallMargin = 60;

                    static final double GUN_FACTOR = 30;
                    static final double AIM_FACTOR = 25;
                    static final int AIM_START = 10;
                    static final int FIRE_FACTOR = 100;
                    private long    currTime;               
                    static final long RADARTIMEOUT  = 20;
                    static double xForce;
                    static double yForce;
                    static double lastDistance;

                    private boolean radarScan;              
                    private long    lastscan;               

                    public void run(){

                        setAdjustRadarForGunTurn(true);
                        setAdjustGunForRobotTurn(true);

                        // Sets these colors (robot parts): body, gun, radar, bullet, scan_arc
                        setBodyColor(new Color(255, 255, 255));
                        setGunColor(new Color(255, 255, 255));
                        setRadarColor(new Color(255, 255, 255));
                        setBulletColor(new Color(0, 0, 0));
                        setScanColor(new Color(255, 255, 255));

                        while(true) {
                            currTime = getTime();
                            turnRadarRightRadians(Double.POSITIVE_INFINITY);
                            turnRadarLeftRadians(Double.POSITIVE_INFINITY);
                            radarScan = true;
                        }
                    }

                    public void onScannedRobot(ScannedRobotEvent e) {

                        double absoluteBearing = e.getBearingRadians()+ getHeadingRadians();
                        double  distance = e.getDistance();
                        double  bulletPower ;
                        double headingRadians = getHeadingRadians();
                        boolean fired = false;

                        if(getOthers() >= 2){

                            xForce = xForce *.80- Math.sin(absoluteBearing) / distance;
                            yForce = yForce *.80 - Math.cos(absoluteBearing) / distance;

                        setTurnRightRadians(Utils.normalRelativeAngle(
                            Math.atan2(xForce + 1/getX() - 1/(getBattleFieldWidth() - getX()), 
                                       yForce + 1/getY() - 1/(getBattleFieldHeight() - getY()))
                                        - getHeadingRadians()) );
                        setAhead(150- Math.abs(getTurnRemaining()));//speed
                        bulletPower = ( distance > 850 ? 0.1 : (distance > 700 ? 0.49 : (distance > 250 ? 1.9 : 3.0)));
                                bulletPower = Math.min( getEnergy()/5, Math.min( (e.getEnergy()/4) + 0.2, bulletPower));

                        if ((getGunHeat() <= 3.0) && (getGunTurnRemaining() == 0.0) && (bulletPower > 0.0) && (getEnergy() > 9.1)) {
                        // Only fire the gun when it is locked and loaded, do a radarsweep afterwards.
                        bulletPower=3;
                        setFire( bulletPower);
                        fired = true;
                        }
                        if ((fired == true) || (currTime >= (lastscan + RADARTIMEOUT))) {
                            setTurnRadarRightRadians( Double.NEGATIVE_INFINITY * Utils.normalRelativeAngle( absoluteBearing- getRadarHeadingRadians()));
                            lastscan = currTime;
                            radarScan = true;
                        }

                        // when not firing, lock on target.
                        //setTurnRadarRightRadians( 2.2 * Utils.normalRelativeAngle( absoluteBearing - getRadarHeadingRadians()));
                        //radarScan = false;

                        lastDistance = Double.POSITIVE_INFINITY;
                        setTurnRadarLeft(getRadarTurnRemaining());
                        setTurnRadarRight(getRadarTurnRemaining());

                        if(lastDistance+20 > distance){
                            lastDistance = distance;
                            if(getGunHeat() < .1){
                                setTurnRadarLeft(getRadarTurnRemaining());
                                setTurnRadarRight(getRadarTurnRemaining());
                                clearAllEvents();
                            }
                            setTurnGunRightRadians(robocode.util.Utils.normalRelativeAngle(absoluteBearing - getGunHeadingRadians() + Math.max(1 - distance / (40), 0) * (e.getVelocity() / (AIM_START + Math.pow(AIM_FACTOR, distance))) * Math.sin(e.getHeadingRadians() - absoluteBearing) ));       
                            }
                    }

                        if(getOthers() == 1){

                            setBodyColor(new Color(255, 0, 0));
                            setGunColor(new Color(255,  0, 0));
                            setRadarColor(new Color(255, 0, 0));
                            setBulletColor(new Color(255, 0, 0));
                            setScanColor(new Color(255, 0, 0));

                            double bulletPower1 = 3;
                            if(distance >= 700){
                                lastDistance = Double.POSITIVE_INFINITY;
                            }

                            else if (distance < 700 && distance > 500){
                                bulletPower = 3 - (( 24.5- getEnergy()) / 6);
                                if (getEnergy() >= 3.1 ) {
                                    setFire(bulletPower);
                                    lastDistance = Double.POSITIVE_INFINITY;
                                }
                                else{
                                    lastDistance = Double.POSITIVE_INFINITY;
                                }
                            }

                        else if (distance <=500 && distance >=350){
                            bulletPower = 3 - (( 17.5- getEnergy()) / 6);
                            if (getEnergy() >= 3.1 ) {
                                setFire(bulletPower);
                                lastDistance = Double.POSITIVE_INFINITY;
                            }
                            else{
                                lastDistance = Double.POSITIVE_INFINITY;
                            }
                        }

                        else if (distance < 350 ){
                            bulletPower = 3 - (( 5- getEnergy()) / 6);
                            if (getEnergy() >= 3.1 ) {
                                setFire(bulletPower);
                                lastDistance = Double.POSITIVE_INFINITY;
                            }
                            else{
                                lastDistance = Double.POSITIVE_INFINITY;
                            }
                        }

                    if(lastDistance+25 > distance){
                        lastDistance = distance;
                        if(getGunHeat() < .1){
                                setTurnRadarLeft(getRadarTurnRemaining());
                                setTurnRadarRight(getRadarTurnRemaining());
                                clearAllEvents();
                        clearAllEvents();
                    }
                setTurnGunRightRadians(robocode.util.Utils.normalRelativeAngle(absoluteBearing - getGunHeadingRadians() + Math.max(1 - distance / (400), 0) * (e.getVelocity() / (AIM_START + Math.pow(AIM_FACTOR, distance))) * Math.sin(e.getHeadingRadians() - absoluteBearing) ));      
                }
                    wall();
                    // always square off against our enemy, turning slightly toward him
                    setTurnRight(e.getBearing() + 90 - (10 * moveDirection));
                    // if we're close to the wall, eventually, we'll move away
                    if (tooCloseToWall > 0) tooCloseToWall--;
                    // normal movement: switch directions if we've stopped
                    if (getVelocity() == 0) {
                        setMaxVelocity(8);
                        moveDirection *= -1;
                        setAhead(500 * moveDirection);
                    }
            }
    }   

                    public void onHitByBullet(HitByBulletEvent e) {
                        setTurnRadarLeft(getRadarTurnRemaining());
                        setTurnRadarRight(getRadarTurnRemaining());
                        clearAllEvents();
                    }

                    public void onBulletMissed(BulletMissedEvent e) {
                        setTurnRadarLeft(getRadarTurnRemaining());
                        setTurnRadarRight(getRadarTurnRemaining());
                        clearAllEvents();
                    }

                    public void onHitRobot(HitRobotEvent e) {
                        setTurnRadarLeft(getRadarTurnRemaining());
                        setTurnRadarRight(getRadarTurnRemaining());
                        clearAllEvents();
                    }

                    public void wall(){
                    //close to left, right, bottom, top wall
                    if(getX() <= wallMargin || getX() >= getBattleFieldWidth() - wallMargin || getY() <= wallMargin || getY() >= getBattleFieldHeight() - wallMargin){
                    if(tooCloseToWall <= 0){
                    // coming near the wall
                        tooCloseToWall += wallMargin;
                        setMaxVelocity(0); // stop!!!
                    }
            }
    }   

}