[Home]EventManagement

Robo Home | Changes | Preferences | AllPages

I use an event manager in my bots, and I like it. Events are received at the beginning of the turn, and all the events are processed and dropped by Fractal before it begins doing anything else on its turn. The event manager handles putting everything into Fractal's memory databanks. The power in this is firstly that I hate event-driven programming; I like this way because I can write my code polling the data in Fractal's memory banks when it needs it, rather than worrying about what data is available in this method and what data is available in that method. It also has some advantages such as if a scan is missed for example, the memory manager can simply extrapolate a new scan and plug it into the memory banks; the entire rest of the robot will never know the difference and can continue seemlesly without a scan. Other advantages are for team play; when sending eachother things like scans, these can be plugged into the memory banks, and once again the bot need not know the difference between an actual scan, a received scan, and an extrapolated scan.

There are several ways of implementing an event manager; Fractal currently does this the easy way, by simply forwarding the events to the event manager and letting them be handled there. for example:

 public void onScannedRobot(ScannedRobotEvent e) {eventManager.scan(e);}
You can also do this somewhat of the fancy way, which may be what I will eventually implement in Fractal. Make one custom event at max priority on a condition that is always true, and whenever the custom event fires make it call eventManager.doEvents();. The event manager can then request the entire event queue, pass through each one and perform the appropriate action (putting the memory where it goes), and then simply dump the event queue. This is how SineSweep managed events (and unfortunately I lost its source code...). This catches all events except those at highest priority.

Anyway, although an event manager may not seem like something important, I find it is something absolutely crucial to the reliability and ease of programming of your bot, and I have also considered the event manager the one most important module of my bot. The event manager built into Fractal was the very first code I wrote for it (as was for most of my bots), and it was the foundation and building blocks for all of its other code. Now that I know it works, I can easily build any methods I want, and I know that any data I want on anybody is readily available in memory.getPacket(<botNum>) regardless of when or how it was acquired. Once the event manager is built, you never have to worry about data acquisition again; you can comfortably design your robot's behaviour just knowing the data it needs is there ready to be used.

-- Vuen

Thanks! I was sure there was someone out there using something like this. I've never used any of the getEvent methods, do you have to check what type of event it is before doing anything? -- Tango

Fractal does not use a 'custom' event manager; none of my bots have and will ever use custom events. Everything the bots need to know is polled for. It's not that it directly improves its rating; it just makes it so much easier to program. All info Fractal has is within the Memory class, and it is managed by the Events class; the way Fractal's objects are created, any data can be accessed from anywhere using memory.<whateveryouwanttoknow>. That way you don't have to worry about where to put your code; if you want something to happen when your bot gets hit, you don't have to worry about putting it in onHitByBullet? for example; you can put in wherever you want the code to be executed 'if (memory.wasHitByBullet?())' and program your statement from there. You can organize it however you want; you can program in one method making it perform some movement decisions, do something if it's hit, perform more stuff, do something else if its hit, use the power you were hit by directly in calculations (something like memory.getHitByBulletPower?()) and these methods can easily share variables and interact with eachother. This power makes Fractal's execution extremely linear, so that code is guaranteed to follow exactly the order its written and you never have to worry about what events fire first and stuff like that. The code is also much more readable; because it's so linear you can simply read along the code and know exactly every decision Fractal will make and in what order. This is just the power I find in an EventManager?.

An EventManager? is also much more useful in melee; I find programming stuff in a scan event is utter nonsense in melee, and if your bot is written for 1v1 like that it'l take you ages to change it for melee because you have to isolate all your code from the scans anyway.

As for the getEvents methods, it will return a vector of all the events; you have to take each one from the vector and use (ex: if it's put in the variable 'event')

 if (event instanceof ScannedRobotEvent) //dump scan info into memory banks
 else if (event instanceof HitRobotEvent?) //dump collision info into memory banks
and so on. The only quirk with this method is you have to call it from within an event; after Robocode advances a frame events fire before a holding call (such as execute()) is released. This is why you have to create a custom event with a permanently true condition on max priority that runs the event manager, and program everything else from within your run() method.

-- Vuen

Using the events mainly to collect data is very much what I think I have seen MathewNelson say they were meant for. And I think that's what most people do with their MegaBots. (I still refuse to prepare my bot for melee until I am actually going to make it melee-aware.)

About getEvents(), can you set your own custom super event to have higher priority than all built-ins? Can you post a a sceleton of code for such an event manager? If I get the sceleton I can make an /ExampleBot? using it. -- PEZ

From the FAQ:

Q: How do I use getAllEvents?()? A: (Based on answer from Graygoo) Add a condition that calls a function to record the state. For example:

  public void run() {
    addCustomEvent(new Condition("recordEverything") {
	  public boolean test() {
	    MyRobot.this.recordState();
		return false; // False so we don't trigger an onCustomEvent handler
	  }
	};
  }
  public void recordState() {
    // call getAllEvents()
	clearAllEvents();
  }

Thanks. Though I tried that a long while ago and never got it to work. Maybe now when I have more Robocode experience I can try again. -- PEZ

It's really a matter of taste. I also never did it that way, my code looks like this:

public void onScannedRobot(ScannedRobotEvent e) {
	enemies.update(e);
}
public void onHitRobot(HitRobotEvent e) {
	movement.update(e);
}
etc. --ABC

Umm, yes, that's somewhat like what I do in Marshmallow, though I package somewhat before calling update() on my managers. I have this thing that they should not bother to know about the event interfaces.

However, the reason I'd want to try using the event vector is that I would like to be able to process all events and not get them prioritized by the Robocode environment. Am I understanding it correctly that this should be possible? -- PEZ

Yes. The above code posted from the FAQ does exactly that. getEvents() returns a vector containing all the events in the robots queue, in the order of their priority (i think thats the order). Of course, the order doesn't matter; the beauty in this is that you see all your events at once in a handy little vector and handle them in the order you want, so you don't have to follow the order Robocode prioritized them. Simply import java.util.Vector, then within the recordState method you get your events like this:

 Vector events = getEvents();
then parse the vector for your events, and in an if (. instanceof .) block cast them into the appropriate types and handle them from there. Another way to build this code is simply make the condition always return true, and put the call to recordState in onCustomEvent; that's how SineSweep did it. However you can't set a priority higher than 90 so WinEvent?, DeathEvent?, and SkippedTurnEvent? get sent to the bot before the call to recordState, at least SineSweep's way; I'm not sure if the FAQ way above will keep these higher priority events in the vector but I don't think they will. In any case, yes, this is how you process events without having them prioritized by Robocode. -- Vuen

Using the method in the FAQ i have just made an event manager, however when i run it i get a NullPointerException... i barely know java, so i don't actually know what that means, and certainly don't know how to fix it... -- Tango

If you don't know what a null pointer exception is I suggest you read up on your Java before trying advanced concepts like this in Robocode. As I said previously, custom event management won't win you any ground in Robocode. It's something to consider maybe when you have a bot a few rating points behind DT. -- PEZ


OK. For Pugilist I now need to be sure that all custom events have been processed when the work inside ScannedRobotEvent handler starts. Someone has an idea how I can do this? I don't have too many bytes to play with... -- PEZ

ScannedRobotEvent has priority 10 (lowes priority) and the CustomEvent's have priority 80 so they will be processed when the ScannedRobotEvent starts... Or have I missed something? -- rozu

Is there a way to increase the priority of ScannedRobotEvent? I understand that having an event manager makes it easier to program the rest of your robot, but I think there is something else it can be used for. Robocode clears it's event queue if a turn is skipped (correct me if i'm wrong). If everything is sent to an event manager to be processed under a different thread, skipped turns won't be a problem then... -- Scoob

@Scoob Read about setEventPriority?(String eventClass, int priority) in the RobocodeAPI. Look under AdvancedRobot.

@PEZ First make sure there is a problem before you waste time and bytes fixing it. I agree with Rozu, but let us know if we're mistaken. :-) --David Alves

I thought so too, but I get weird results. Look at this bot:

package pez.tests;
import robocode.*;

public class EventPrioTest extends AdvancedRobot {
    static long registreredEvents;

    public void run() {
	do {
	    turnRadarRightRadians(Double.POSITIVE_INFINITY); 
	} while (true);
    }

    public void onScannedRobot(ScannedRobotEvent e) {
	out.println(registreredEvents + " : " + Doomed.reaped);
	addCustomEvent(new Doomed());
	registreredEvents++;
	Doomed.reaped = 0;
    }
}

class Doomed extends Condition {
    static long reaped;
    public boolean test() {
	reaped++;
	return false;
    }
}
Put it up against SittingDuck and watch the ouput. Not quite what I expect anyway... And if you remove the while loop around the blocking radar turn call it seems my reset if the reap-count doesn't do it's job...

-- PEZ

I'm still stuck with this problem. But I think it might have to do with threading somehow. It seems I read the values from different threads or something... -- PEZ

i haven't ran the code, but surely the problem is that the event is being tested every tick, and you're adding more events every scan. add removeCustomEvent() somewhere and try it... --Brainfade

No, that's not the problem. I leave the events in the queue on purpose in the example. The thing is I would need the print to print consistant numbers. Like so:

0 : 0
1 : 8
2 : 16
3 : 24
...
But it doesn't. Every other row it prints "2 : 2" and such. But if I move the actual printing to a static method in Doomed I get consistant output. I think. It seem to behave a bit arbritrary... -- PEZ

Hmm, now I get consistant output when I run the above code. Arbritrary is just the first name of this. =( -- PEZ

Indeed hmmmmmm, i have to say now that having tested it i get precisely what you wrote above, that's both with and without the do...while loop in the run method. However that's only when you run it over 1 round. if you run it over several rounds, the second printed value is always 8. I'm puzzled... --Brainfade

Actually, no its not at all puzzling. It seems to be that calling the class in a new round uses a new instance of the class, and therefore the static variable is different. The funny thing is that i have been messing around trying to store instances of the Doomed class in a static variable in the bot, in order to trying stop it beong garbage collected incase that was the problem. And from teh print statements it seems to show that 2 different static values are used:

1067 : 161
1068 : 1134
1069 : 163
1070 : 1148
1071 : 165
1072 : 1162
As you can see the number of events every 2 ticks are the right amount. Just taht there are 2 instances of Doomed accepting them. Strange that... :) --Brainfade

I got this from Mat Nelson;

Pez,

A bit strapped for time to fully analyze this, but as I recall, addCustomEvent doesn't just put an event in the event queue; It actually adds a new test to perform on each tick. So if you keep adding new tests each time ScannedRobotEvent is called, that "reaped" variable is going to go up quick. Normally you add a custom event just once, despite my lousy documentation. :)

All events exist inside any event *handler*. Not inside the test() method. So just create a custom event (returning true so the handler is called each tick) once - at the beginning of run(), call getAllEvents? inside onCustomEvent, and don't call execute() inside any (other) event handlers. Make sense?

-Mat

Anyone understand that?

-- PEZ

He seems to think you're adding events every tick because you think robocode is forgetting the events after the tick (just adding an event in the queue that time through). At least that's what I get from him saying "doesn't just put an event in the event queue; It actually adds a new test to perform on each tick". He's saying what you should do is add one event that always returns true at the start of run and have onCustomEvent() do the work currently in test() of incrementing that variable once each turn (since the always true event will call that handler once every tick when it's test()ed). He seems to have misunderstood you for the most part, but maybe there is something to using onCustomEvent(). You can still use getAllEvents? to find out the total number of custom events - or you can take advantage of each custom event calling the handler once and just put the increment code there - if you're adding one each tick, from the onCustomEvent handler. I think by "All events exist inside any event *handler*." he meant you can't find the other instances of the event from within the test() method, but that doesn't seem to make sense to me if you can access the robot's methods (including getAllEvents?); I'm not so sure about this bit.

On a me note, has it occured to you that you might have so many events going that you're skipping turns and simply not having all the test()s called consistently? -- Kuuran

Hehe, I do want the events hanging around until I say so. And I do want all those tests() methods keep doing their work. =) No, that hasn't occured to me that skipped events could be the problem. Must be checked. But when using custom events for targeting waves I have found i don't even need to ever remove them. I do it mainly because of a tidy part of me says I should. Then again the order of event execution isn't critical then as it is now when I need to keep a summary of some of the EnemyWaves... I couldn't fit building my own event queue manager in Pugilist I think. If I could fit that I could as well use a list for the enemy waves instead of trying to get Robocode keep book on them for me. -- PEZ

OK, so the mystery with the test bot above seems to have been solved. Mat Nelson, suggested I try the below code to debug better:

package pez.tests;
import robocode.*;

public class EventPrioTest extends AdvancedRobot {
    static long registreredEvents;

    public void run() {
	do {
	    turnRadarRightRadians(Double.POSITIVE_INFINITY); 
	} while (true);
    }

    public void onScannedRobot(ScannedRobotEvent e) {
	System.out.println(registreredEvents + " : " + Doomed.reaped);
	addCustomEvent(new Doomed("round " + getRoundNum() + " time " + getTime()));
	registreredEvents++;
	Doomed.reaped = 0;
    }
}

class Doomed extends Condition {
    static long reaped;

    public Doomed(String name) {
	super(name);
    }
    
    public boolean test() {
	System.out.println("Doomed created at " + name + " increasing reaped. " + reaped);
	reaped++;
	return false;
    }
}
Running it I saw that the weird results came when two scans followed without the 7 tick delay of a full radar turn. And this seems to happen at close ranges. Then the scanned event gets fired twice as the beam passes SittingDuck. Shouldn't be a problem for Pugilist though, but it has other problems. Now I am trying to figure out how the event prioirity thingy is supposed to work. I'll let you know if that becomes clear to me.

-- PEZ

If you figure out event priorities please post your results here. I have tried several times setting and manipulating the order in which events are triggered and I have never been able to succeed. -- jim

Well, here it is. Mat wrote me this on the direct question:

When you (and all other robots) have called execute(), the following happens in this order:

  1. All robots and bullets move
  2. Events may be generated (ScannedRobotEvent, HitRobotEvent?, etc.) and added to the event queue.
  3. Custom event test()s are run, possibly generating CustomEvents.
  4. The event queue is sorted by priority
  5. for each event in the event queue, the appropriate onXXX handler is called. (in order of priority, since the event queue is sorted).
  6. execute() returns.

Pretty much like I have assumed things work until I ran in to strange problems with Pugilist. But those problems must stem from my own bugs. =) As you can see from the above, event priorities doesn't even matter as long as we don't use the custom event event handler.

-- PEZ

Where does the call to onPaint() fall in the order of operations? -- RobertWalker

Yeah, before I read your response I had dug into Robocode's source and found the main game loop inside the Battle.runRound() method. Careful reading of that method gave me this list of operations:

  1. Move bullets
  2. Move robots, and zap them 0.1 if the inactivity timer is elapsed
  3. Built-in events are added to the queue (but not fired)
  4. Paint
  5. Custom event test()s are run, generated events added to queue (but not fired)
  6. Events are fired in priority order
  7. Robots process their turns

Maybe this is picking nits, but it's a bit misleading to say that the run() method gets called in the cycle; it really only gets run once. The bot's thread is simply put to sleep whenever it performs a blocking action. When it's time for the bots to act, Battle.wakeupRobots() is called, which iterates the bots and calls Object.notify() on each. It's easy to think of it as a call to run(), though, which caused a bug in the pluggable robot architecture I've been working on.

Anyway, I had some architectural concerns about putting my event management code inside the test() method on a custom listener, but the pragmatic coder in me won out. Finally (most of) my debug graphics actually match up with the game's graphics. It's sweeeeet. :) Now to figure out why my waves don't always line up with the enemy bullets... -- RobertWalker

Update: The order I listed above previously is *wrong*; the onPaint() call happens *before* the custom event test()s get run! Yikes! -- RobertWalker

Hmm.. interesting.. perhaps the Paint should be done after handling events? I could change this and see what comes out of it. --Fnl

I have a method called handleEvents() which iterates and processes the event queue manually, and sets a flag saying that it's been done. If handleEvents() gets called and the flag is already set, it does nothing. Then I call it in both onPaint() and in the test() method of my event listener custom event. This way, it is guaranteed to happen every turn, whether onPaint() gets called or not, it will *include* the ever-elusive Win, Death and SkippedTurn events, and my debug graphics will have the latest data to use for painting. This is nothing earth-shaking; just a combination of a couple of different tricks I read here, but I haven't seen any sample code putting them all together. So here it is: (Update: Some lines commented out to make it compatible with Robocode 1.4.2)

// private boolean _eventsProcessed = false;
public void run() {
	// Register custom event test() hook for event manager
	addCustomEvent(new Condition("eventManager") {
		public boolean test() {
			MyRobotClass.this.handleEvents();
			return false;
		}
	});
	// Main loop
	while(true) {
		// Combat logic here
		// _eventsProcessed = false;
		execute();
	}
}
private void handleEvents() {
	// if(_eventsProcessed) return;
	for(Event event : getAllEvents()) {
		// Event handling code here
	}
	clearAllEvents();
	//_eventsProcessed = true;
}
public void onPaint(Graphics2D g) {
	// handleEvents();
	// Debug graphics code here
}
-- RJW

I'm not sure if that will work or not, as I donno if you can actually call robot functions from onPaint (such as turnLeft, etc). However this might allow a calculation escape point for slowbots and avoid skipping turns (that is however if onPaint is not taken into consideration for a robots turn time). If it is the only thing you get is a neat way to impliment your bot. As you would have to pass the actions back onto the main thread. --Chase-san


Ok, I don't know if this is somewhere else on the wiki, but here are the default event priorities (I unzipped the source & found it in EventManager?.java):
scannedRobotEventPriority? 10
hitByBulletEventPriority? 20
hitWallEventPriority? 30
hitRobotEventPriority? 40
bulletHitEventPriority? 50
bulletHitBulletEventPriority? 55
bulletMissedEventPriority? 60
robotDeathEventPriority? 70
messageEventPriority? 75
custom events defaults to 80
skippedTurnEventPriority? 100
winEventPriority? 100
deathEventPriority? 100
The higher the priority, the sooner the event be passed to your robot.

Click on the "view" link on that Sourceforge page:

private final int deathEventPriority = -1; // System event -> cannot be changed!
private int scannedRobotEventPriority = 10;
private int hitByBulletEventPriority = 20;
private int hitWallEventPriority = 30;
private int hitRobotEventPriority = 40;
private int bulletHitEventPriority = 50;
private int bulletHitBulletEventPriority = 55;
private int bulletMissedEventPriority = 60;
private int robotDeathEventPriority = 70;
private int messageEventPriority = 75;
// custom events defaults to 80
private final int skippedTurnEventPriority = 100; // System event -> cannot be changed!
private final int winEventPriority = 100; // System event -> cannot be changed!

-- AaronR

I dunno. Try printing in your onDeath and onHitByBullet? methods. Running version 1.4.8, onHitByBullet? does not get called. -- Simonton

66: HIT!!!
166: HIT!!!
178: HIT!!!
221: HIT!!!
293: HIT!!!
584: DEATH
584: 0 skipped turns
584: Gun time: 0.0252 ms/turn, 18.00 ms/round
584: Movement time: 0.0783 ms/turn, 56.01 ms/round
584: Damage taken: 85.89 (42.95/57.05)
584: Damage given: 90.10 (45.05/54.95)
SYSTEM: simonton.beta.DCGTResearch 0019 has died

You are right, it goes straight from this:

1: Hit!
2: Hit!
3: Hit!
4: Hit!
5: Hit!
6: Hit!
7: Hit!
8: Hit!
9: Hit!

... to this:

1: Hit!
2: Hit!
3: Hit!
4: Hit!
5: Hit!
6: Hit!
7: Hit!
8: Hit!
9: Hit!
Died!

Weird... -- AaronR

Maybe -1 is a way of putting POSITIVE_INFINITY or something like that? -- Skilgannon

The only thing in the comments was "- Changed the priority of the DeathEvent? from 100 to -1 in order to let robots process events before they die" ... yet that seems to be "useless" since the onHitByBullet? isn't being called --Starrynte

Ok, using Robocode 1.5.1 I did another quick test using Windows.

75: onHitByBullet() called. This event has 20 priority according to e.getPriority()
91: onHitByBullet() called. This event has 20 priority according to e.getPriority()
107: onHitByBullet() called. This event has 20 priority according to e.getPriority()
123: onHitByBullet() called. This event has 20 priority according to e.getPriority()
139: onHitByBullet() called. This event has 20 priority according to e.getPriority()
155: onHitByBullet() called. This event has 20 priority according to e.getPriority()
171: onHitByBullet() called. This event has 20 priority according to e.getPriority()
171: onDeath() called. This event has -1 priority according to e.getPriority()
apparently the "problem" has been fixed :) --Starrynte

Robo Home | Changes | Preferences | AllPages
Edit text of this page | View other revisions
Last edited February 16, 2008 21:29 EST by cpe-75-84-83-181.socal.res.rr.com (diff)
Search: