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