Difference between revisions of "GFTargetingBot"

From Robowiki
Jump to navigation Jump to search
(Minor fixes)
 
(8 intermediate revisions by 6 users not shown)
Line 1: Line 1:
I'm new to java, and my lack of understanding Java has me hung up on this key peice of code:
+
{{Infobox Robot
  buffer = statBuffers[distanceIndex][velocityIndex][lastVelocityIndex];
+
| author          = [[PEZ]]
from my understanding;  buffer is an array of 25 (ints), each bin being a counter of times visted.
+
| extends        = [[AdvancedRobot]]
And statBuffers is a 4dimesional array of size: 5(int),  5(int), 5(int),and  25(int) :
+
| targeting      = [[GuessFactor Targeting]]
private static int[][][][] statBuffers = new int[DISTANCE_INDEXES][VELOCITY_INDEXES][VELOCITY_INDEXES][BINS];
+
| movement        = [[RandomMovement|Random Movement]], Orbit movement
so back to the problem code :
+
| current_version = 1.02
buffer = statBuffers[distanceIndex][velocityIndex][lastVelocityIndex];
+
| license        = Public domain
 +
| isOneOnOne      = true
 +
| isMelee        = false
 +
| isOpenSource    = true
 +
}}
  
so the above "indexes" (0-4) should retrieve  the values in thoses locations in the statBuffers arrays..
+
=== What's special about it? ===
Am I wrong so Far ?
+
It's a simple [[Statistical Targeting]] bot using [[GuessFactors]], if not literal so the general idea. My goal is that people who want to start out with Statistical Targeting can do so with this bot without having to figure it all out first. This bot is quite competitive out-of-the-box, but with just some little work it can be made to kick ass.
It seems to me were  assigning apples to oranges here? How does it work ? ..
 
- Justin
 
  
<code>int single[] = new int[ 5 ];</code>
+
It features:
<br><i>single</i> is "an array of integers".
+
* [[Waves]], to collect the data.
<br><code>int double[][] = new int[ 5 ][ 5 ];</code>
+
* [[Visit Count Stats]], accumulating data into simple int arrays.
<br><i>double</i> is "an array of 5 arrays of 5 integers".
+
* [[Segmentation]] (really simple, just to show how it can be done).
 +
It lacks:
 +
* [[Energy Management]]
 +
* Advanced [[segmentation]]
 +
* Gun alignment nitty-gritty
 +
* Rolling averages
 +
* [[Saving|Data saving]]
 +
* Anything advanced really
  
This means that each element in double[ n ] is itself "an array of 5 integers".
+
==== Great, I want to try it. Where can I download it? ====
 +
Compile it from the source below.
  
<br>Let's change <i>int</i> to <i>apple</i>.
+
[Note: There used to be a download link, but the link is now broken. If someone still has a copy of <code>wiki.tutorial.GFTargetingBot_1.02.jar</code>, please upload it.]
<br><code>apple double[][] = new apple[ 5 ][ 5 ];</code>
 
<br><i>double</i> is now "an array of 5 arrays of 5 apples."
 
  
This means that every double[ n ] is a container, and each container holds 5 apples.  The container is not an apple.  It is 'a container of one or more apples'.  You can visualize it as a paper bag if you like.
+
==== How competitive is it? ====
 +
Quite so for something this simple.
  
<br><code>apple triple[][][] = new apple[ 5 ][ 5 ][ 5 ];</code>
+
==== How does it [[Movement|move]]? ====
<br><i>triple</i> is "an array of 5 arrays of 5 arrays of apples" or perhaps "5 containers of 'containers of apples'".
+
Random, fluid, orbit movement. Almost verbatim [[Aristocles]] movement. (Minus [[Musashi Trick]]).
  
Going back to your original question, here is the situation:  <i>buffer</I> is 'an array of integers'.  (I deliberately did not state the size, because <i>buffer</i> can be assigned an array of any size of the same dimensions.) <i>statBuffers</i> is 'an array of arrays of arrays of arrays of integers'.  Each element in statBuffers[][][] is itself an 'array of integers', and that is why the assignment works.
+
==== How does it fire? ====
 +
A [[GuessFactor Targeting (traditional)|GuessFactor Targeting]] gun segmented on distance, velocity, last-scan-velocity.  
  
I hope that helped.  If not, best thing is to just practice with arrays it until it sinks in. -- [[User:Pedersen|Martin]]
+
==== How does it [[Dodging Bullets|dodge bullets]]? ====
 +
It tries to create a flat Movement Profile.
  
<i>Addendum:</i>  Above you mentioned "statBuffers is a 4dimesional array of size: 5(int),  5(int), 5(int),and  25(int)", and therein lies the misconception.  The "5(int)" sections are not arrays of integers.  They are "arrays of arrays".  The integers only exist in the final array. -- [[User:Pedersen|Martin]]
+
==== How does the [[Melee|melee]] strategy differ from [[OneOnOne|one-on-one]]  strategy? ====
 +
This bot doesn't know about [[Melee]] battles.
  
 +
==== What does it save between rounds and matches? ====
 +
The visit counts are saved between rounds. Nothing is saved between matches.
  
 +
==== Where did you get the name? ====
 +
Duh... It's a GuessFactor Targeting tutorial bot.
  
yes I wasn't thinking dimesionally :), in the case of GFTargengBot code: buffer ends up being the 4 dimension of statBuffer that corisponds to the first 3 dimensions..correct?  
+
==== Can I use your code? ====
-justin
+
Sure. That's the whole idea. It's included in the jar. Use it as you see fit. Of course if I do not mind credits. And here's the code for version 1.0 (meaning it might not always be up to date.):
 +
<syntaxhighlight>
 +
package wiki.tutorial;
 +
import robocode.*;
 +
import robocode.util.Utils;
 +
import java.awt.Color;
 +
import java.awt.geom.*;
 +
 
 +
// GFTargetingBot, by PEZ. A simple GuessFactorTargeting bot for tutorial purposes.
 +
// Use the code as you see fit. Of course if I do not mind credits.
 +
 
 +
public class GFTargetingBot extends AdvancedRobot {
 +
private static final double BULLET_POWER = 1.9;
 +
 +
private static double lateralDirection;
 +
private static double lastEnemyVelocity;
 +
private static GFTMovement movement;
 +
 +
public GFTargetingBot() {
 +
movement = new GFTMovement(this);
 +
}
 +
 +
public void run() {
 +
setColors(Color.BLUE, Color.BLACK, Color.YELLOW);
 +
lateralDirection = 1;
 +
lastEnemyVelocity = 0;
 +
setAdjustRadarForGunTurn(true);
 +
setAdjustGunForRobotTurn(true);
 +
do {
 +
turnRadarRightRadians(Double.POSITIVE_INFINITY);
 +
} while (true);
 +
}
 +
 +
public void onScannedRobot(ScannedRobotEvent e) {
 +
double enemyAbsoluteBearing = getHeadingRadians() + e.getBearingRadians();
 +
double enemyDistance = e.getDistance();
 +
double enemyVelocity = e.getVelocity();
 +
if (enemyVelocity != 0) {
 +
lateralDirection = GFTUtils.sign(enemyVelocity * Math.sin(e.getHeadingRadians() - enemyAbsoluteBearing));
 +
}
 +
GFTWave wave = new GFTWave(this);
 +
wave.gunLocation = new Point2D.Double(getX(), getY());
 +
GFTWave.targetLocation = GFTUtils.project(wave.gunLocation, enemyAbsoluteBearing, enemyDistance);
 +
wave.lateralDirection = lateralDirection;
 +
wave.bulletPower = BULLET_POWER;
 +
wave.setSegmentations(enemyDistance, enemyVelocity, lastEnemyVelocity);
 +
lastEnemyVelocity = enemyVelocity;
 +
wave.bearing = enemyAbsoluteBearing;
 +
setTurnGunRightRadians(Utils.normalRelativeAngle(enemyAbsoluteBearing - getGunHeadingRadians() + wave.mostVisitedBearingOffset()));
 +
setFire(wave.bulletPower);
 +
if (getEnergy() >= BULLET_POWER) {
 +
addCustomEvent(wave);
 +
}
 +
movement.onScannedRobot(e);
 +
setTurnRadarRightRadians(Utils.normalRelativeAngle(enemyAbsoluteBearing - getRadarHeadingRadians()) * 2);
 +
}
 +
}
 +
 
 +
class GFTWave extends Condition {
 +
static Point2D targetLocation;
 +
 
 +
double bulletPower;
 +
Point2D gunLocation;
 +
double bearing;
 +
double lateralDirection;
 +
 
 +
private static final double MAX_DISTANCE = 1000;
 +
private static final int DISTANCE_INDEXES = 5;
 +
private static final int VELOCITY_INDEXES = 5;
 +
private static final int BINS = 25;
 +
private static final int MIDDLE_BIN = (BINS - 1) / 2;
 +
private static final double MAX_ESCAPE_ANGLE = 0.7;
 +
private static final double BIN_WIDTH = MAX_ESCAPE_ANGLE / (double)MIDDLE_BIN;
 +
 +
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();
 +
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;
 +
}
 +
}
 +
 
 +
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));
 +
}
 +
}
 +
 
 +
class GFTMovement {
 +
private static final double BATTLE_FIELD_WIDTH = 800;
 +
private static final double BATTLE_FIELD_HEIGHT = 600;
 +
private static final double WALL_MARGIN = 18;
 +
private static final double MAX_TRIES = 125;
 +
private static final double REVERSE_TUNER = 0.421075;
 +
private static final double DEFAULT_EVASION = 1.2;
 +
private static final double WALL_BOUNCE_TUNER = 0.699484;
 +
 
 +
private AdvancedRobot robot;
 +
private Rectangle2D fieldRectangle = new Rectangle2D.Double(WALL_MARGIN, WALL_MARGIN,
 +
BATTLE_FIELD_WIDTH - WALL_MARGIN * 2, BATTLE_FIELD_HEIGHT - WALL_MARGIN * 2);
 +
private double enemyFirePower = 3;
 +
private double direction = 0.4;
 +
 
 +
GFTMovement(AdvancedRobot _robot) {
 +
this.robot = _robot;
 +
}
 +
 +
public void onScannedRobot(ScannedRobotEvent e) {
 +
double enemyAbsoluteBearing = robot.getHeadingRadians() + e.getBearingRadians();
 +
double enemyDistance = e.getDistance();
 +
Point2D robotLocation = new Point2D.Double(robot.getX(), robot.getY());
 +
Point2D enemyLocation = GFTUtils.project(robotLocation, enemyAbsoluteBearing, enemyDistance);
 +
Point2D robotDestination;
 +
double tries = 0;
 +
while (!fieldRectangle.contains(robotDestination = GFTUtils.project(enemyLocation, enemyAbsoluteBearing + Math.PI + direction,
 +
enemyDistance * (DEFAULT_EVASION - tries / 100.0))) && tries < MAX_TRIES) {
 +
tries++;
 +
}
 +
if ((Math.random() < (GFTUtils.bulletVelocity(enemyFirePower) / REVERSE_TUNER) / enemyDistance ||
 +
tries > (enemyDistance / GFTUtils.bulletVelocity(enemyFirePower) / WALL_BOUNCE_TUNER))) {
 +
direction = -direction;
 +
}
 +
// Jamougha's cool way
 +
double angle = GFTUtils.absoluteBearing(robotLocation, robotDestination) - robot.getHeadingRadians();
 +
robot.setAhead(Math.cos(angle) * 100);
 +
robot.setTurnRightRadians(Math.tan(angle));
 +
}
 +
}
 +
</syntaxhighlight>
 +
 
 +
==== What's next for your robot? ====
 +
I must document the code to make this tutorial thing to really work. You're more than welcome to help in this.
 +
 
 +
==== What other robot(s) is it based on? ====
 +
[[Aristocles]] ‒ both gun and movement
 +
 
 +
[[Category:Source Code]]
 +
[[Category:Bots]]

Latest revision as of 17:34, 18 August 2017

GFTargetingBot
Author(s) PEZ
Extends AdvancedRobot
Targeting GuessFactor Targeting
Movement Random Movement, Orbit movement
Current Version 1.02
Code License Public domain

What's special about it?

It's a simple Statistical Targeting bot using GuessFactors, if not literal so the general idea. My goal is that people who want to start out with Statistical Targeting can do so with this bot without having to figure it all out first. This bot is quite competitive out-of-the-box, but with just some little work it can be made to kick ass.

It features:

It lacks:

Great, I want to try it. Where can I download it?

Compile it from the source below.

[Note: There used to be a download link, but the link is now broken. If someone still has a copy of wiki.tutorial.GFTargetingBot_1.02.jar, please upload it.]

How competitive is it?

Quite so for something this simple.

How does it move?

Random, fluid, orbit movement. Almost verbatim Aristocles movement. (Minus Musashi Trick).

How does it fire?

A GuessFactor Targeting gun segmented on distance, velocity, last-scan-velocity.

How does it dodge bullets?

It tries to create a flat Movement Profile.

How does the melee strategy differ from one-on-one strategy?

This bot doesn't know about Melee battles.

What does it save between rounds and matches?

The visit counts are saved between rounds. Nothing is saved between matches.

Where did you get the name?

Duh... It's a GuessFactor Targeting tutorial bot.

Can I use your code?

Sure. That's the whole idea. It's included in the jar. Use it as you see fit. Of course if I do not mind credits. And here's the code for version 1.0 (meaning it might not always be up to date.):

package wiki.tutorial;
import robocode.*;
import robocode.util.Utils;
import java.awt.Color;
import java.awt.geom.*;

// GFTargetingBot, by PEZ. A simple GuessFactorTargeting bot for tutorial purposes.
// Use the code as you see fit. Of course if I do not mind credits.

public class GFTargetingBot extends AdvancedRobot {
	private static final double BULLET_POWER = 1.9;
	
	private static double lateralDirection;
	private static double lastEnemyVelocity;
	private static GFTMovement movement;
	
	public GFTargetingBot() {
		movement = new GFTMovement(this);	
	}
	
	public void run() {
		setColors(Color.BLUE, Color.BLACK, Color.YELLOW);
		lateralDirection = 1;
		lastEnemyVelocity = 0;
		setAdjustRadarForGunTurn(true);
		setAdjustGunForRobotTurn(true);
		do {
			turnRadarRightRadians(Double.POSITIVE_INFINITY); 
		} while (true);
	}
	
	public void onScannedRobot(ScannedRobotEvent e) {
		double enemyAbsoluteBearing = getHeadingRadians() + e.getBearingRadians();
		double enemyDistance = e.getDistance();
		double enemyVelocity = e.getVelocity();
		if (enemyVelocity != 0) {
			lateralDirection = GFTUtils.sign(enemyVelocity * Math.sin(e.getHeadingRadians() - enemyAbsoluteBearing));
		}
		GFTWave wave = new GFTWave(this);
		wave.gunLocation = new Point2D.Double(getX(), getY());
		GFTWave.targetLocation = GFTUtils.project(wave.gunLocation, enemyAbsoluteBearing, enemyDistance);
		wave.lateralDirection = lateralDirection;
		wave.bulletPower = BULLET_POWER;
		wave.setSegmentations(enemyDistance, enemyVelocity, lastEnemyVelocity);
		lastEnemyVelocity = enemyVelocity;
		wave.bearing = enemyAbsoluteBearing;
		setTurnGunRightRadians(Utils.normalRelativeAngle(enemyAbsoluteBearing - getGunHeadingRadians() + wave.mostVisitedBearingOffset()));
		setFire(wave.bulletPower);
		if (getEnergy() >= BULLET_POWER) {
			addCustomEvent(wave);
		}
		movement.onScannedRobot(e);
		setTurnRadarRightRadians(Utils.normalRelativeAngle(enemyAbsoluteBearing - getRadarHeadingRadians()) * 2);
	}
}

class GFTWave extends Condition {
	static Point2D targetLocation;

	double bulletPower;
	Point2D gunLocation;
	double bearing;
	double lateralDirection;

	private static final double MAX_DISTANCE = 1000;
	private static final int DISTANCE_INDEXES = 5;
	private static final int VELOCITY_INDEXES = 5;
	private static final int BINS = 25;
	private static final int MIDDLE_BIN = (BINS - 1) / 2;
	private static final double MAX_ESCAPE_ANGLE = 0.7;
	private static final double BIN_WIDTH = MAX_ESCAPE_ANGLE / (double)MIDDLE_BIN;
	
	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();
		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;
	}	
}

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

class GFTMovement {
	private static final double BATTLE_FIELD_WIDTH = 800;
	private static final double BATTLE_FIELD_HEIGHT = 600;
	private static final double WALL_MARGIN = 18;
	private static final double MAX_TRIES = 125;
	private static final double REVERSE_TUNER = 0.421075;
	private static final double DEFAULT_EVASION = 1.2;
	private static final double WALL_BOUNCE_TUNER = 0.699484;

	private AdvancedRobot robot;
	private Rectangle2D fieldRectangle = new Rectangle2D.Double(WALL_MARGIN, WALL_MARGIN,
		BATTLE_FIELD_WIDTH - WALL_MARGIN * 2, BATTLE_FIELD_HEIGHT - WALL_MARGIN * 2);
	private double enemyFirePower = 3;
	private double direction = 0.4;

	GFTMovement(AdvancedRobot _robot) {
		this.robot = _robot;
	}
	
	public void onScannedRobot(ScannedRobotEvent e) {
		double enemyAbsoluteBearing = robot.getHeadingRadians() + e.getBearingRadians();
		double enemyDistance = e.getDistance();
		Point2D robotLocation = new Point2D.Double(robot.getX(), robot.getY());
		Point2D enemyLocation = GFTUtils.project(robotLocation, enemyAbsoluteBearing, enemyDistance);
		Point2D robotDestination;
		double tries = 0;
		while (!fieldRectangle.contains(robotDestination = GFTUtils.project(enemyLocation, enemyAbsoluteBearing + Math.PI + direction,
				enemyDistance * (DEFAULT_EVASION - tries / 100.0))) && tries < MAX_TRIES) {
			tries++;
		}
		if ((Math.random() < (GFTUtils.bulletVelocity(enemyFirePower) / REVERSE_TUNER) / enemyDistance ||
				tries > (enemyDistance / GFTUtils.bulletVelocity(enemyFirePower) / WALL_BOUNCE_TUNER))) {
			direction = -direction;
		}
		// Jamougha's cool way
		double angle = GFTUtils.absoluteBearing(robotLocation, robotDestination) - robot.getHeadingRadians();
		robot.setAhead(Math.cos(angle) * 100);
		robot.setTurnRightRadians(Math.tan(angle));
	}
}

What's next for your robot?

I must document the code to make this tutorial thing to really work. You're more than welcome to help in this.

What other robot(s) is it based on?

Aristocles ‒ both gun and movement