Foilist/Code
< Foilist
Jump to navigation
Jump to search
/*
Foilist v1.3.1 by Sheldor. 03/29/2025 1497 bytes
a minibot with wave surfing and pattern matching
v1.3.1 -- movement, energy management
Foil is one of the three forms of modern sport fencing,
along with épée and sabre. https://en.wikipedia.org/wiki/Foil_%28fencing%29
Credits:
Movement : jk.mini.CunobelinDC 1.2, simonton.mini.WeeksOnEnd 1.10.4, kc.micro.WaveShark 0.5,
kc.mega.BeepBoop 2.0, kc.serpent.Hydra
Targeting: kc.micro.WaveShark 0.5, jk.micro.Cotillion 0.8, pez.mini.Pugilist 2.5.1f
Also, a general thanks to all open source bot authors and contributors to the RoboWiki.
Foilist is open source and released under the terms of the RoboWiki Public Code License (RWPCL) - Version 1.1.
see license here: https://robowiki.net/wiki/RWPCL
*/
package sheldor.mini;
import robocode.*;
import robocode.util.Utils;
import java.awt.geom.*; // for Point2D's
import java.lang.*; // for Double and Integer objects
import java.util.ArrayList; // for collection of Waves
import java.awt.*;
import java.util.*;
public class Foilist extends AdvancedRobot {
static final int GUESS_FACTORS = 100;
static final int MIDDLE_FACTOR = 16;
private static final int MAX_BFT = 70;
private static final int MAX_MATCH_LENGTH = 40;
private static final double WALL_FEATURE = 0.17;//0.45;
private static final int SCAN_LENGTH = 48;
private static final int VELOCITY_WEIGHT = (SCAN_LENGTH * 3) / 2;
private static final int VELOCITY_VALUE = 1;
private static final int WEIGHTS = 5;
private static final int WEIGHT_LENGTH = 8;
private static final int SCANS = 127;
// static ArrayList[] hitScans = new ArrayList[10];
// static int[] indices = new int[9];
static ArrayList hitScans = new ArrayList();
public static Point2D.Double myLocation;
public static Point2D.Double enemyLocation;
public static ArrayList enemyWaves;
public static ArrayList pastWaves = new ArrayList();;
public static double enemyEnergy;
public static final double WALL_STICK = 160;
static double lastLatVel;
static double lastAdvVel;
static double previousLastLatVel;
static double lastPreviousLastLatVel;
static double lastLatVelChange;
static double waveCount;
static double velocity;
static double lastVelocity;
static int ticksSinceVelocityChange;
static int previousTicksSinceVelocityChange;
static int ticksSinceHit;
// static double[] weightScores = new double[WEIGHTS];
static int weightIndex;
static double powerSinceLastHit;
static double hits;
static double decay;
static int deathCount;
static double threshold;
static double directionDistance;
static double previousDirectionDistance;
static double lastFactor;
static ScannedRobotEvent e;
public void run() {
setAdjustGunForRobotTurn(true);
setAdjustRadarForGunTurn(true);
enemyWaves = new ArrayList();
setColors(java.awt.Color.white, java.awt.Color.black, java.awt.Color.lightGray);
}
public void onStatus(StatusEvent e){
setTurnRadarRightRadians(1);
}
public void onScannedRobot(ScannedRobotEvent e) {
int i;
double absoluteBearing;
double enemyDistance;
double tempIndex;
double bearing;
double latVel;
double bulletPower;
Wave ew;
//wave surfing based on CunobelinDC's
(ew = new Wave()).direction = (//direction =
(int)Math.signum(//(direction / 100.0) +
(//history[--historyIndex] =
(latVel = (getVelocity()*Math.sin(bearing = e.getBearingRadians())))) + 1E-10));
// double advVel = (getVelocity()*Math.cos(absoluteBearing));
setTurnRadarRightRadians(Utils.normalRelativeAngle((absoluteBearing = (bearing) + getHeadingRadians()) - getRadarHeadingRadians()) * 2);
pastWaves.add(0,ew);
addWave(enemyEnergy - (enemyEnergy = e.getEnergy()));
// update after EnemyWave detection, because that needs the previous
// enemy location as the source of the Wave
enemyLocation = project((myLocation = new Point2D.Double(getX(), getY())), (ew.directAngle = absoluteBearing), (enemyDistance = e.getDistance()));
updateWaves();
if (velocity != (velocity = getVelocity())
// if (getVelocity() == 0
){
//previousTicksSinceVelocityChange = ticksSinceVelocityChange;
ticksSinceVelocityChange = 0;//deceleration;
}
double distance;
double[] localScan;
(localScan = (ew.scan = new double[WEIGHT_LENGTH]))[1] = lastLatVel;
localScan[2] = 1.34 * //10 *
(lastLatVel = Math.abs(latVel));
localScan[3] = ((distance = enemyDistance));
localScan[5] = ++ticksSinceVelocityChange;//Math.cbrt(--ticksSinceVelocityChange);//VELOCITY_HISTORY_TABLE.charAt(ticksSinceVelocityChange += 3);
localScan[7] = velocity*
Math.cos(bearing);
i = 25;
do{
localScan[5 - Integer.signum(--i)] += (fieldContains(enemyLocation, (absoluteBearing + Math.PI) + (ew.direction * (i * 0.04)), enemyDistance));
}while(i > -25);
// int span = 2 + (400 / (int)enemyDistance);
double lowest = Double.POSITIVE_INFINITY;
tempIndex = (i = (2 + (2 * (480 / (int)enemyDistance))));
do {
double angle;
double test;
if ((test = checkDanger(
angle = ((short)ANGLE_TABLE.charAt(i) / 10.0)//Math.cbrt((i * 8))//6.375
//5//((35 / Math.cbrt(enemyDistance)))
)) < lowest){
tempIndex = angle;
lowest = test;
}
}
while (i-- > 0);//(--i >= -2);
setAhead(0);
if(tempIndex != 0){
double goAngle;
setAhead(Math.cos(goAngle =
wallSmoothing(myLocation,
absoluteBearing,
tempIndex)
- getHeadingRadians()) / 0);
setTurnRightRadians(Math.tan(goAngle));
}
//calculate wall proximity
//possibly inspired by Pugilist
int wallScore = 0;
i = 24;
do{
wallScore += fieldContains(myLocation, absoluteBearing + (4.29//(60.0 / bulletVelocity)
/ (short)WALL_TABLE.charAt(--i)), enemyDistance);
} while(i > 0);
//pattern matching based on kc.micro.WaveShark's
// Update log of enemy movements and pattern match
enemyHistory = String.valueOf(
(char)(
((int)Math.round((e.getVelocity() *
Math.sin(e.getHeadingRadians() - absoluteBearing))
//+ (Math.random() - 0.5)
) << 5)
| wallScore//(fieldContains(absoluteBearing + WALL_FEATURE, enemyDistance) << 1)
// | fieldContains(absoluteBearing - WALL_FEATURE, enemyDistance)
)
).concat(enemyHistory);
if (getGunHeat() <= 0.2){
int matchLength = MAX_MATCH_LENGTH;//(int)(Math.ceil(Math.random() * MAX_MATCH_LENGTH));
int matchPos;
do{}
while ((matchPos = enemyHistory.indexOf(
enemyHistory.substring(0, (--matchLength)), MAX_BFT)) < 0);
do {
absoluteBearing += Math.signum(enemyEnergy) *
((short)enemyHistory.charAt(--matchPos) >> 5) / enemyDistance;
} while ((distance -= Rules.getBulletSpeed(bulletPower = //(240 / (int)enemyDistance) +
Math.min(enemyEnergy / 4, BULLET_POWER_TABLE.charAt(
((int)enemyDistance >> 5) * 127 + (int)getEnergy())
//((int)getEnergy() >> 2) * 127 + (int)enemyEnergy)
/ 100.01))
) > 0);
if (getEnergy() > 0.21){
setFire(bulletPower);
}
}
setTurnGunRightRadians(Utils.normalRelativeAngle(
(Math.random() * 0.007) +
absoluteBearing - getGunHeadingRadians()));
}
public static void addWave(double deltaEnergy){
if (pastWaves.size() > 2 && (deltaEnergy > 0 || enemyWaves.isEmpty()
)){
Wave ew;
(ew = (Wave)pastWaves.get(2)).fireLocation = enemyLocation;
ew.bulletVelocity = Rules.getBulletSpeed(deltaEnergy);
enemyWaves.add(ew);
}
}
// CREDIT: mini sized predictor from Apollon, by rozu
// http://robowiki.net?Apollon
public double checkDanger(double direction) {
Wave wave;// = null;
int waveIndex = 0;
double totalScore = 0;
try{
while(true){
//Iterator it = enemyWaves.iterator();
// while (it.hasNext()){
// while (waveIndex < enemyWaves.size()){
// if ((wave = (Wave)it.next()).surf > 0)
// {
Point2D.Double predictedPosition = myLocation;
double predictedVelocity;// = getVelocity();
double predictedHeading = getHeadingRadians();
double moveAngle = 0;//(predictedHeading = getHeadingRadians());
double moveDir = -Math.signum(predictedVelocity = getVelocity());
int counter = 0; // number of ticks in the future
//wave = (Wave)enemyWaves.get(waveIndex);//(Wave)enemyWaves.get(waveIndex);
do {
// moveDir = 1;
if(direction != 0){
moveDir = 1;
if(Math.cos(moveAngle = wallSmoothing(predictedPosition,
absoluteBearing(predictedPosition,enemyLocation), direction) - predictedHeading) < 0) {
moveAngle += Math.PI;
moveDir = -1;
}
/* else{
moveDir = 1;
}*/
}
else if (Math.abs(predictedVelocity) <= 2){
counter = 127;
}
predictedPosition = project(predictedPosition,
predictedHeading +=
posNegLimit(Utils.normalRelativeAngle(moveAngle),
Rules.getTurnRateRadians(Math.abs(predictedVelocity)))
,
predictedVelocity = posNegLimit(predictedVelocity += (predictedVelocity * moveDir < 0 ? 2*moveDir : moveDir), 8)
);
// System.out.println(Math.copySign(8, direction));
// }
} while(predictedPosition.distance((wave = (Wave)enemyWaves.get(waveIndex)).fireLocation)
> wave.distanceTraveled + ++counter*wave.bulletVelocity);
predictedVelocity = //1 -
(predictedHeading = getFactor(wave, predictedPosition));
//predictedHeading = getFactor(wave,predictedPosition);
waveIndex++;
if(wave.dist > (wave.bulletVelocity-22)){
// int index;
int i = 0;//indices[index = (((int)wave.scan[3]))];//depth;
try{
do{//if(i < SCANS)
{//while(i <= threshold){
//Iterator i = hitScans.iterator();
// while(i.hasNext()){
/* if (i > threshold){
break;
}*/
double[] scan = //((Wave)hitScans[index][--i]).scan;
(double[])hitScans.get(i);
//i.next();
double dist = (1/4000.0);
int j = 1;
do{
dist += Math.abs//pow
((wave.scan[j] - scan[j]) / (double)WEIGHT_TABLE.charAt(//weightIndex +//(weightIndex * WEIGHT_LENGTH) +
j)
//, 2
);
}
while(++j < scan.length);
predictedVelocity += ((double)(20 + ++i)//(1.0 / Math.cbrt(++i))//1.0//scan[1]//Math.pow(scan[1], random)
/(dist*dist*(//0.25
//random
(1.0/200//50
)
+ Math.pow(//pow(100 *
(predictedHeading - scan[0])
, 2
))));
}} while (true);//i < SCANS);
}catch(Exception ex){}
totalScore += ((predictedVelocity) / predictedPosition.distanceSq(enemyLocation)) / waveIndex;//Math.pow(waveIndex, 2);
// totalScore += predictedVelocity / Math.pow(predictedPosition.distance(enemyLocation) * (wave.dist + 150), 2);
// }
}
}
}
catch(Exception ex){}
return totalScore;
}
// CREDIT: Iterative WallSmoothing by Kawigi
// - return absolute angle to move at after account for WallSmoothing
// robowiki.net?WallSmoothing
public static double wallSmoothing(Point2D.Double botLocation, double angle, double orientation) {
angle += orientation;
do{}
while (fieldContains(botLocation,( angle -= orientation*0.025)
, WALL_STICK) > 0);
return angle;
}
public static void updateWaves() {
Wave ew;
int i = 0;//enemyWaves.size();
// Iterator it = enemyWaves.iterator();
try{
while(true){
// while(it.hasNext()) {
if (((ew = (Wave)enemyWaves.get(i)).dist =
myLocation.distance(ew.fireLocation)
- (ew.distanceTraveled += ew.bulletVelocity )
) < -50) {
// ew.factors[(int)getFactor(ew,myLocation)] += waveCount;
// lastFactor = getFactor(ew,myLocation);
enemyWaves.remove(i);
// it.remove();
continue;
}
i++;
}
}
catch(Exception ex){}
}
// Given the EnemyWave that the bullet was on, and the point where we
// were hit, calculate the index into our stat array for that factor.
public static double getFactor(Wave ew, Point2D.Double targetLocation) {
// return Utils.normalRelativeAngle(absoluteBearing(targetLocation, ew.fireLocation) - ew.directAngle) * ew.direction
// / Math.asin(8.0/ew.bulletVelocity);
return //(MIDDLE_FACTOR + 25) +
(//MIDDLE_FACTOR *
(Utils.normalRelativeAngle(absoluteBearing(targetLocation, ew.fireLocation) - ew.directAngle) * ew.direction
/ Math.asin(8.0/ew.bulletVelocity)));
}
public void onBulletHitBullet(BulletHitBulletEvent e){
logEnemyBullet(e.getHitBullet());
}
public void onHitByBullet(HitByBulletEvent e) {
//threshold = 20 + (Math.random() * 100);// * 50;
logEnemyBullet(e.getBullet());
enemyEnergy += 20 - e.getVelocity();
}
public void onBulletHit(BulletHitEvent e){
enemyEnergy -= Rules.getBulletDamage(e.getBullet().getPower());
}
public static void logEnemyBullet(Bullet b){
// look through the EnemyWaves, and find one that could've hit us.
Point2D.Double hitLocation;
int i = 0;//enemyWaves.size();
// Iterator it = enemyWaves.iterator();
try{
do{
Wave ew;
if (Math.abs((ew = (Wave)enemyWaves.get(i)).distanceTraveled
- (hitLocation = new Point2D.Double(b.getX(), b.getY())).distance(ew.fireLocation)) <= 40) {
ew.scan[0] = getFactor(ew, hitLocation);
//ew.scan[1] = waveCount;//Math.pow(waveCount, decay);// * (1 + b.getPower());
//int index;
//hitScans[index = ((int)ew.scan[3])][indices[index]++] = ew;
hitScans.add(0, ew.scan);
// We can remove this wave now, of course.
enemyWaves.remove(i);
//it.remove();
}
i++;
}while (true);
}
catch(Exception ex){}
}
// This can be defined as an inner class if you want.
static class Wave {
Point2D.Double fireLocation;
int direction;
// int surf;
double[] scan;
// int[] factors;
double dist;
double bulletVelocity, directAngle, distanceTraveled;
}
// CREDIT: from CassiusClay, by PEZ
// - returns point length away from sourceLocation, at angle
// robowiki.net?CassiusClay
public static Point2D.Double project(Point2D.Double sourceLocation, double angle, double length) {
return new Point2D.Double(sourceLocation.x + Math.sin(angle) * length,
sourceLocation.y + Math.cos(angle) * length);
}
// got this from RaikoMicro, by Jamougha, but I think it's used by many authors
// - returns the absolute angle (in radians) from source to target points
public static double absoluteBearing(Point2D.Double source, Point2D.Double target) {
return Math.atan2(target.x - source.x, target.y - source.y);
}
public static double posNegLimit(double value, double max) {
return Math.max(-max, Math.min(value, max));
}
private static int fieldContains(Point2D.Double source, double heading, double distance) {
return Integer.signum(new Rectangle2D.Double(20, 20, 760, 560).outcode(
project(source, heading, distance)));
}
static final String WEIGHT_TABLE = ""
+ (char)1 + (char)1 + (char)1 + (char)250
+ (char)5 + (char)10 + (char)8 + (char)2;
public static final String ANGLE_TABLE = ""
+ (char)-20 + (char)0 + (char)20 + (char)22 + (char)-22
+ (char)26 + (char)-26 + (char)30 + (char)-30
+ (char)16 + (char)-16 + (char)24 + (char)-24
+ (char)28 + (char)-28 + (char)32 + (char)-32
+ (char)34 + (char)-34 + (char)36 + (char)-36
+ (char)38 + (char)-38 + (char)40 + (char)-40
+ (char)0 + (char)0 + (char)0 + (char)0 + (char)0
+ (char)0 + (char)0 + (char)0 + (char)0 + (char)0
+ (char)0 + (char)0 + (char)0 + (char)0 + (char)0
+ (char)0 + (char)0 + (char)0 + (char)0 + (char)0
+ (char)0 + (char)0 + (char)0 + (char)0 + (char)0
+ (char)0 + (char)0 + (char)0 + (char)0 + (char)0;
public static final String WALL_TABLE = ""
+ (char)6 + (char)6 + (char)6 + (char)6 + (char)6
+ (char)10 + (char)10 + (char)10 + (char)10 + (char)10
+ (char)15 + (char)15 + (char)15 + (char)15 + (char)15
+ (char)30 + (char)30 + (char)30 + (char)30 + (char)30
+ (char)-6 + (char)-10 + (char)-15 + (char)-30;
public static final String MOVEMENT_WALL_TABLE = ""
+ (char)2 + (char)2
+ (char)4 + (char)4
+ (char)-3;
static final String BP300 = ""
+ (char)300 + (char)300 + (char)300 + (char)300
+ (char)300 + (char)300 + (char)300 + (char)300
+ (char)300 + (char)300 + (char)300 + (char)300
+ (char)300 + (char)300 + (char)300 + (char)300
+ (char)300 + (char)300 + (char)300 + (char)300;
static final String BP250 = ""
+ (char)250 + (char)250 + (char)250 + (char)250
+ (char)250 + (char)250 + (char)250 + (char)250
+ (char)250 + (char)250 + (char)250 + (char)250
+ (char)250 + (char)250 + (char)250 + (char)250
+ (char)250 + (char)250 + (char)250 + (char)250;
static final String BP200 = ""
+ (char)200 + (char)200 + (char)200 + (char)200
+ (char)200 + (char)200 + (char)200 + (char)200
+ (char)200 + (char)200 + (char)200 + (char)200
+ (char)200 + (char)200 + (char)200 + (char)200
+ (char)200 + (char)200 + (char)200 + (char)200;
static final String BP30 = ""
+ (char)30 + (char)30 + (char)30 + (char)30
+ (char)30 + (char)30 + (char)30 + (char)30
+ (char)30 + (char)30 + (char)30 + (char)30
+ (char)30 + (char)30 + (char)30 + (char)30
+ (char)30 + (char)30 + (char)30 + (char)30;
static final String BP10 = ""
+ (char)10 + (char)10 + (char)10 + (char)10
+ (char)10 + (char)10 + (char)10 + (char)10
+ (char)10 + (char)10 + (char)10 + (char)10
+ (char)10 + (char)10 + (char)10 + (char)10
+ (char)10 + (char)10 + (char)10 + (char)10;
static final String CLOSE_DISTANCE = ""
+ BP300
+ BP300
+ BP300
+ BP300
+ BP300
+ BP300
+ (char)300 + (char)300 + (char)300 + (char)300
+ (char)300 + (char)300 + (char)300;
static final String MID_CLOSE_DISTANCE = ""
+ (char)10 + (char)200 + (char)200 + (char)200
+ (char)200 + (char)200 + (char)200 + (char)200
+ (char)200 + (char)200 + (char)200 + (char)200
+ (char)200 + (char)200 + (char)200 + (char)200
+ BP250
+ BP250
+ BP250
+ BP250
+ BP250
+ (char)250 + (char)250 + (char)250 + (char)250
+ (char)250 + (char)250 + (char)250 + (char)250
+ (char)250 + (char)250 + (char)250;
static final String MID_DISTANCE = ""
+ (char)10 + (char)10 + (char)10 + (char)200
+ (char)200 + (char)200 + (char)200
+ BP200
+ BP200
+ BP200
+ BP200
+ BP250
+ BP250 ;
static final String MID_LONG_DISTANCE = ""
+ (char)10 + (char)10 + (char)10 + (char)10
+ (char)10 + (char)10 + (char)10
+ BP30
+ (char)30 + (char)30 + (char)30 + (char)30
+ (char)150 + (char)150 + (char)150 + (char)150
+ (char)150 + (char)150 + (char)200 + (char)200
+ (char)200 + (char)200 + (char)200 + (char)200
+ (char)200 + (char)200 + (char)200 + (char)200
+ BP200
+ BP200
+ BP200
+ BP250;
static final String LONG_DISTANCE = ""
+ (char)10 + (char)10 + (char)10 + (char)10
+ (char)10 + (char)10 + (char)10
+ BP30
+ (char)30 + (char)30 + (char)30 + (char)30
+ (char)30 + (char)30 + (char)30 + (char)30
+ (char)150 + (char)150 + (char)150 + (char)150
+ (char)150 + (char)150 + (char)200 + (char)200
+ (char)200 + (char)200 + (char)200 + (char)200
+ BP200
+ BP200
+ BP200
+ BP250;
static final String BULLET_POWER_TABLE = ""
//0 * 127
+ CLOSE_DISTANCE
//1 * 127
+ CLOSE_DISTANCE
//2 * 127
+ CLOSE_DISTANCE
//3 * 127
+ MID_CLOSE_DISTANCE
//4 * 127
+ MID_CLOSE_DISTANCE
//5 * 127
+ MID_CLOSE_DISTANCE
//6 * 127
+ MID_CLOSE_DISTANCE
//7 * 127
+ MID_CLOSE_DISTANCE
//8 * 127
+ MID_CLOSE_DISTANCE
//9 * 127
+ MID_DISTANCE
//10 * 127
+ MID_DISTANCE
//11 * 127
+ MID_DISTANCE
//12 * 127
+ MID_DISTANCE
//13 * 127
+ MID_DISTANCE
//14 * 127
+ MID_DISTANCE
//15 * 127
+ MID_LONG_DISTANCE
//16 * 127
+ MID_LONG_DISTANCE
//17 * 127
+ MID_LONG_DISTANCE
//18 * 127
+ MID_LONG_DISTANCE
//19 * 127
+ MID_LONG_DISTANCE
//20 * 127
+ MID_LONG_DISTANCE
//21 * 127
+ LONG_DISTANCE
//22 * 127
+ LONG_DISTANCE
//23 * 127
+ LONG_DISTANCE
//24 * 127
+ LONG_DISTANCE
//25 * 127
+ LONG_DISTANCE
//26 * 127
+ LONG_DISTANCE
//27 * 127
+ LONG_DISTANCE
//28 * 127
+ LONG_DISTANCE
//29 * 127
+ LONG_DISTANCE
//30 * 127
+ LONG_DISTANCE
//31 * 127
+ LONG_DISTANCE
//32 * 127
+ LONG_DISTANCE
+ LONG_DISTANCE + LONG_DISTANCE + LONG_DISTANCE + LONG_DISTANCE + LONG_DISTANCE;
// log of enemy movement, initialize to something to avoid out-of-bounds
static String enemyHistory = "" +
(char)0 + (char)3 + (char)0 + (char)-3 + (char)0 + (char)3 + (char)0 + (char)-3 + (char)0 +
(char)0 + (char)3 + (char)0 + (char)-3 + (char)0 + (char)3 + (char)0 + (char)-3 + (char)0 +
(char)0 + (char)3 + (char)0 + (char)-3 + (char)0 + (char)3 + (char)0 + (char)-3 + (char)0 +
(char)0 + (char)3 + (char)0 + (char)-3 + (char)0 + (char)3 + (char)0 + (char)-3 + (char)0 +
(char)0 + (char)3 + (char)0 + (char)-3 + (char)0 + (char)3 + (char)0 + (char)-3 + (char)0 +
(char)0 + (char)3 + (char)0 + (char)-3 + (char)0 + (char)3 + (char)0 + (char)-3 + (char)0 +
(char)0 + (char)3 + (char)0 + (char)-3 + (char)0 + (char)3 + (char)0 + (char)-3 + (char)0 +
(char)0 + (char)3 + (char)0 + (char)-3 + (char)0 + (char)3 + (char)0 + (char)-3 + (char)0 +
(char)0 + (char)3 + (char)0 + (char)-3 + (char)0 + (char)3 + (char)0 + (char)-3 + (char)0 +
(char)0 + (char)3 + (char)0 + (char)-3 + (char)0 + (char)3 + (char)0 + (char)-3 + (char)0;
}