|
|
(12 intermediate revisions by the same user not shown) |
Line 3: |
Line 3: |
| | parent = Basilisk | | | parent = Basilisk |
| | page1 = Version History | | | page1 = Version History |
| + | | page2 = Code |
| }} | | }} |
| | | |
Line 8: |
Line 9: |
| | author = [[User:Slugzilla]] | | | author = [[User:Slugzilla]] |
| | extends = [[AdvancedRobot]] | | | extends = [[AdvancedRobot]] |
− | | targeting = Guessfactor Targeting | + | | targeting = [[GuessFactor Targeting]] |
− | | movement = Stop and go and Random Movement | + | | movement = [[Stop And Go]], [[Random Movement]] |
− | | current_version = 2.5 | + | | current_version = 2.35 |
| | released = 2019 | | | released = 2019 |
| | license = [[RWPCL]] | | | license = [[RWPCL]] |
− | | download_link = [https://drive.google.com/uc?export=download&id=1TBpIPIr8Mkf55gg-1zLxFsFKl_lT49J5] | + | | download_link = [https://drive.google.com/uc?export=download&id=16-Cts6GGsFpUqmavKflMbMd5dr6SGTvw Basilisk 2.35] |
| | isOpenSource = true | | | isOpenSource = true |
| | isMelee = false | | | isMelee = false |
Line 28: |
Line 29: |
| '''How competitive is it?''' | | '''How competitive is it?''' |
| | | |
− | Currently #22 in the MiniRumble and 133 in the RoboRumble. | + | Currently #5 in the MiniRumble and #72 in the RoboRumble. |
| | | |
| == Strategy == | | == Strategy == |
Line 34: |
Line 35: |
| '''How does it move?''' | | '''How does it move?''' |
| | | |
− | It starts with stop and go, but if it gets hit too much, it will switch to random movement.
| + | Basilisk starts out with [[Stop And Go]], but if it gets hit often, it will switch over to [[Random Movement]]. |
| | | |
| '''How does it fire?''' | | '''How does it fire?''' |
| | | |
− | It uses guessfactor targeting. | + | Basilisk uses [[GuessFactor Targeting]]. It's based off of [[GFTargetingBot]], but has [[Energy Management]], [[Rolling Averages]], and a lot of [[Segmentation]]. |
| | | |
| '''How does it dodge bullets?''' | | '''How does it dodge bullets?''' |
| | | |
− | Stop and go dodges the simple targeters, random movement dodges the rest =) | + | Stop and go dodges [[HOT]] and [[Linear Targeting]], an [[Random Movement]] dodges the rest =) |
| | | |
| '''How does the melee strategy differ from one-on-one strategy?''' | | '''How does the melee strategy differ from one-on-one strategy?''' |
Line 56: |
Line 57: |
| '''Can I use your code?''' | | '''Can I use your code?''' |
| | | |
− | Sure! It's in the .jar file [https://drive.google.com/uc?export=download&id=1TBpIPIr8Mkf55gg-1zLxFsFKl_lT49J5 Basilisk] [[RWPCL]]. | + | Sure! See code below or decompile the .jar file. |
| | | |
| '''What's next for your robot?''' | | '''What's next for your robot?''' |
| | | |
− | Top 10!!!
| + | A baby brother, maybe [[Basilite]]? |
| | | |
| '''Does it have any White Whales?''' | | '''Does it have any White Whales?''' |
| | | |
− | [[Partial]] is it's sworn enemy!!! | + | [[Partial]] is it's arch-nemesis!!! |
| | | |
| '''What other robot(s) is it based on?''' | | '''What other robot(s) is it based on?''' |
Line 70: |
Line 71: |
| Credits: | | Credits: |
| | | |
− | Based off of RandomMovementBot and the GFTargetingBot by PEZ
| + | Basilisk is based off of many ideas and concepts from the RoboWiki. Specifically, I used code/drew inspiration from |
| | | |
− | Uses the Turn Multiplier Lock from the RoboWiki
| + | [[RandomMovementBot]], [[GFTargetingBot]], [[Aristocles]], [[RaikoNano]], [[Tityus]], [[Raiko]], [[Cotillion]], [[BlackWidow]], [[HedgehogGF]], [[Vyper]], [[Thorn]], and [[EpeeistMicro]]. |
| | | |
− | Dive protection is from by Tityus
| + | Special thanks to Dsekercioglu for help identifying bugs and beating bullet shielders =) |
| | | |
− | The favorite distance idea was inspired by Raiko
| |
− |
| |
− | Uses Raiko's reverse direction formula
| |
− |
| |
− | Stop and go at the beginning and then swapping to random movement was inspired by Cotillion and BlackWidow
| |
− |
| |
− | Improved mode swaps from EpeeistMicro and Cotillion
| |
− |
| |
− | Uses Cotillions stop and go move amount formula
| |
− |
| |
− | Special thanks to Dsekercioglu for giving me ideas for beating the bullet shielders, and help identifying bugs
| |
− |
| |
− | And of course, a huge thank you to all the contributors on the RoboWiki for your awesome tutorials and open source robots!
| |
− |
| |
− | == Code ==
| |
− |
| |
− | '''Version 2.14'''
| |
− |
| |
− | <syntaxhighlight>
| |
− | package slugzilla;
| |
− | import robocode.*;
| |
− | //import robocode.util.Utils;
| |
− | import java.awt.geom.*;
| |
− | import robocode.util.*;
| |
− | import java.awt.*;
| |
− | import static robocode.util.Utils.normalRelativeAngleDegrees;
| |
− |
| |
− | public class Basilisk extends AdvancedRobot {
| |
− |
| |
− | private static double lateralDirection;
| |
− | private static double lastEnemyVelocity;
| |
− | static final double MAX_VELOCITY = 8;
| |
− | static final double WALL_MARGIN = 25;
| |
− | // static double alpha;
| |
− | static double theta;
| |
− | static double enemyFirePower;
| |
− | static double enemyBulletVelocity;
| |
− | Point2D robotLocation;
| |
− | Point2D enemyLocation;
| |
− | double enemyDistance;
| |
− | double enemyAbsoluteBearing;
| |
− | double enemyVelocity;
| |
− | double movementLateralAngle;
| |
− | double direction = 1;
| |
− | static int bulletCount;
| |
− | static double hits;
| |
− | static double prevEnergy = 100.0;
| |
− | static double enemyEnergy;
| |
− | static double deltaT;
| |
− | static double lastDeltaT;
| |
− | static double lastReverseTime;
| |
− | public void run() {
| |
− | setAdjustRadarForGunTurn(true);
| |
− | setAdjustGunForRobotTurn(true);
| |
− | setBodyColor(new Color(40, 100, 100));
| |
− | setGunColor(new Color(34, 50, 50));
| |
− | setRadarColor(new Color(0, 255, 0));
| |
− | setScanColor(new Color(0, 255, 0));
| |
− | turnRadarRightRadians(Double.POSITIVE_INFINITY);
| |
− | do {
| |
− | scan();
| |
− | } while (true);
| |
− | }
| |
− |
| |
− | public void onScannedRobot(ScannedRobotEvent e) {
| |
− | //radar ↓
| |
− | double radarTurn = getHeadingRadians() + e.getBearingRadians() - getRadarHeadingRadians();
| |
− |
| |
− | //movement ↓
| |
− | robotLocation = new Point2D.Double(getX(), getY());
| |
− | enemyAbsoluteBearing = getHeadingRadians() + e.getBearingRadians();
| |
− | enemyDistance = e.getDistance();
| |
− | enemyVelocity = e.getVelocity();
| |
− | enemyEnergy = e.getEnergy();
| |
− | enemyLocation = vectorToLocation(enemyAbsoluteBearing, enemyDistance, robotLocation);
| |
− | enemyBulletVelocity = 20 - 3*enemyFirePower;
| |
− |
| |
− | //alpha = 1/(enemyDistance/19);
| |
− | //we move far away when close, but stabilize and move perpendicular when far ↓
| |
− | movementLateralAngle = Math.max(enemyDistance/2000, 0.075);
| |
− |
| |
− | if (prevEnergy > enemyEnergy) {
| |
− | enemyFirePower = prevEnergy - enemyEnergy;
| |
− | }
| |
− | move();
| |
− | prevEnergy = e.getEnergy();
| |
− | //gun ↓
| |
− |
| |
− | //to beat the bullet shielders ↓
| |
− | double wiggle;
| |
− | wiggle = ((Math.random()/500)-0.002);
| |
− |
| |
− | if (enemyVelocity != 0) {
| |
− | lateralDirection = GFTUtils.sign(enemyVelocity * Math.sin(e.getHeadingRadians() - enemyAbsoluteBearing));
| |
− | }
| |
− | GFTWave wave = new GFTWave(this);
| |
− |
| |
− | wave.bulletPower = Math.min(e.getEnergy()/4, Math.min(1.5+200/enemyDistance, getEnergy()/8));
| |
− | wave.MAX_ESCAPE_ANGLE = Math.asin(8/(20-3*wave.bulletPower));
| |
− | wave.BIN_WIDTH = wave.MAX_ESCAPE_ANGLE / (double)wave.MIDDLE_BIN;
| |
− | wave.gunLocation = new Point2D.Double(getX(), getY());
| |
− | GFTWave.targetLocation = GFTUtils.project(wave.gunLocation, enemyAbsoluteBearing, enemyDistance);
| |
− | wave.lateralDirection = lateralDirection;
| |
− | wave.setSegmentations(enemyDistance, enemyVelocity, lastEnemyVelocity);
| |
− | lastEnemyVelocity = enemyVelocity;
| |
− | wave.bearing = enemyAbsoluteBearing;
| |
− | setTurnGunRightRadians(Utils.normalRelativeAngle(enemyAbsoluteBearing - getGunHeadingRadians() + wave.mostVisitedBearingOffset()) + wiggle);
| |
− | if (getEnergy() > 0.4) {
| |
− | //fire at will! ↓
| |
− | setFire(wave.bulletPower);
| |
− | }
| |
− | if (getEnergy() >= wave.bulletPower) {
| |
− | addCustomEvent(wave);
| |
− | }
| |
− | //radar ↓
| |
− | setTurnRadarRightRadians(Utils.normalRelativeAngle(radarTurn));
| |
− | }
| |
− | //movement ↓
| |
− | void move() {
| |
− | if (bulletCount >= 1) {
| |
− | considerChangingDirection();
| |
− | }
| |
− | for (int i = 0; i < 2; i++) {
| |
− | Point2D robotDestination = null;
| |
− | double tries = 0;
| |
− | do {
| |
− | robotDestination = vectorToLocation(absoluteBearing(enemyLocation, robotLocation) + movementLateralAngle*direction,
| |
− | enemyDistance * (1.1 - tries / 125.0), enemyLocation);
| |
− | tries++;
| |
− | } while (tries < 100 && !fieldRectangle(WALL_MARGIN).contains(robotDestination));
| |
− | goTo(robotDestination);
| |
− | if (tries < 27 + i * 100 || movementLateralAngle > 0.5) {
| |
− | break;
| |
− | }
| |
− | // dive protection ↓
| |
− | direction *= -1;
| |
− | lastDeltaT = deltaT;
| |
− | lastReverseTime = getTime();
| |
− | }
| |
− | }
| |
− | //random movement ↓
| |
− | void considerChangingDirection() {
| |
− | deltaT = getTime() - lastReverseTime;
| |
− | // if ((Math.random()*.92) > .96*Math.pow(alpha, alpha)) {
| |
− | // if (Math.random() < 1 - (Math.pow(alpha, alpha))) {
| |
− | // if (Math.random() < 2.5/(enemyDistance/enemyBulletVelocity)) {
| |
− | theta = 0.5952*(20D - 3D*enemyFirePower)/enemyDistance;
| |
− | if (Math.random() > Math.pow(theta, theta)) {
| |
− | if (deltaT >= lastDeltaT +2 || deltaT <= lastDeltaT -2) {
| |
− | direction *= -1;
| |
− | lastDeltaT = deltaT;
| |
− | lastReverseTime = getTime();
| |
− | }
| |
− | }
| |
− | }
| |
− | RoundRectangle2D fieldRectangle(double margin) {
| |
− | return new RoundRectangle2D.Double(margin, margin,
| |
− | getBattleFieldWidth() - margin * 2, getBattleFieldHeight() - margin * 2, 75, 75);
| |
− | }
| |
− |
| |
− | void goTo(Point2D destination) {
| |
− | double angle = Utils.normalRelativeAngle(absoluteBearing(robotLocation, destination) - getHeadingRadians());
| |
− | double turnAngle = Math.atan(Math.tan(angle));
| |
− | setTurnRightRadians(turnAngle);
| |
− |
| |
− | //random movement ↓
| |
− | if (bulletCount >= 1) {
| |
− | // setAhead(robotLocation.distance(destination) * (angle == turnAngle ? 1 : -1));
| |
− | setAhead(Double.POSITIVE_INFINITY * (angle == turnAngle ? 1 : -1));
| |
− | //stop and go ↓
| |
− | } else {
| |
− | if (prevEnergy > enemyEnergy) {
| |
− | //enemyFirePower = prevEnergy - enemyEnergy;
| |
− | setAhead(((3 + (int)(enemyFirePower * 1.999999)) << 3 ) * (angle == turnAngle ? 1 : -1));
| |
− | }
| |
− | prevEnergy = enemyEnergy;
| |
− | }
| |
− | // Hit the brake pedal hard if we need to turn sharply
| |
− | setMaxVelocity(Math.abs(getTurnRemaining()) > 33 ? 2 : MAX_VELOCITY);
| |
− | }
| |
− |
| |
− | static Point2D vectorToLocation(double angle, double length, Point2D sourceLocation) {
| |
− | return vectorToLocation(angle, length, sourceLocation, new Point2D.Double());
| |
− | }
| |
− |
| |
− | static Point2D vectorToLocation(double angle, double length, Point2D sourceLocation, Point2D targetLocation) {
| |
− | targetLocation.setLocation(sourceLocation.getX() + Math.sin(angle) * length,
| |
− | sourceLocation.getY() + Math.cos(angle) * length);
| |
− | return targetLocation;
| |
− | }
| |
− |
| |
− | static double absoluteBearing(Point2D source, Point2D target) {
| |
− | return Math.atan2(target.getX() - source.getX(), target.getY() - source.getY());
| |
− | }
| |
− |
| |
− | public void onHitByBullet(HitByBulletEvent e) {
| |
− | enemyEnergy = prevEnergy += (3*enemyFirePower);
| |
− | //enemyEnergy += (3*enemyFirePower);
| |
− |
| |
− | if ((hits += (4.25 / enemyBulletVelocity)) > getRoundNum() + 1) {
| |
− | bulletCount++;
| |
− | }
| |
− | }
| |
− |
| |
− | public void onBulletHit(BulletHitEvent e) {
| |
− | enemyEnergy = prevEnergy -= ((4*enemyFirePower) + Math.max(2*(enemyFirePower-1), 0));
| |
− | // enemyEnergy -= ((4*enemyFirePower) + Math.max(2*(enemyFirePower-1), 0));
| |
− | }
| |
− | }
| |
− | //gun ↓
| |
− | class GFTWave extends Condition {
| |
− | static Point2D targetLocation;
| |
− | double bulletPower;
| |
− | Point2D gunLocation;
| |
− | double bearing;
| |
− | double lateralDirection;
| |
− | // 1000 is the maximum space away in a 800 by 600 battlefield
| |
− | private static final double MAX_DISTANCE = 1000;
| |
− | //segment into 5 sections ↓
| |
− | private static final int DISTANCE_INDEXES = 5;
| |
− | private static final int VELOCITY_INDEXES = 5;
| |
− | private static final int BINS = 25;
| |
− | static final int MIDDLE_BIN = (BINS - 1) / 2;
| |
− | static double MAX_ESCAPE_ANGLE;
| |
− | static double BIN_WIDTH;
| |
− |
| |
− | private static int[][][][] statBuffers = new int[DISTANCE_INDEXES][VELOCITY_INDEXES][VELOCITY_INDEXES][BINS];
| |
− |
| |
− | private int[] buffer;
| |
− | private AdvancedRobot robot;
| |
− | private double distanceTraveled;
| |
− |
| |
− | GFTWave(AdvancedRobot _robot) {
| |
− | this.robot = _robot;
| |
− | }
| |
− | public boolean test() {
| |
− | advance();
| |
− | //remove passed waves
| |
− | if (hasArrived()) {
| |
− | buffer[currentBin()]++;
| |
− | robot.removeCustomEvent(this);
| |
− | }
| |
− | return false;
| |
− | }
| |
− | double mostVisitedBearingOffset() {
| |
− | return (lateralDirection * BIN_WIDTH) * (mostVisitedBin() - MIDDLE_BIN);
| |
− | }
| |
− | void setSegmentations(double distance, double velocity, double lastVelocity) {
| |
− | int distanceIndex = (int)(distance / (MAX_DISTANCE / DISTANCE_INDEXES));
| |
− | int velocityIndex = (int)Math.abs(velocity / 2);
| |
− | int lastVelocityIndex = (int)Math.abs(lastVelocity / 2);
| |
− |
| |
− | buffer = statBuffers[distanceIndex][velocityIndex][lastVelocityIndex];
| |
− | }
| |
− | private void advance() {
| |
− | distanceTraveled += GFTUtils.bulletVelocity(bulletPower);
| |
− | }
| |
− | private boolean hasArrived() {
| |
− | return distanceTraveled > gunLocation.distance(targetLocation) - 18;
| |
− | }
| |
− | private int currentBin() {
| |
− | int bin = (int)Math.round(((Utils.normalRelativeAngle(GFTUtils.absoluteBearing(gunLocation, targetLocation) - bearing)) /
| |
− | (lateralDirection * BIN_WIDTH)) + MIDDLE_BIN);
| |
− | return GFTUtils.minMax(bin, 0, BINS - 1);
| |
− | }
| |
− | private int mostVisitedBin() {
| |
− | int mostVisited = MIDDLE_BIN;
| |
− | for (int i = 0; i < BINS; i++) {
| |
− | if (buffer[i] > buffer[mostVisited]) {
| |
− | mostVisited = i;
| |
− | }
| |
− | }
| |
− | return mostVisited;
| |
− | }
| |
− | }
| |
− | //helpful utilities ↓
| |
− | class GFTUtils {
| |
− | static double bulletVelocity(double power) {
| |
− | return 20 - 3 * power;
| |
− | }
| |
− | static Point2D project(Point2D sourceLocation, double angle, double length) {
| |
− | return new Point2D.Double(sourceLocation.getX() + Math.sin(angle) * length,
| |
− | sourceLocation.getY() + Math.cos(angle) * length);
| |
− | }
| |
− | static double absoluteBearing(Point2D source, Point2D target) {
| |
− | return Math.atan2(target.getX() - source.getX(), target.getY() - source.getY());
| |
− | }
| |
− | static int sign(double v) {
| |
− | return v < 0 ? -1 : 1;
| |
− | }
| |
− | static int minMax(int v, int min, int max) {
| |
− | return Math.max(min, Math.min(max, v));
| |
− | }
| |
− | }
| |
− | </syntaxhighlight>
| |
| [[Category:MiniBots]] | | [[Category:MiniBots]] |
| [[Category:Bots]] | | [[Category:Bots]] |
| [[Category:1-vs-1 Bots]] | | [[Category:1-vs-1 Bots]] |