[Home]Horizon/Questions

Robo Home | Horizon | Changes | Preferences | AllPages

On this page, I humbly beg for help from the Robocode masters.

Pummeled by Linear / Circular Targeting

I think I know why it is that Horizon is ranked much lower than BasicGFSurfer on RoboRumble. Check out these scores from the WaveSurfingChallenge2K6:

Bot Name Score vs. Bot AScore vs. Bot BScore vs. Bot COverall Score
BasicSurfer 1.0 91.59 58.87 61.09 70.51
Horizon 0.9.2.1 99.184 53.574 38.346 63.70

In other words, Horizon outperforms BasicSurfer by a large amount when dodging HOT, but, against linear/circular targeting, gets converted to spare parts. Not that BasicSurfer is exactly top-notch at dodging linear targeting, but at least it doesn't get *killed* by the challenge bots! Anyone have suggestions as to how to avoid this type of targeting? Is it segmentation on velocity? (At one point I tried putting segmentation on Horizon's movement, but even when I had only one segmentation dimension its learning became much too slow - see next question.)

Data Smoothing Across Segments

The gun used by the current version of Horizon has about the maximum segmentation it can before starting to lose performance from slow learning. This problem could probably be reduced if I had smoothing across segments in addition to smoothing across bins. Unfortunately, I haven't found any simple, easy to implement segment smoothing code. The current layout of my code looks like this:

private double[][][][] gunStats = new double[DISTANCE_BINS][VELOCITY_BINS][LATERAL_VELOCITY_BINS][BINS];

private void updateWaves() {
	for (int i = 0; i < myWaves.size(); i++) {
		Wave ew = myWaves.get(i);

		ew.advance(robot.getTime());
		if (ew.isHit(enemyLocation)) {
			double[] segment = getSegment();
			ew.logHit(segment, enemyLocation, rollingDepth);

			myWaves.remove(i);
			i--;
		}
	}
}

private double[] getSegment() {
	int distanceSegment = (int) round(limit(0,
			myLocation.distance(enemyLocation) / 200, DISTANCE_BINS - 1));
	int velocitySegment = (int) round(abs(enemyVelocity / 2));
	int lateralVelocitySegment = (int) round(abs(enemyLateralVelocity / 2));

	return gunStats[distanceSegment][velocitySegment][lateralVelocitySegment];
}

Note that the logHit() method does BinSmoothing on its own, and has a weight of 1.0 by default but can also accept a weight parameter. How would I implement segment smoothing here (preferably without a large performance hit)?


For the poor performance against Bots B and C in the WSC, I just wanted to say it probably isn't segmentation - BasicSurfer doesn't segment at all and is still getting much higher scores than you. There are a lot of places the bugs could be hiding, so you probably know where to look better than I would, but the first thing that comes to mind is to make sure your Precise Prediction is still correctly predicting your movement after the changes you made. If you are WallSmoothing, controlling distance, or anything differently than the original code was, you need to make sure the prediction is predicting that stuff correctly. E.g., Dookious adjusts distancing / attack angle and does WallSmoothing every tick, so I have to simulate those decisions in every part of the prediction.

As for the DataSmoothing, I have always found this to be incredibly slow when I tried it, though I haven't tried it for a while. There are other ways to overcome the problem, though. You could have multiple buffers, some with lower segmentations than others, and sum them to get the score for any segment - if your high buffer has no data, it will just sum in as a 0. DynamicClustering is a different style of stats, but it basically has DataSmoothing built into how it works. There are other little tricks you could try, as well, like switching from a low segmentation to a higher one once you have enough data.

Hope some of that is helpful, good luck!

-- Voidious

I found that I get poor preformance vs B and C when the wave detection is a little off, it might dodge to late and get hit, Head On is easiest to dodge, and noticed very little difference in its stat. I personally like DynamicClustering, its power is unquestionable, as its used in Lukious, Shadow and Chalk, all very nice, very powerful bots. DataSmoothing is very slow on anything but two or three segments, I have tried it and it can really slow a bot down. --Chase-san

Thank you, thank you, THANK YOU! Not only did I find a bug in my surfing that caused waves to lag behind bullets by a couple ticks, but fixing that, tweaking a few variables, and sorting my GuessFactors with DynamicClustering instead of segmentation improved Horizon's scores against all three test bots:

Bot Name Score vs. Bot AScore vs. Bot BScore vs. Bot COverall Score
BasicSurfer 1.0 91.59 58.87 61.09 70.51
Horizon 0.9.2.1 99.184 53.574 38.346 63.70
Horizon development 99.628 68.51 43.67 70.602

So Horizon's surfing is now better overall than BasicSurfer against simple targeting. I also added DC to the gun, so I will be releasing an improved (hopefully) version soon.

-- AaronR


Time for a second round of questions.

Execute Every Turn

It's somewhat hard to understand how I have been working with Robocode for this long and not come upon this problem. The problem is: how do I execute a non-blocking call every tick? That is, I have a nonblocking method that I want to execute *exactly* once every tick, regardless of whether I scan the enemy or not on that turn. I can't use execute() or waitFor(), as those would cause all nonblocking calls to execute immediately, which is not what I want. Do I have to use custom events?

public void run() {
	// ...

	do {
		foo();
		execute();	// Won't work! Executes foo() and all other nonblocking calls!
	} while (true);
}

public class Horizon extends AdvancedRobot {
	private static List<Component> components = new ArrayList<Component>();

	static {
		components.add(new RadarLock());
		// ...
	}

	public void run() {
		// ...

		for (Component component : components) {
			component.startRound(this);
		}

		/* The problem */
		do {
			for (Component component : components) {
				component.eachFrame();
			}

			execute();
		} while (true);
	}

	public void onScannedRobot(ScannedRobotEvent e) {
		for (Component component : components) {
			component.scannedRobot(e);
		}
	}

	public void onHitByBullet(HitByBulletEvent e) {
		for (Component component : components) {
			component.hitByBullet(e);
		}
	}

	// ...
}

public abstract class Component {
	protected AdvancedRobot robot = null;
	
	public final void startRound(AdvancedRobot robot) {
		this.robot = robot;
		startRound();
	}
	
	protected void startRound() {
	}
	
	public void eachFrame() {
	}
	
	public void scannedRobot(ScannedRobotEvent e) {
	}
	
	public void hitByBullet(HitByBulletEvent e) {
	}

	// ...
}

public class RadarLock extends Component {
	public void eachFrame() {
		robot.setTurnRadarRightRadians(Double.POSITIVE_INFINITY);
	}
	
	public void scannedRobot(ScannedRobotEvent e) {
		// ...

		robot.setTurnRadarRightRadians(...);
	}
}


I admit I was being somewhat unclear. The problem is, running Horizon with this setup makes it spin the radar forever without getting a lock, because scannedRobot() in RadarLock? is being called *before* eachFrame(). That is, eachFrame()'s turnRadarRight?(POSITIVE_INFINITY) is overriding scannedRobot()'s radar lock. I replaced the problematic loop with a custom event with the highest priority so that eachFrame() would always be called *before* any other events are executed.

-- AaronR

Move Away Code Could be Better

In Horizon, two sections of code work together to avoid being close to the enemy. The first is a simple angle adjustment that causes the bot to spiral outward. The second is a modification to the danger factor of predicted positions that looks like this:

private double checkDanger(Wave wave, double[] segment, int direction) {
	double danger = 1.0;
	Point2D.Double location = predictPosition(wave, direction);

	// ...

	if (location.distance(enemyLocation) < myLocation.distance(enemyLocation)) {
		danger *= 1.02;
	}

	return danger;
}

The problem with this is, it tends to make the bot settle into a nice, comfortable spot in the corner and vibrate back and forth - not a good thing! However, taking out this adjustment makes the bot head along the wall that the enemy is on and barrel right towards them (due to the wall smoothing). What alternative methods of avoiding the enemy are there?

Horizon Has a Seizure

Even with the enemy avoidance code taken out, Horizon spasms a lot. How do you deal with spasming? Also, I have tried adding a stop position to the code but it didn't make any improvement. Is there a way to detect when the bot is spasming and make it freeze instead?


Now that the conversation has gotten completely out of control... =)

Where should we move this discussion to? It seems like so far, we have parts that could go to ShrapnelDodging, AntiSurferTargeting, GuessFactorTargeting, maybe Trigonometry...

-- AaronR

I moved it over to WaveSurfing, since that seemed like the biggest topic and it all related to that in some way or another. Feel free to delete this comment and your own if you want to keep this page nice and tidy. =) -- Voidious


This will probably be the last question before the 1.0 release of Horizon. I won't say anything specific about this release yet, but I will say that it will be completely different than the current version...

Anyway, here's the question:

Segmentation Optimization

The clustering factors used in the current release of Horizon are:

Can anyone suggest an improved set of segments? Possible factors I am considering include:

In addition, since I am using dynamic clustering, I can assign each factor a weight. Which factors should be weighted highest?


Post responses here

I think you have too many velocities; if you're in 1v1, I'd definitely just stick with lateral velocity. For me, these are the first attributes I try in a new gun: LateralVelocity, distance (or bullet time), time since velocity change (divided by distance or bullet time), accel (0 = decelerating, 1 = same speed, 2 = accelerating), and WallDistance.

The best way to segment WallDistance in 1v1, as far as I know / am concerned, is by segmenting on what amounts to "at what GuessFactor starting from now will the enemy be when they hit the wall if they keep orbiting me"; so a value of 1 means they will hit the wall right as my bullet arrives. One last minor thing: with DynamicClustering, I personally tend to do something like min(wallDistance, 1) for some values - if your WallDistance is 2 or 3 or 5, it amounts to about the same thing: you don't have to account for the wall. Hope this helps. (PS - they're not segments unless you're segmenting them! =) But segments and attributes are almost synonymous because of the popularity of VisitCountStats...)

Not sure I can give solid advice on the weights, but I'd say LateralVelocity and distance would be the first ones I'd try to give higher weights to.

-- Voidious

Thanks, that is exactly what I was looking for advice-wise! ;-) I think I threw in so many velocity factors because I saw someone said at some point that acceleration was a good segment (not factor or attribute, segment, since there were no GF guns using clustering at the time), and someone else said that lateral acceleration was also good, and ... you get the idea. -- AaronR


Well, as it turns out, that was not the last question before the 1.0 release.

Surfing Multiple Waves Isn't as Easy as It Looks

I've got most of the movement coded for Horizon 1.0, but the danger calculation and precise prediction related to surfing multiple waves is giving me a problem. Specifically, I realized that during my precise prediction, I would have to advance not one but all of the waves I am surfing. Of course, if I actually did this, I wouldn't be able to surf the waves after one tick (since the state is being corrupted), so I have to make a copy of all of the waves. However, because I still have danger calculations for the next waves after this, I have to do the copying recursively. And the recursive copying is now three-way instead of two-way, since I added evaluation for a stop position. Oh, and I still have to do all of this three times, because I need to determine which direction to surf. The inevitable result of this three-way recursive deep copying three times is that my robot is using up all of my system's memory and skipping turns all over the place.

Now what?


Post responses here

Hmm, I'm not sure I really understand your code situation - why do you have make multiple copies of all the waves? Let me try this: as you move tick by tick through the prediction, you can calculate the bot's distance to the wave source and the distance the wave has traveled by that tick, without actually modifying the wave (and thus needing to copy it to have the original version); those are all the data you need to check if the wave has intercepted you by that tick. Am I imagining this dilemma correctly, or am I way off here? =) -- Voidious

You are close, that is pretty much what I was doing with all of my previous versions that only surfed one wave (look at lines 213-217 of BasicSurfer, that is what I was doing). My current problem is that I now have to determine when one wave hits, precise predict that, and then determine when the next wave will hit, adjusting for the time during which I was predicting my surfing for the first wave. So I could predict that wave A will hit in 10 ticks, then start predicting wave B, but I have to compensate for the distance B "traveled" while A was being predicted. To do this I either have to mutate the waves in the precise prediction function or have the function pass back an integer indicating how many ticks will elapse before the relevant wave hits, and then mutate the waves in the danger function. Either way I end up with a mess.

P.S. Sorry if I can't make it any clearer, my code is about as clear to me as mud right now.

-- AaronR

So why can't your precise prediction function return a class with position and time? Than that will be the base to calculate the next-wave danger. Not that I used it, but probably I also need it if I want to improve my multiple-wave precise prediction. -- GrubbmGait

What I did in DrussGT was to weight waves by multiplying the danger at a point by pow(0.55, timeUntilArrival? - 15). This function tapers off quickly, so the closest waves get weighted almost exclusively, but it will still weight ChaseBullets quite highly. However, why don't you return an array of points from your PrecisePrediction, adding a point each time a wave passes? -- Skilgannon

You do need to compensate for the time B traveled while surfing A, but no need to permanently "mutate" the wave! The wave has logged its starting (center) point, time fired, and bullet velocity. Your wave class could have a distanceToBot?(Point2D bot, time t) method that calculates ((how far from wave source to enemy bot) - (distance traveled at time t)) - you'd surf it until that is less than zero. When you surf wave A, say you have a variable time = now that increments by 1 as you surf; then pass that off to the method that's surfing wave B and start at time t = wave A intercepted you. Right? -- Voidious

Here is basically what my code should do:

... etc ...

Hope that makes things clearer.

-- AaronR

Well, if that's not what your code is doing, then you need to refactor =). I always put it off, because I'm scared of breaking things when I do. -- Skilgannon

It appears I got my precise prediction working. I ran 100 rounds of HawkOnFire vs. Horizon 1.0 with no gun, and Horizon won 6000 to 15. =) -- AaronR


Robo Home | Horizon | Changes | Preferences | AllPages
Edit text of this page | View other revisions
Last edited September 19, 2007 6:03 EST by AaronR (diff)
Search: