一、大体思路
扫描敌人的逻辑不变,对扫描到敌人后的应对方法进行重写
下面是整体的代码实现
package jjysss;
import java.awt.Color;
import robocode.AdvancedRobot;
import robocode.HitWallEvent;
import robocode.ScannedRobotEvent;
import robocode.HitByBulletEvent;
public class Jjyssss extends AdvancedRobot {
private static final int MAX = 20;
public static final double P=3.14159265359d;
private static final double min_dis = 220.0d;
// 坦克移动方向
int way = 1;
// 宽度,高度
double width;
double height;
double MIN_RANGE = 30.0d;
// 回合计数器
int ifcontinue = MAX;
public void run() {
setGunColor(new Color(0, 0, 0));
setBulletColor(new Color(0, 0, 0));
setRadarColor(new Color(0, 0, 0));
setScanColor(new Color(0, 0, 0));
setBodyColor(new Color(0, 0, 0));
//分开旋转
setAdjustRadarForRobotTurn(true);
setAdjustGunForRobotTurn(true);
// 雷达持续旋转
turnRadarLeftRadians(2147483647);
// 活动范围
width = this.getBattleFieldWidth() - MIN_RANGE;
height = this.getBattleFieldHeight() - MIN_RANGE;
}
//扫描到了
public void onScannedRobot(ScannedRobotEvent e) {
//自己与敌的连线,与Y轴形成的夹角
double bearing = getHeadingRadians() + e.getBearingRadians();
bearing=robocode.util.Utils.normalRelativeAngle(bearing); //根据robocode教程
// 速度
double enemySpeed = Math.sin(e.getHeadingRadians()-bearing) * e.getVelocity(); // em的相对与我的相对位置
setTurnRadarRightRadians(-getRadarTurnRemainingRadians()); //转回去,回调函数
//旋转gun
// 10 11 平滑处理,降低旋转速度。 gun是相对坐标的
double gunturn = norma(bearing - getGunHeadingRadians() + enemySpeed/11);
setTurnGunRightRadians(gunturn);
// 默认向前
boolean ahead = true;
if (e.getDistance() > min_dis){
// 接近 //得到e.getbearingradians,这是机器人需要调整的角度+一个补偿值(敌坦的速度/自己的速度)
setTurnRightRadians(norma(bearing - getHeadingRadians() + enemySpeed / 11));
} else {
// away 右转加45度 45跑路
setTurnRightRadians(45 + e.getBearing());
ahead = false;
}
move(ahead, way * (e.getDistance() - min_dis));
if(e.getDistance()<=230) setFire(3);
else setFire(1);
}
private static double norma(double x)
{
return (x %= (2 * P)) >= 0 ? (x < P) ? x : x - (2 * P)
: (x >= -P) ? x : x + (2 * P);
}
private void move(boolean ahead, double distance) {
// 自己的位置
double x = this.getX() + distance;
double y = this.getY() + distance;
if (ifcontinue <= 0)
{
if (x <= MIN_RANGE || y <= MIN_RANGE || x >= width || y >= height)
{
ahead = false;
//重启计数器
ifcontinue = MAX;
}
if (ahead)
setAhead(distance);
else
setBack(distance);
}
else
{
ifcontinue--;
}
}
public void onHitByBullet(HitByBulletEvent event)
{
setTurnRightRadians(90);
setBack(50);
execute();
}
public void onHitWall(HitWallEvent e) {
way = -way;
ifcontinue = MAX;
}
}
二、细节分述
1.导入各个包
package jjysss;
import java.awt.Color;
import robocode.AdvancedRobot;
import robocode.HitWallEvent;
import robocode.ScannedRobotEvent;
import robocode.HitByBulletEvent;
2.类中属性
其中,MAX代表一个周期计数器的最大值,ifcontinue就是一个计数器,在后面的代码实现了当计数器小于0时,坦克会发生一次移动,大于0时坦克不变,这个设定会给坦克一个随机量,移动更灵活。width和height则表示坦克的活动范围为(width,height),而MIN_RANGE限定一个边界值,后面我们用地图的width,height减去MIN_RANGE,就是坦克的活动范围。这个和way的作用类似,就是避免坦克撞到墙。min_dis是和敌人最小距离即安全距离。p是π的近似值
private static final int MAX = 20;
public static final double P=3.14159265359d;
private static final double min_dis = 220.0d;
// 坦克移动方向
int way = 1;
// 宽度,高度
double width;
double height;
double MIN_RANGE = 30.0d;
// 回合计数器
int ifcontinue = MAX;
3.run方法重写
1.设置颜色
2.雷达枪分开旋转,这样可以使扫敌和攻击效率变高
3.turnRadarLeftRadians(INT_MAX),给一个尽可能大的弧度制,让雷达一直旋转。
4.限定坦克活动范围
public void run() {
setGunColor(new Color(0, 0, 0));
setBulletColor(new Color(0, 0, 0));
setRadarColor(new Color(0, 0, 0));
setScanColor(new Color(0, 0, 0));
setBodyColor(new Color(0, 0, 0));
//分开旋转
setAdjustRadarForRobotTurn(true);
setAdjustGunForRobotTurn(true);
// 雷达持续旋转
turnRadarLeftRadians(2147483647);
// 活动范围
width = this.getBattleFieldWidth() - MIN_RANGE;
height = this.getBattleFieldHeight() - MIN_RANGE;
}
4.OnScannedRobot重写
1.计算出bearing值。
2.计算出enemyspeed,敌人的朝向-自己朝向=枪该转过去的角度。取sin()是因为能更好的表达两者的角度差,差为0时,结果为0,差为90度时,sin为1,差为180时,sin为0。前面这一坨也是一个权重值,乘以敌人的速度,得出敌人的一个相对速度。为后面的枪旋转射击做准备。
3.setTurnRadarRightRadians(-getRadarTurnRemainingRadians()); 这句代码的精妙之处在于,类似于C++的回调函数,当坦克扫描到敌人后,进入onScannedRobot方法,而getRadarTurnRemainingRadians()代码为获取雷达旋转剩余的弧度数,取负数往反方向旋转,setTurnRadarRight-Radians方法接收此参数后,会反方向扫描,但此时又恰好好会遇见刚才扫描过的敌人,又会进入onScannedRobot方法,形成了一个闭环,算是一个简陋的索敌雷达。
4.枪应该旋转的角度,根据课件原本为雷达应该旋转的角度,但是在本坦克中,雷达的锁定有另一种方法, 但是这个也可以界定为枪旋转的角度,虽然不是很精确,不过也实现了一定程度的追踪打击。但是枪旋转期间,敌人也在移动,所以还需要加上一个权重值来表示额外的旋转角度,经过实验发现,enemyspeed*一个数,旋转得太快了,所以改为/,测试后10和11是胜率比较高的数。
5.我们的坦克在正常情况下是向前走的。在下面的代码中,遇到MIN_RANGE和小于和敌人的安全距离会停下来。
6.当前距离大于安全距离时,坦克接近敌人,逻辑和枪寻敌一样。小于安全距离时,右转45度停止向前移动。
7.开火判定,距离小于等于230,发射威力为3的子弹,else威力为1。
public void onScannedRobot(ScannedRobotEvent e) {
//自己与敌的连线,与Y轴形成的夹角
double bearing = getHeadingRadians() + e.getBearingRadians();
bearing=robocode.util.Utils.normalRelativeAngle(bearing); //根据robocode教程
// 速度
double enemySpeed = Math.sin(e.getHeadingRadians()-bearing) * e.getVelocity(); // em的相对与我的相对位置
setTurnRadarRightRadians(-getRadarTurnRemainingRadians()); //转回去,回调函数
//旋转gun
// 10 11 平滑处理,降低旋转速度。 gun是相对坐标的
double gunturn = norma(bearing - getGunHeadingRadians() + enemySpeed/11);
setTurnGunRightRadians(gunturn);
// 默认向前
boolean ahead = true;
if (e.getDistance() > min_dis){
// 接近 //得到e.getbearingradians,这是机器人需要调整的角度+一个补偿值(敌坦的速度/自己的速度)
setTurnRightRadians(norma(bearing - getHeadingRadians() + enemySpeed / 11));
} else {
// away 右转加45度 45跑路
setTurnRightRadians(45 + e.getBearing());
ahead = false;
}
move(ahead, way * (e.getDistance() - min_dis));
if(e.getDistance()<=230) setFire(3);
else setFire(1);
}
5.move方法
1.先确定自己的位置,distance为即将移动的距离,加上再判断是否走到了限定范围外。
如果走到了限定范围外,ahead变为false,计数器重启。
在下面的判断语句中,如果ahead为true,往前走,反之往后走。
ifcontinue不为0,ifcontinue--,计数器MAX为一个轮回。
private void move(boolean ahead, double distance) {
// 自己的位置
double x = this.getX() + distance;
double y = this.getY() + distance;
if (ifcontinue <= 0)
{
if (x <= MIN_RANGE || y <= MIN_RANGE || x >= width || y >= height)
{
ahead = false;
//重启计数器
ifcontinue = MAX;
}
if (ahead)
setAhead(distance);
else
setBack(distance);
}
else
{
ifcontinue--;
}
}
6.特殊情况判定
上面的坦克设定中,被射击到不会躲避,并且在矩形的边界中,如果遇见了猥琐在边界的敌人,此坦克会铁着头往敌人子弹上撞,所以重写onHitByBullet函数,虽然不能完全规避这种情况,但也减少了一部分情况的发生。
public void onHitByBullet(HitByBulletEvent event)
{
setTurnRightRadians(90);
setBack(50);
execute();
}
7.撞到墙后
虽然有限定范围,但是坦克的受击反馈仍可能会撞到墙,这里做一个特殊处理,往相反方向走,计数器重启。
public void onHitWall(HitWallEvent e) {
way = -way;
ifcontinue = MAX;
}
}
8.norma
网上抄的,用于将角度转换为-π到π之间。
private static double norma(double x)
{
return (x %= (2 * P)) >= 0 ? (x < P) ? x : x - (2 * P)
: (x >= -P) ? x : x + (2 * P);
}