The trick:
When i have start working on Musashi's movement trying to get a FlatMovementProfile?, called my attention the fact that while my performance against the top rated bots had increased, some low rated bots like Barracuda and Wesco? had became a problem. All these bots had one thing in common: HeadOnTargeting guns.
Many bots have a hudge lack of performance against HeadOnTargeting guns, this happens because when u try a flat movement, normally is based in a perpendicular movement with a lot of inversions of direction, and oftenly you return to the same points, becoming vulnerable to lucky HeadOnTargeting shots.
The trick consists in start moving trying to stay as far as possible from the most recently visited points, and discover if that tatic is working, if it is not working, switch back to the flat movement. In Musashi, that means an OrbitMovement? at full speed, inverting the direction only if necessary (if it is about to hit a wall) - that is, the moving without the flatting issue (random inversions).
This was first published in Axe's Musashi version 2.15 (january 4th, 2004). You can also find additional information at it's page: Musashi.
Is it effective?:
My test bot was Kawigi's Barracuda (In my opinion the most harmfull HeadOnTargeting).
. ELO rating before applying MusashiTrick: 43.2% . ELO rating after applying MusashiTrick: 84.7%Also all HeadOnTargeting ELO ratings had an increase of 100%. And better: the ELO scoring against the top bots hadn't changed.
. version 2.14: 28th. . version 2.15: 11th.
Can i use this in my bot?:
Of course, otherelse i wouldn't wrote this. Just give credit.
Requirements:
The code:
PS: this code is slightly changed from the Musashi's original source code in order to improve it's readability(?):
... //Holds the time of the last inversion of direction, that is, when i have last "bumped" on a wall. private long last_inversion = 0; //This boolean is tested to decide if the flattering will be used. It's static because //i don't want to test it avery round, but only in the beginning of the match. private static boolean flat = false; ... //controls the moving public void move(){ // test if it is about to hit a wall, if it is calls the invert() method ... if(flat){ //do the flat thing } } //inverts the moving direction private void invert(){ // do the inversion stuff ... //stores the time of this inversion last_invertion = super.getTime(); } // call this every time you are hit by a bullet (passing the bullet's power as pw. public void hitByBullet(double pw) { // calculate the time elapsed since the last direction inversion. I remind you that // i store the current time at last_inversion each time i invert my direction: long lastRevDelta= getTime() - last_inversion; // the time ESTIMATED that the enemy bullet took to reach me // (I use the current enemy distance plus 100 to have some slack - a better way to tune it would // probably be something like distance*k, where k is the slack ). // That estimation is what makes the function not 100% accurate. // If u have the real bullet travel time it would be very precise, // i think (but probably a lot of additional code too). long enemyBulletTravelTime= (long) ((him.getDistance() + 100) / RoboMath.getBulletVelocity(pw)); // I am being hit near GF 1.0 if: // - (him != null): exist an enemy (safety in order to avoid any nullPointerException) // - (him.getDistance()>200):the distance to the enemy is greater than 200 - the trick // don't perform fine at very close range (u migth want to tune this // too, but since my bot tries not to stay so close, it works for me) // - (enemyBulletTravelTime < lastRevDelta): here is kernel of the trick: since // this bullet was fired i have not inverted my direction. if ((him != null) && (him.getDistance()>200)&&(enemyBulletTravelTime < lastRevDelta) ) { // I was hit near GF 1.0!!!!! flat = true; } } ...-- Axe
Here's something like the code used in Tityus:
static final int MAX_FULL_LEAD_HITS = 1; static int fullLeadHits; static boolean isFlattening; int movementDirection = 1; public void onHitByBullet(HitByBulletEvent e) { if (enemyDistance > 100) { if (timeSinceReverse > enemyDistance / e.getVelocity()) { fullLeadHits++; isFlattening = fullLeadHits >= MAX_FULL_LEAD_HITS; } } } void move() { timeSinceReverse++; ... if (isFlattening && ...) { reverse(); } ... } void reverse() { timeSinceReverse = 0; movementDirection *= -1; } }The code is checking if Tityus has been moving in the same lateral direction during the travel time of the bullet that just hit it. For this to work Tityus must be moving at full speed always. Even if your bot isn't always doing that under normal conditions, you can make sure it does while "isFlattening" evaluates to "false". Check out the source code of VertiLeach to see how it uses a special movement function while it is trying to exploit the head-on targeting weakness.
-- PEZ
Please add your bots here, if they use this trick (i know PEZ has added it to a few bots)
Why did you say this was ugly? It's the most elegant movement idea I've seen here in a long time. Well done! :-) (The fact that it was taken up with such speed proves that) -- Tango
static double pvelravg = 1;
public void onScannedRobot(ScannedRobotEvent e) {
setTurnGunRightRadians?(Utils.normalRelativeAngle((pvelravg = (pvelravg * 14 + e.getVelocity() * Math.sin(e.getHeadingRadians() - (e.getBearingRadians?() + getHeadingRadians()))) / 15) * Math.ceil(e.getDistance() / 11) / e.getDistance() + e.getBearingRadians?() + getHeadingRadians() - getGunHeadingRadians?()));}
should be a viable replacement for direct guns (on the same level of simplicity) that find themselves dropping due to this trick. Codesize can obviously be improved, that's the one-line version. -- Kuuran
That doesn't make sense to me... what does it do, how does it do it, and why is in onScannedRobot, not onHitByBullet?? :-/ -- Tango
The point is to hit the MusashiTrick I believe. -- Kawigi
Thanks. I needed this for VertiLeach. -- PEZ
That assuming that i am at vel 8.0, orbiting your bot and you fires only 3.0 bullets, right? Ain't it a CircularTargeting gun? -- Axe
Well, close, it's angular projection - circular projection would actually try to figure out how fast you were turning, too. Angular projection assumes you are at the middle of the circle. -- Kawigi
It's somewhat akin to circular with averaging. It's basically an ultra-simple adaptive gun I made for all-movement nanos when I didn't want to use direct. You're looking at the haiku version, however. It's meant to be a replacement for direct which isn't as rigid as linear/circular, it abstracts the movement into 'how far does he tend to go to what side, from my perspective' and does nothing beyond that. Obviously I don't recommend it as a gun on heavy duty bots, but any all-movement bot which is hurting from direct being out of the picture should consider this. It will tend to fire as either near full lead or direct. -- Kuuran
But the fact is that the intention of MusashiTrick is taking out the HeadOnTargeting guns, since that they tent to be much harmfull (in special to the top bots, with flat movings) that they should be. But it's only a trick, and i am sure that there are many counter-tricks to it (and a lot of counter-counter-tricks too:).
The point of Kuuran is to use a non-HeadOnTargeting gun, a nice implementation indeed! But the fact is that it isn't a HeadOnTargeting. I am curious about, wich bot uses that cannon? Let me test to see if it is at least so harmfull against v2.14 (pre-MusashiTrick). -- Axe
No the point is to use a gun that fires both head-on and full-lead. -- PEZ
i thought you should check to see if you are being hit at 0, and if you are.don't switch directions.why do you check to see if you are hit at gf 1 -- JohnDoe
Think about it. If you are being hit at GF0, what does that tell you? Not much. What the MusashiTrick helps you check is if you are ever hit at GF1. And if you aren't, don't switch directions. So you're not actually checking if you're hit at GF1, you're testing if you are not. -- PEZ
what's the hit atmaxspeed variable? nevermind, i answered my own question . it is the enemybulletime long -- andrew
U r right, the variable name was wrong, thnks for noticing. -- Axe
That variable name is changed in most implementing bots. =) -- PEZ
Is there a way to adapt the MusashiTrick to figure out if they are using LT/CT? If they are, you could simply change direction whenever they shoot and you will always dodge all of their bullets. It's like the Musashi trick, but for slightly more advanced opponents. So you have regular MusashiTrick, LT/CT MusashiTrick, and some other more advanced adaptive movement thing. If I'm wrong, tell me before I spend a lot of time trying to implement this in Squirrel. :) --Bayen
Well, if you are a decent distance away, a "buzzing" movement that switched directions every tick would have velocity = 1 and -1 every other tick, so should never get hit by LT or CT. Where MusashiTrick keeps moving until something other than HoT? hits it, you could "buzz" until something other than LT or CT hit you. -- Voidious
I took the tangental oscillation movement of SnippetBot a step further and reverse directions every time the enemy fires. It was very, very effective against about 3 dozen opponents, getting me consistent kill ratios in the 90s. It performed poorly against other bots though. Statistical guns (e.g. guess-factor) will tear it a new one. -- Martin
A StopAndGo movement is quite effective against LT/CT, you must keep some distance though. Gruwel has an initial movement that can dodge both HOT and LT/CT, but it is not as lean and clean as the MusashiTrick. The biggest problem with such specific movements is that it is only effective against a limited number of bots, approximately onethird (130) of the current participants. -- GrubbmGait
How about this (it's untested, tho):
static boolean antiHOT = true; //anti Head On Targeting movement (circle them) static boolean antiBT = false; //anti Basic Targeting (LT,CT) movement ("buzz") static boolean antiST = false; //anti stat/other targeting movement (flat, or in my case, random) ScannedRobotEvent lastScan; //the info from the last onScannedRobot long lastDirectionChange; ... public boolean GF1(HitByBulletEvent e) { long timeSinceDirectionChange = Squirrel.getTime() - lastDirectionChange; long timeSinceBulletFired = (long) ((lastScan.getDistance() + 100) / e.getVelocity); if(timeSinceBulletFired > timeSinceDirectionChange) return true; else eturn false; } public void onHitByBullet(HitByBulletEvent e) { //assumes that if you are hit by a GF 1 they are using CT or LT if(antiHOT) { if(GF1(e)){ antiHOT = false; antiBT = true; } } //now, if you are already dodging CT and LT, if you are hit by anything besides GF 1 you change to flat movement if(antiBT) { if(!(GF1(e))){ antiBT = false; antiST = true; } } }
What do you think? --Bayen
This looks great to me. The only thing I might add is having the movements (mainly buzzing to flat movement) not switching on their first hit. Even the best dodging movements occasionally get an unlucky hit, so having some leeway means the movement doesn't "accidentally" switch. You could do something like this onHitByBullet?:
if(antiBT) { if(!(GF1(e))){ if(antiBTHits++ > Math.min(getRoundNum(), 3){ antiBT = false; antiST = true; } } }This means as the game goes on, the tolerence level of switching increases because it is less likely you're facing a good gun. -- Kev
I think that the code accounts for that, because
if(!(GF1(e))){means that it will only change if the buzzing is hit by a NON GF1 shot. Even the lucky hits should be at GF 1 if they are using
Even so, I think that your idea is a good one. When I get around to implementing this in Squirrel, I'll use your changes. --Bayen
By the way, neither LinearTargeting nor CircularTargeting are guaranteed to hit GF=1. It seems like this might only loosely matter in the code you're using here, but I thought it was worth mentioning. For instance, both LT and CT will shoot head on if their velocity is zero, but traditional GF=1 doesn't change depending on velocity. -- Voidious
How about to hit the bot with simple musashi trick by keep firing at MEA until it hit, then fire standard head-on :) --Nat
Well, GF=1/MEA can't actually be achieved in an orbital movement which most musashi trick implementations use. GF=1/MEA occurs when is perpendicular at the time of firing but moves in a straight non-curving line from that point. Therefore firing at the true MEA wouldn't wouldn't be ideal against musashi trick normally. -- Rednaxela