Quantum/Source

From Robowiki
Jump to navigation Jump to search
// -------------------------------------------------------------------------------------------------
// ==============
// Quantum (Nano)
// ==============
// Author: David414
// License: RWPCL (https://robowiki.net/wiki/RWPCL)
// -------------------------------------------------------------------------------------------------

// -------------------------------------------------------------------------------------------------
// Credits
// -------------------------------------------------------------------------------------------------
// Quantum started out life as a tweaked version of Mike Dorgan's DustBunny before being heavily
// shrunk to make room for improved target tracking (inspired by Infinity, also by Mike).

// -------------------------------------------------------------------------------------------------
// Version History
// -------------------------------------------------------------------------------------------------
// ================================================================================
// v0.2.2 (2024-04-06) Codesize: 249  | Colors: Yes | 1v1 capable: No
// ================================================================================
// - Antigravity movement based on DustBunny, but with forces calculated based on
//   where we would aim to hit the enemy, rather than where they currently are
// - Aim like DustBunny but with different energy management based only on how
//   many enemies are still alive. Fire more agressively early on and much less
//   agressively in 1v1
// - Quite a bit of code shrinking to make room for Infinity style target tracking
//
// Nano melee:     2nd APS (64.81),  1st Survival (25.36)
// Micro melee:    9th APS (64.17),  3rd Survival (25.20)
// Mini melee:    18th APS (63.52),  8th Survival (24.63)
// General melee: 67th APS (60.56), 55th Survival (21.60)
//
// Knocked DustBunny off of the top of the table for a few days before settling in to second place.
// Only 0.2 APS behind but significantly ahead on survival.
// Currently assumes a 1000x1000 battlefield. There would be space to remove this restriction but
// that would mean giving up on my colours :(
//
// ================================================================================
// v0.2.2a (2024-04-11) Codesize: 249  | Colors: Yes | 1v1 capable: No
// ================================================================================
// - No functional changes, added credits, version history and commented the code

// -------------------------------------------------------------------------------------------------
// TODO
// -------------------------------------------------------------------------------------------------
// - Measure the APS/Survival impact of moving setTurnRadarRight from run() to onRobotDeath()
// - Experiment with other guns and energy management
// - Find a way to avoid doing a circle of death in the corner, mainly an issue in the final 1v1
// - Thorough testing of different settings for the tuning knobs

package d414.nano;

import robocode.*;
import robocode.util.Utils;

import java.awt.Color;

public class Quantum extends AdvancedRobot {
    // ---------------------------------------------------------------------------------------------
    // Tuning Knobs
    // ---------------------------------------------------------------------------------------------
    static final double WALL_FORCE = 1;
    static final double AGRAV_DECAY = 0.9;
    static final double AHEAD_AMOUNT = 120;

    static final double AIM_START = 10;
    static final double AIM_FACTOR = 1.008;
    static final double RADAR_LOCK_THRESHOLD = 1;
    static final int    POWER_FACTOR = 2;

    // ---------------------------------------------------------------------------------------------
    // Globals
    // ---------------------------------------------------------------------------------------------
    static String targetName;
    static double targetDistance;
    static double xForce;
    static double yForce;

    @Override
    public void run() {
        setAllColors(Color.cyan);
        setAdjustGunForRobotTurn(true);
        onRobotDeath(null);
    }

    // Moving setTurnRadarRight here, instead of in run(), saves 1 byte
    @Override
    public void onRobotDeath(RobotDeathEvent e) {
        setTurnRadarRight(targetDistance = Double.POSITIVE_INFINITY);
    }

    @Override
    public void onScannedRobot(ScannedRobotEvent e) {
        double distance; // Keep this first to save 2 bytes
        double absoluteBearing;
        double aimAngle;

        // 1. Calculate the angle DustBunny 3.8's gun would aim at
        // 2. Update rolling averages of antigravity forces, using the aim angle of DustBunny 3.8's
        //    gun instead of the absoluteBearing
        // 3. Move based on angtigravity forces
        setTurnRightRadians(Utils.normalRelativeAngle(
                    Math.atan2(
                        (xForce = xForce * AGRAV_DECAY
                         - Math.sin(
                             aimAngle = (absoluteBearing = e.getBearingRadians() + getHeadingRadians())
                             + (e.getVelocity()
                                 / (AIM_START + Math.pow(AIM_FACTOR, distance = e.getDistance()))
                               )
                             * Math.sin(e.getHeadingRadians() - absoluteBearing)
                             ) / distance
                        )
                        + calcWallForce(getX())
                        //+ calcWallForce(getX(), getBattleFieldWidth())
                        , 
                        (yForce = yForce * AGRAV_DECAY - Math.cos(aimAngle) / distance)
                        + calcWallForce(getY())
                        //+ calcWallForce(getY(), getBattleFieldHeight())
                        )
                    - getHeadingRadians()
                    ));
        setAhead(AHEAD_AMOUNT - Math.abs(getTurnRemaining()));
        
        // Use Infinity style target tracking
        if (distance < targetDistance || e.getName() == targetName) {
            targetName = e.getName();
            targetDistance = distance;

            if (getGunHeat() < RADAR_LOCK_THRESHOLD) {
                setTurnRadarLeft(getRadarTurnRemaining());
            }

            // Fire more agressively when there are more enemies.
            // With POWER_FACTOR = 2
            // 6-9 enemies: setFire(3)
            // 4-5 enemies: setFire(2)
            // 2-3 enemies: setFire(1)
            // 1v1:         setFire(0.1)
            setFire(getOthers() / POWER_FACTOR);

            setTurnGunRightRadians(Utils.normalRelativeAngle(aimAngle - getGunHeadingRadians()));
        }
    }

    // Factoring out the code for wall forces saves 8 bytes
    public static double calcWallForce(double d) {
        return (WALL_FORCE / d) - (WALL_FORCE / (1000 - d));
    }

    // Use this version of calcWallForce to remove the assumption that the battlefield is 1000x1000
    // This would cost us 6 bytes
    /*
    public static double calcWallForce(double d, double len) {
        return (WALL_FORCE / d) - (WALL_FORCE / (len - d));
    }
    */
}