Add polbot
This commit is contained in:
commit
cae9af4df6
239
pol/PolBot.java
Normal file
239
pol/PolBot.java
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
package pol;
|
||||||
|
|
||||||
|
import robocode.*;
|
||||||
|
import robocode.util.Utils;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
// API help : https://robocode.sourceforge.io/docs/robocode/robocode/Robot.html
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PolBot - a robot by (your name here)
|
||||||
|
*/
|
||||||
|
public class PolBot extends AdvancedRobot
|
||||||
|
{
|
||||||
|
|
||||||
|
private Map<String, ScannedRobotEvent> seenBots = new HashMap<>();
|
||||||
|
private PotentialField field;
|
||||||
|
private boolean forward = true;
|
||||||
|
private Map<String, Double> hits = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* run: PolBot's default behavior
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Initialization of the robot should be put here
|
||||||
|
|
||||||
|
// After trying out your robot, try uncommenting the import at the top,
|
||||||
|
// and the next line:
|
||||||
|
|
||||||
|
setColors(Color.red,Color.blue,Color.green); // body,gun,radar
|
||||||
|
|
||||||
|
// Robot main loop
|
||||||
|
|
||||||
|
// keep the radar still while we turn
|
||||||
|
setAdjustRadarForRobotTurn(true);
|
||||||
|
setBodyColor(new Color(128, 128, 50));
|
||||||
|
setGunColor(new Color(50, 50, 20));
|
||||||
|
setRadarColor(new Color(200, 200, 70));
|
||||||
|
setScanColor(Color.white);
|
||||||
|
setBulletColor(Color.white);
|
||||||
|
|
||||||
|
setAhead(1e100);
|
||||||
|
setTurnLeft(1e40);
|
||||||
|
|
||||||
|
// keep the gun still while we turn
|
||||||
|
setAdjustGunForRobotTurn(true);
|
||||||
|
|
||||||
|
// keep spinning that radar
|
||||||
|
setTurnRadarRightRadians(Double.POSITIVE_INFINITY);
|
||||||
|
|
||||||
|
final int width = (int)getBattleFieldWidth();
|
||||||
|
final int height = (int)getBattleFieldHeight();
|
||||||
|
field = new PotentialField(width, height);
|
||||||
|
field.addWalls(1000, 200);
|
||||||
|
field.addBlob(0, 0, 1000, 200);
|
||||||
|
field.addBlob(width, 0, 1000, 200);
|
||||||
|
field.addBlob(width, height, 1000, 200);
|
||||||
|
field.addBlob(0, height, 1000, 200);
|
||||||
|
field.addBlob(width / 2, height / 2, 500, 200);
|
||||||
|
|
||||||
|
System.out.println("Starting main loop");
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
tick();
|
||||||
|
execute();
|
||||||
|
// waitFor(new Condition() {
|
||||||
|
// public boolean test() { return true; }
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String pickATarget() {
|
||||||
|
if (seenBots.size() < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
double highest = 0;
|
||||||
|
String target = seenBots.keySet().iterator().next();
|
||||||
|
for (String bot : seenBots.keySet()) {
|
||||||
|
if (hits.get(bot) != null && hits.get(bot) > highest) {
|
||||||
|
highest = hits.get(bot);
|
||||||
|
target = bot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tick() {
|
||||||
|
// main tick
|
||||||
|
|
||||||
|
// TO DO:
|
||||||
|
// [x] track enemy
|
||||||
|
// [x] shoot
|
||||||
|
// [x] move a bit
|
||||||
|
// [ ] do periodic full scans
|
||||||
|
// [ ] estimate likelihood of hitting
|
||||||
|
// [ ] estimate enemy trajectory
|
||||||
|
// [ ] follow enemy
|
||||||
|
// [x] avoid walls
|
||||||
|
// [ ] avoid other robots
|
||||||
|
// [ ] select enemy at not-random
|
||||||
|
|
||||||
|
aimAndShoot();
|
||||||
|
moveAround();
|
||||||
|
}
|
||||||
|
|
||||||
|
private double[] computeVel() {
|
||||||
|
final double vel = getVelocity();
|
||||||
|
final double heading = Math.PI / 2 - getHeadingRadians();
|
||||||
|
|
||||||
|
return new double[]{Math.cos(heading) * vel, Math.sin(heading) * vel};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void moveAround() {
|
||||||
|
final double[] v = computeVel();
|
||||||
|
final double[] newV = field.dv((int)getX(), (int)getY(), v[0], v[1]);
|
||||||
|
|
||||||
|
final double newVx = newV[0];
|
||||||
|
final double newVy = newV[1];
|
||||||
|
|
||||||
|
final double vel = Math.sqrt(newVx * newVx + newVy * newVy);
|
||||||
|
final double heading = Math.atan2(newVy, newVx);
|
||||||
|
final double turnRight = (Math.PI / 2 - heading) - getHeadingRadians();
|
||||||
|
|
||||||
|
if (Math.abs(vel) > 1.0) {
|
||||||
|
setAhead(vel * 5);
|
||||||
|
}
|
||||||
|
setTurnRightRadians(turnRight * 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param target
|
||||||
|
* @return the adjusted relative bearing for the given target (radians)
|
||||||
|
*/
|
||||||
|
private double aim(ScannedRobotEvent target, double power) {
|
||||||
|
final double heading = Math.PI / 2 - (target.getHeadingRadians() - getHeadingRadians());
|
||||||
|
final double bearing = Math.PI / 2 - target.getBearingRadians();
|
||||||
|
|
||||||
|
final double x = Math.cos(bearing) * target.getDistance();
|
||||||
|
final double y = Math.sin(bearing) * target.getDistance();
|
||||||
|
|
||||||
|
final double bulletSpeed = Rules.getBulletSpeed(power);
|
||||||
|
final double hitTime = target.getDistance() / bulletSpeed;
|
||||||
|
|
||||||
|
final double nextX = Math.cos(heading) * 1.0 * hitTime * target.getVelocity() + x;
|
||||||
|
final double nextY = Math.sin(heading) * 1.0 * hitTime * target.getVelocity() + y;
|
||||||
|
|
||||||
|
System.out.println("(" + x + ", " + y + ") -> (" + nextX + ", " + nextY + ")");
|
||||||
|
|
||||||
|
return Math.PI / 2 - Math.atan2(nextY, nextX);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void aimAndShoot() {
|
||||||
|
final String target = pickATarget();
|
||||||
|
if (target == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final ScannedRobotEvent enemy = seenBots.get(target);
|
||||||
|
// do a full scan if the next target hasn't been seen for a while
|
||||||
|
if (enemy.getTime() < getTime() - 10) {
|
||||||
|
setTurnRadarRight(1e10);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final double distance = enemy.getDistance();
|
||||||
|
final double power = 1 + 9 / (distance / 10);
|
||||||
|
if (power < 1.1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (getEnergy() < 15 && distance > 200) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final double seenBearing = enemy.getBearing();
|
||||||
|
final double aimBearing = 180 / Math.PI * aim(enemy, power);
|
||||||
|
final double radarBearing = Utils.normalRelativeAngleDegrees(getRadarHeading() - getHeading());
|
||||||
|
final double gunBearing = Utils.normalRelativeAngleDegrees(getGunHeading() - getHeading());
|
||||||
|
final double dgh = Utils.normalRelativeAngleDegrees(aimBearing - gunBearing);
|
||||||
|
final double drh = Utils.normalRelativeAngleDegrees(seenBearing - radarBearing);
|
||||||
|
|
||||||
|
setTurnGunRight(dgh);
|
||||||
|
setTurnRadarRight(drh);
|
||||||
|
|
||||||
|
if (Math.abs(dgh) < 5) {
|
||||||
|
setFire(power);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRobotDeath(RobotDeathEvent event) {
|
||||||
|
seenBots.remove(event.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* onScannedRobot: What to do when you see another robot
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onScannedRobot(ScannedRobotEvent e) {
|
||||||
|
// Replace the next line with any behavior you would like
|
||||||
|
seenBots.put(e.getName(), e);
|
||||||
|
System.out.println("seen " + e.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reverse() {
|
||||||
|
if (forward) {
|
||||||
|
forward = false;
|
||||||
|
setAhead(-1e100);
|
||||||
|
} else {
|
||||||
|
forward = true;
|
||||||
|
setAhead(1e100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* onHitByBullet: What to do when you're hit by a bullet
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onHitByBullet(HitByBulletEvent e) {
|
||||||
|
// Replace the next line with any behavior you would like
|
||||||
|
reverse();
|
||||||
|
|
||||||
|
// keep a grudge
|
||||||
|
if (hits.get(e.getName()) == null) {
|
||||||
|
hits.put(e.getName(), 0.0);
|
||||||
|
}
|
||||||
|
hits.put(e.getName(), hits.get(e.getName()) + e.getPower());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* onHitWall: What to do when you hit a wall
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onHitWall(HitWallEvent e) {
|
||||||
|
// Replace the next line with any behavior you would like
|
||||||
|
reverse();
|
||||||
|
}
|
||||||
|
}
|
106
pol/PotentialField.java
Normal file
106
pol/PotentialField.java
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package pol;
|
||||||
|
|
||||||
|
public class PotentialField {
|
||||||
|
private final double[] potential;
|
||||||
|
private final int width;
|
||||||
|
private final int height;
|
||||||
|
|
||||||
|
public PotentialField(final int width, final int height) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
potential = new double[width * height];
|
||||||
|
}
|
||||||
|
|
||||||
|
public double value(final int x, final int y) {
|
||||||
|
return potential[x * height + y];
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[] gradient(final int x, final int y) {
|
||||||
|
double gx;
|
||||||
|
double gy;
|
||||||
|
|
||||||
|
if (x < 1) {
|
||||||
|
gx = value(x, y);
|
||||||
|
} else {
|
||||||
|
gx = value(x, y) - value(x - 1, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y < 1) {
|
||||||
|
gy = value(x, y);
|
||||||
|
} else {
|
||||||
|
gy = value(x, y) - value(x, y - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new double[]{gx, gy};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the force from the potential field
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param vx
|
||||||
|
* @param vy
|
||||||
|
* @return the new speed, assuming unit mass, time, and everything.
|
||||||
|
*/
|
||||||
|
public double[] dv(final int x, final int y, final double vx, final double vy) {
|
||||||
|
final double[] grad = gradient(x, y);
|
||||||
|
return new double[]{vx - grad[0], vy - grad[1]};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a bell-shaped blob somewhere
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param value
|
||||||
|
* @param size the standard deviation of the blob
|
||||||
|
*/
|
||||||
|
public void addBlob(final int x, final int y, final double value, final double size) {
|
||||||
|
for (int i = 0; i < width; i++) {
|
||||||
|
final int mx = i - x;
|
||||||
|
for (int j = 0; j < height; j++) {
|
||||||
|
final int my = j - y;
|
||||||
|
final double v = value * Math.exp(-0.5 * (mx*mx + my*my) / (size*size));
|
||||||
|
potential[i * height + j] += v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
for (int i = 0; i < width; i++) {
|
||||||
|
for (int j = 0; j < height; j++) {
|
||||||
|
potential[i * height + j] = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double wallPotential(double x, double value, double size) {
|
||||||
|
return value / ((x / size + 1) * (x / size + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private double wallValue(int x, int y, double value, double size) {
|
||||||
|
// 4 regions: top, right, bottom, left; defined by two intersecting diagonals
|
||||||
|
final double a = height * x - width * y;
|
||||||
|
final double b = height * x + width * y - width * height;
|
||||||
|
|
||||||
|
if (a > 0 && b > 0) { // right
|
||||||
|
return wallPotential(width - x, value, size);
|
||||||
|
} else if (a > 0 && b <= 0) { // bottom
|
||||||
|
return wallPotential(y, value, size);
|
||||||
|
} else if (a <= 0 && b <= 0) { // left
|
||||||
|
return wallPotential(x, value, size);
|
||||||
|
} else {
|
||||||
|
return wallPotential(height - y, value, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add half-bells ish lines along walls
|
||||||
|
*/
|
||||||
|
public void addWalls(final double value, final double size) {
|
||||||
|
for (int i = 0; i < width; i++) {
|
||||||
|
for (int j = 0; j < height; j++) {
|
||||||
|
potential[i * height + j] += wallValue(i, j, value, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
64
pol/PotentialFieldTest.java
Normal file
64
pol/PotentialFieldTest.java
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package pol;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class PotentialFieldTest {
|
||||||
|
@Test
|
||||||
|
public void testAddBlob() {
|
||||||
|
final PotentialField pf = new PotentialField(4, 5);
|
||||||
|
|
||||||
|
pf.addBlob(2, 3, 1, 1);
|
||||||
|
|
||||||
|
final double v = pf.value(2, 3);
|
||||||
|
assertEquals(1.0, v, 1e-10);
|
||||||
|
|
||||||
|
final double[] dv = pf.dv(3, 4, 0, 0);
|
||||||
|
assertTrue("dv[0] goes down gradient", dv[0] > 0);
|
||||||
|
assertTrue("dv[1] goes down gradient", dv[1] > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddWalls() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClear() {
|
||||||
|
final PotentialField pf = new PotentialField(4, 5);
|
||||||
|
|
||||||
|
pf.addBlob(2, 3, 1, 1);
|
||||||
|
assertEquals(1.0, pf.value(2, 3), 1e-10);
|
||||||
|
|
||||||
|
pf.clear();
|
||||||
|
|
||||||
|
assertEquals(0, pf.value(2, 3), 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDv() {
|
||||||
|
final PotentialField pf = new PotentialField(4, 5);
|
||||||
|
|
||||||
|
final double[] dv = pf.dv(2, 3, 50, 40);
|
||||||
|
assertEquals(50.0, dv[0], 1e-10);
|
||||||
|
assertEquals(40.0, dv[1], 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGradient() {
|
||||||
|
final PotentialField pf = new PotentialField(4, 5);
|
||||||
|
|
||||||
|
final double[] g = pf.gradient(2, 3);
|
||||||
|
assertEquals(0.0, g[0], 1e-10);
|
||||||
|
assertEquals(0.0, g[1], 1e-10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValue() {
|
||||||
|
final PotentialField pf = new PotentialField(4, 5);
|
||||||
|
|
||||||
|
assertEquals(0, pf.value(0, 0), 1e-10);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user