Difference between revisions of "Archived talk:Raiko 20090429"

From Robowiki
Jump to navigation Jump to search
 

Latest revision as of 04:51, 8 October 2009

Sub-pages:
RaikoVersion History - Archived Talk 20090429
       Archive        This is an archive of past discussions. Do not edit the contents of this page. If you wish to start a new discussion or revive an old one, please do so on the current talk page.     

Cool. It seems strong. But it lacks a flag. Is it a secret where you are from? =) -- PEZ

I'm from... several places. But let it fight under the Irish flag. It needs one defender in the Rumble!

Very impressive indeed! Congratulations and welcome. (or should i say "badcome" since my bot had been overcame? :^))) -- Axe

A splash entry indeed. I'm not sure I have the flag of the Ireland around. It might take a while to find it. -- PEZ

By the way, Raiko's movement seems to have the same problems that Musashi have against bots like Barracuda. I (and other people too) think that is a weakness near GF 0.0 -- Axe

Thanks guys! :-) It's performing even better than I'd hoped against the top ten (well saved data helps, but this is vale tudo, heh). Now if I can just fix these problem bots. And the indomnitable VertiLeech... ;-)

Axe, yes, seems our movements are pretty similar in concept! Damn, I thought I'd created something original. ;-) I suspect the problem is more subtle than a spike at GF 0; most of my spikes are around .7 or .8. I think that this type of movement mostly produces a mountain-profile, i.e. a peak with the other visits evenly distributed to either side. As a result averaging the offsets is just as good as full-blown guess factors, and learning is faster.

I also had some other interlocking problems with Barracuda and others;

  • Segmentation on the last time a bullet stopped doesn't work well on bots which don't move continuously, or which change velocity to flatten their profile. In fact it just introduces junk noise.
  • Barracuda segments based on distance from walls and corners, which is a big problem area still.
  • Barracuda and FloodMicro use dynamic distancing. My movement is only flat at a certain range, and they were slipping inside and taking advantage. Melee bots were also a problem because they tend to run away and get outside my combat range.
  • I was maintaining the wrong distance, because I couldn't multiply 160 by three. Duh... :p

My current development version is no longer vulnerable to these bots, but we'll see if I've introduced any other problems. Teach me not to do all my testing vs the top ten. :\ 0.12 coming soon... - Jamougha

You are pretty observant for a newbie. =) I also think Barracuda is able to expose more than vulnerability to head-on targeting. Maybe Shadow too has some particular distance where it is vulnerable to head-on and then Barracuda finds that distance and leads ABC to think it's a more general problem? Testing against the top-10 isn't wrong. Only the very best (read Paul and iiley) can afford thinking about performance against the weaker bots as well. -- PEZ

  • Or those who want to be among the very best. I say agian: Beat everyone else as well as they do, then beat them. Lacrimas is a case study of this. He can beat DT 1-v-1 but is still #2 overall because he can not beat everyone else. It may seem counter intuative, but I suspect that beating everyone else will be easier than beating SandboxDT and Lacrimas. -- jim
  • It doesn't beat DT in short battles, does it? DT is undefeated in PremierLeague. I think you get forward a good bot faster by testing against top-10 than bottom-10 or a mixed-10. Besides, it's cool with a newbie who aims that high. =) -- PEZ
    • The approach I have always taken is to focus on the bots that I perform worse than expected against. One nice side affect of fighting these bots is that they sometimes seem to be geneticaly engineered to take advantage of some weakness that I did not know existed. The first one that I can clearly remember was Aroura. It had the nasty habit of cornering me. The next one was one of the early versions of one of the Flood* bots. Those could pin me to a wall. Next set were some of the tobe.* bots (particularly Gravitron). But as VertiLeach and Jekyl clearly point out, there is more than one road to the top (and neither of ours has been completely successful so far) so feel free to chose your own path. -- jim
    • I count VertiLeach as a complete success. It was made to be a competetive mini. Getting the #1 mini is way, way beyond my expectations. If Verti is not a complete success I don't know what is. -- PEZ
      • This is complete success. Everything else is only an immitation. -- jim

My philosophy is, try to beat everyone... :-) While it's great to optimize movement against only the best, guns involve a lot of voodoo. I've ended up with one gun which apparently hits DT more than it hits many bots around 30th in the rankings. Fun, but not useful (in a mini...) -- Jamougha

Good philosphy. =) But still you'll have to decide what bots to test against. I think it's movement where you can make your bot extra strong against the weak while maintaing strength against the strong. And I also think movement holds more voodoo than targeting. =) Do you mean above that segmenting on time since enemy was stationary worked against Tityus 0.6.0? I'm curious, because I would guess that T 0.6.0 changes velocity about as often as it is stationary. -- PEZ

That segment appeared to make all the difference, yes, though my movement is also a small factor. Unfortunately I only noticed the new release as I was about to make mine, and ended up staying up till dawn trying to fix it; no luck, though. I either loose against you or about 5 bots way down the mini rankings.

And I've noticed that you're a movement queen. ;-) Marshmallow has always seemed unhittable, with an absolutely beautiful profile, but gets a consistently worse hit rate vs one of my movements than head-on targeting. Bizzare...

- Jamougha

You are the second to tell me Marshmallow is a good movement bot. Yet, that movement was too complicated. I always fail to port it to other bots without it losing whatever it is that makes it good. I think it might be a bug actually. ... And it's guns are too complicated too. Good against some bots, but can be tricked badly by some movements... It's good at catching bad wall movement and if you don't have that the guns are more or less worthless. So, my new Tityus movement is vulnerable to segmentation on time since stationary? That might explain why it's problem bot list looks like it does. I have no clue how to fix that weakness though so I'll have to live with it a while I guess. At the movement I find some comfort in that 0.6.1 manages to keep some points ahead of Raiko. =) -- PEZ

One way to fix that is probably to have T stop dead every so often. Raiko finds this an bewildering habit. :-) However since no-one uses that as a segment AFAIK I think you'll be pretty safe. And 0.6.1's movement is too damn good anyway! - Jamougha

The difficulty of that segmentation is granularity, but it helps against Iiley's bots, and I can only assume it helps against the FloodMovement. However, time since stationary might be too simplistic - I think time since a change in velocity at all might be more effective against FloodMini, and against a goto-style bot (like Iiley's and PEZ's are), even time since a change in direction (or change in velocity). Empirically, 3 or 4 segments where the highest one is the amount of time for a bullet to reach the enemy or more is about the right number. I have yet to actually release a bot that uses that, though (but it fits in the dev FloodMini, if I choose to include it). -- Kawigi

Swiffer uses a segment like that, though it uses change in lateral velocity and time, making it a combined acceleration and "time since velocity change" segment. It exploited a weakness in DT 2.31 movement and made Swiffer beat that DT. Paul somehow knew how to fix that weakness though. I could only enjoy beating DT in battle for half a day ... -- PEZ

Hah, so I'm not alone on that seg. Kawigi, unfortunately I haven't found that it helps against flood-style movement; in fact just the reverse, it actually decreases peformance. Actually not much I do works well against Flood movement :\. I'll try messing with the granularity, though, and see if things improve. Mostly I've found it works best against bots which move at top speed all the time.

PEZ, coincidence, I started using it when I did all my testing vs DT 2.31 :-) works well vs the Duelist series, too. - Jamougha

I don't think you should revert back to 0.1. They seem about equal in strength and you probably need very little tweaking on 0.12 to get it to pass 0.1. -- PEZ

Not perchance because it got trashed by everything you ever wrote? ;-)

Anyway, I posted 0.14 just before you wrote, so it's a bit late. :-) 0.12 was a dicey proposition in the first place. By the time I'd seen it's position I was working on a more promising fork, so I decided I'd rather be back in the mini-top ten in the meantime. Now it's fingers crossed for the new version... - Jamougha

I don't mind my bots trashing yours. =) However, I have great experience in revoking good bots too early. So take this advice of mine and wait for your bot to get some 500 battles under it's belt in the rumble before deciding what to do with it. -- PEZ

Fair enough; I admit I'm still blundering about like a bull in a china shop while trying to make improvements, so there may well be some good ideas in there. I'll let 0.14 settle for a bit. And who knows... there's just about enough space there to implement a virtual gun. 0.12 may yet see it's revenge. - Jamougha

Hmmm... well I'm postponing my decision about 0,14's ranking, but this is nice while it lasts :-

  • pe.SandboxDT_2.41 54.0 1 31-12-2003:1:12 37.2 16.7
  • pe.SandboxLump_1.52 52.8 1 31-12-2003:0:39 47.5 5.2
  • pe.mini.SandboxMini_1.2 64.2 1 31-12-2003:1:30 57.5 6.7

- Jamougha

Way cool. Way cool! -- PEZ

It seems Raiko is strong against quite a few strong bots. A question; You have a static method looking like this:

    private static double maxEscapeAngle(double bulletVelocity) {
	return 1.2*Math.asin(MAX_VELOCITY / bulletVelocity);
    }

What's the 1.2 about?

Also I found I improved the Tityus gun when I didn't do:

	bearingDirection = deltaBearing < 0 ? -1 : 1;

but instead:

	if (deltaBearing < 0) {
	    bearingDirection = -1;
	}
	else if (deltaBearing > 0) {
	    bearingDirection = 1;
	}

That is, if the enemy is stationary I keep the bearingDirection it previously had. On the other hand; I did this to be able to better hit stop-and-go bots like the Cigarets and Tityus 0.4 -> 0.5. Yet you seem to hit this movement better than I so I might try it your way instead...

-- PEZ

Well again the saved data help, but it beats 3-4 of the top ten with clean files, given enough rounds. Not sure why I introduced such a huge problem against some bots, though.

Didn't I say I multiplied everything by 1.2 for the hell of it? ;-) OK, I'm not sure if that factor actually helps. However when I hooked RoboGrapher up to Raiko's gun I noticed that there seemed to be some overhang, with bots moving more than a 1.0 guess factor, so I put in 1.2* to be able to see the full graph. Then I never got around to taking it out. :-\ I intended to go through the code and figure out if it was a bug or not, but haven't had time yet.

I'll definitely try your gun modification, it make a lot of sense. I think the reason Raiko hits Cigaret so much is the near-wall segment; I had the same experience as Kawigi mentioned on Tityus' page. - Jamougha

A GF of more than 1 is certainly a bug. I avoid that bug by simply checking the GF as soon as it is calculated and if it is more than 1 or less than -1 I make it exactly 1 or -1. -- Tango

That doesn't fix the bug though. It only makes your bot not crash (which is of course an improvement anyway). I had that overhang problem with RoboGrapherBot a while, but I managed to fix it. Don't remember what it was though. I'll let you know if it comes back to me. -- PEZ

Indeed. I "avoid" the bug, rather than fix it. I don't know how often it happens, i should probably check, i have a feeling i fixed it, so i could remove the checks... -- Tango

Well, I don't think you should remove them. Even if you fix the bug, sometimes, I don't know why, you get indexes outside the range. Maybe it is because of lost scans or something. Here's a tiny function you can use to force a value inside a range:

    private static double minMax(double v, double min, double max) {
	return (Math.max(Math.min(v, max), min));
    }

It adds clarity I think. and it also saves codesize if you need to use it more than once. Use it like so to increment a visitcount:

   visitCounts[minMax(index, 0, NUM_INDEXES - 1)]++;

-- PEZ

I belive i only use it once, so i would stick with putting the code inline, i think. The only reason i would remove it altogether was if i decided to make it a mini/micro/nanobot and needed the bytes. -- Tango

In my code I multiplied that asin by 1.2 as well, but eventually tweaked it down to 1.15. I'm not sure what the actual value is, but I believe it has to do with poor research into the geometry of the "maximum escape angle". The asin(MAX_VELOCITY / bulletVelocity) works only if the maximum escape angle is reached by perpendicular motion (to transcribe the base of an isoceles triangle). I believe the problem has to do with bots moving toward you instead of perpendicular to you, and it might also (mayyyybe) have something to do with the discrete nature of Robocode physics. If anyone wants to sit down and puzzle out the actual solution to this problem, I would definitely like to hear the answer. -- nano

  • I did this some time ago but was promptly told that the problem didn't exist and I was an idiot. Your mileage may vary. -- Kuuran

Look at iileys bots for code where someone has given the thing more thought. -- PEZ

The culprit is always either the discrete tick-based movement or missed scans (I suppose it could also possibly be using invalid bullet velocities, but I assume you would have made checks to get that right). The Math.asin(MAX_VELOCITY/bulletVelocity) is the max escape angle. You won't get to as far of an angle if you don't move perpendicular to the direction to the enemy when he fired. It is also possible to just be one tick off in tracing the wave. The max escape angle with any direction of movement can be found using the law of sines, but the key variable is the sine of that angle (the one between the direction you move in and the one to the enemy). The peak of the sine function is at 90 degrees, as does the function. -- Kawigi


You have managed to make Raiko stronger again. Congrats! -- PEZ

Thanks! :-) Now I have to go tweak it vs Tityus, BlackDestroyer and Griffon, lol. -Jamougha

Hey what did you feed Raiko 0.2? Impressive! -- PEZ

Very impressive indeed! -- ABC

What did he feed? That's easy: our bots... :) Congrats, Jamougha!!! -- Axe

Woohoo, cheers! :-) I don't think it will stabilize there, lol. However the movement is completely untweaked since I changed it's strategy, so there's some room for improvement. All I've done is make it more wall-fearing and given it a truly stupid movement at close range. - Jamougha

You mean "less" wall fearing, in'it? -- PEZ

I think that Musashi needs some stupid moving at close range too. I've been a total failure at close ranges, never getting it flat... Unbelievable. So, or the movement get stupid, or i get less stupid. (Stupid phrase, i know!) :) -- Axe

Ah! The "No fear from the walls" thing! We were talking about this at PEZ's RandomMovementBot. -- Axe

PEZ, nope, it should be more wall-fearing (unless it's just a bug :p) as it now changes direction when the approach angle is less than Math.PI/3 inside distance 400. Stops me ramming the other bot.

Axe, yup, I think it's a problem with this type of movement generally - it's very hard to flatten it at all ranges.

Oh, and cheers PEZ for the flag! :-) - Jamougha

You are right that the movement still has to be tweaked. RoboGrapher tells me it's rather weak against power 3 bullets at mid to long ranges. Seeing how well Raiko does, this tells me two things. 1) It might have been a mistake to focus on higher powered bullets. 2) Raiko's gun must be quite strong. Actually three things.3) Raiko can go very, very far if you tweak that movement. -- PEZ

Pre-learned data also helps... ;) -- ABC

Not that there is much pre-learnt data in the bot package, so I don't think it makes a very big difference. -- PEZ

Problem-bot data can make a big difference, like Musashi demonstrated. And how is data on 41 bots "not much"? Not that I'm complaining, it's stil a valid tactic... -- ABC

It's not 41 rumble bots. And it's not much compared to VertiLeach and Griffon and some others. We would need to see an empty Raiko to know how much difference it makes though. -- PEZ

Yup, saved data helps a bunch. ;-) It's not high quality, mostly 50 - 100 rounds a bot, but with a heavily segmented gun it's useful. I'm also curious about how it would do without it, but... not that curious. :-) Ah well, down to sixth. The tweaked version will have to wait a few days. - Jamougha

You have got rid of the wall segmentation too, haven't you? And what about that granularity thing in time-since-stationary? Tell, tell. =) -- PEZ

OK, ok. :-) The wall segmentation is still there, it's just more obfuscated. (I was trying to reduce codesize so that I could fit DynamicSegmentation in.) The time-since-stationary segment is governed by the vSegment method. It calculates timeSinceLastChange/(enemyDistance/bulletVelocity) and segments at values of .2, .6, and 1, 4 segments in total. Thanks again to Kawigi for the suggestion. :-) I also reverted to segmenting on distance, not bullet travel time. Because of the way the bullet power is chosen everything was being squashed into two segments, and dissimilar cases were overlaping. As a result the 250-300 values were badly contaminated, and I couldn't reliably hit bots which change ranges around this value. I'm going to have to find a way to balance that out, though, because it causes problems against several strong bots. You'll notice in now looses to Sedan, something which hasn't happened in my testing since 0.1.

Btw, I just graphed the profile and I was shocked. If I'd seen it then I'd never have released this version! I suspect the reason it works (sort of) is that only low-ranked bots spend any real time in the near-range segment... most of those use either head-on or average-offset-targeting, and the spike at 1.0 obviously works against head-on, while average-offset targeters are firing at the foot of the peak. I guess it's not whether a profile is flat, it's whether the opponent can hit it or not. ;-)

I've also just realised that my grapher is segmenting every 140 clicks, and my movement modifiers are segmenting every 160. Er, perhaps that explains why trying to improve near-range movement always messed up the wrong segment. :-\ Tweaking should be a bit easier from now on. - Jamougha

Tityus chooses a given bullet power for a given distance index so that it shouldn't need segment on bullet power. Segmenting on bullet travel time is a bit like doing bullet power segmentation. -- PEZ

You're right, and it's an elegant solution. The reason I switched to bullet travel time was to deal accurately with the cases where I'm firing based on the opponent's energy. Those are often the most critical, and they can contaminate the other data if e.g. I fire lots of 1.1 value bullets at the 300-range in the third round. - Jamougha

I don't know if those shots are often most critical. If the enemy is low on energy you will eventually kill it even if you don't have data collected for those bullets. My bots usually don't collect data for bullets below a certain bullet power (or in some cases if the fired bullet has a lower power than the default power for a certain distance). That way you do not contaminate the data for start and mid game. -- PEZ

BTW. It's scary to see Raiko's MiniBot rating raise as it does right now... It will soon pass VertiLeach I think. That's no fun! -- PEZ

There you go, it's gone down again since you said that. ;-) Your number one spot is safe, I think... maybe in the next release. But Vertileach is hard to unstick... <groan>

Raiko is currently ceasing data collection when it's firing low-power bullets, but with a 5-segment gun I'm keen enough to collect all the data I can. I'll give the whole thing some more though when I have time; I'm a sucker for trying to create 'optimal' solutions. - Jamougha

I know that feeling. It's worth exploring all paths of course. Sometime I feel like I have. But I probably sometimes explore a strategy or technique and do it the wrong way and then draw the wrong conclusions. That's what cool with a new mind in the game. Anyway, you might try with a four way gun and see if the faster learning pays in the RoboRumble tournament format. -- PEZ

Man! 1894 after 380+ battles. I knew you would make the jump if you applied Axe's trick, but this high?? Now I am pretty sure I will lose my mini-crown too. I really needed some sleep tonight! -- PEZ

iiley picked a nice time to release a broken Lacrimas, too. ;-) I'll have to give you another few days before the next version, university is murderous at the moment. - Jamougha

Congrats man!! But if u have used my trick, u at least should had the decency of sending me a bottle of a fine irish wiskey!! What a shame... -- Axe

I surely deserve that bottle, look at you LRP graph (If u need i'll send my adress for the posting)!! -- Axe

Lol, thanks Axe, I definitely wouldn't be there without you. Mail me the address and I'll send you a bottle next time I'm not broke. ;-) - Jamougha

I don't know about the "I definitely wouldn't be there without you", i am anly interested in that irish wiskey. Consider it a deal, please send the bottle when u become not broken, ok? -- Axe

A discussion worthy of the BeerPoet. =) I sure wouldn't mind a dram or two of some 12 year Bushmill malt. -- PEZ

Bushmill's, huh? Not a bad choice, even if I do say it is a Vodka drinker. ;-) Go on then, send me an address and I'll get round to that too. (Damn you've both caught me in a good mood! :p) - Jamougha

Bushmill's is one of my very favourites among whiskeys. Very oily and with a lot of fruitiness to its after taste. -- PEZ

Hey, why don't you sign Raiko up in the RobocodeLittleLeague? I bet it would rule the mini 1v1 division. For a while at least. Until I have gotten Tityus where I want it. =) -- PEZ

Err, LittleLeague? Ah, OK.. yes, I'll get around to that, thanks. :-) But probably after I see what you have up your sleeve with 0.9.5. That's a nice graph on the Shadow page... - Jamougha

Relax. I have trashed that development track. I couldn't get the profile any flat when clear of the walls. Strange, very strange... I'm working with a new system now. It will take a little longer than usual for me to get next version out. -- PEZ

I say. Raiko 0.3. WOW! -- PEZ

Aaargh, this is torture... it keeps skipping above and below Paul... make up your mind, silly bot... :-) we'll see where it settles. - Jamougha

Still, this is a milestone in Robocode bot development. A MiniBot fightingfor the #2 spot with SandboxDT!!!! I like to see things like this:

Fighting battle 26 ... jam.mini.Raiko 0.3,pe.SandboxDT 2.41
RESULT = jam.mini.Raiko 0.3 wins 2718 to 2487

=) Sweeeet! -- PEZ

Hehe, cool :-) 1918 with 400+ battles, it just *might* stay at #2 until the next DT. :-) And still with 50 bytes to play with... - Jamougha

Mini Rankings <--- is anyone else not seeing Raiko there? -- Kuuran

This usually happens - it takes a while longer for the mini rankings to update. I just checked the codesize at 1453, so it should show up later. - Jamougha

The delay you're talking about is while it waits for it's first match against a mini. It's already run 600+ matches at the time of that writing and fought most of the minis in the minirumble. -- Kuuran

True - however I've often noticed bots fighting more than 400 battles without appearing in the minibot rankings, then showing up with only 30 battles fought. Of course I have no idea how RoboRumble works, so I've no idea why this should be so. - Jamougha

If someone is running RR set to "General" maybe it only uploads to Roborumble and not the mini leagues? No clue why else that might happen. Albert? -- Kuuran

Congrats, the second to overcome DT! Things are getting tougher and tougher... -- Axe

I see nothing wrong with the mini ranking table. In fact it looks "cured" somehow. =) -- PEZ

The premier leagues also seem to be slow to update. -- Alcatraz

Yes, they are. Only updated twice a day. -- PEZ

There is a problem with Robocode security manager that prevents RR@H to check the robot codesize once the first iteration has runed. It can happen then that the battles used for the general ranking are not used with the mini ranking. The only work around is to stop the client and start it again. -- Albert

Top result from a mini bot, congrats - looks like a rating of 2000 must be possible. Good news, my contract has finished and I am unemployed, I can now spend some time on DT :) - perhaps I can reach the 2000 mark first. -- Paul

Good news? If I could I would employ you myself. =) -- PEZ

I have two suggestions for what you could do with those 509 bytes left.

  1. Stop Raiko from embarassing itself on arenas != 800X600
  2. Make Raiko hit disabled opponents

The first change won't win you more points in RR@H, but the second change might. -- PEZ

Raiko 0.3 is my worst result: 37%. Sure that is a good bot, what a hell did u feed it with (excluding our bots, of course)? -- Axe

Paul, thanks. :-) There's still some optimization to be done on this movement, as 0.3 still doesn't react quite right to enemy bullet power. The dev version has a much nicer profile against DT. :-)

PEZ, oops, I say I'll fix it against disabled opponents every version and never do it. This time. :-)

Axe, it's all it the movement. ;-) -- Jamougha

I imagine. The movement is allways my weak point, i'm a complete failure... :( Probably is that the reason of my cheap tricks, i have to fill the weakness with something... Btw: that Raiko is not AM? If it isn't, i think that it is probably the greatest "pure moving" ever! Imagine: beating DT without AM... And with a mini... Wow. -- Axe

Nope, no AM - unless you count the MusashiTrick, of course. ;-)

The movement of Raiko is simply great and so small. A nano with this movement is absolutely no problem. If you think of it perhaps you can do something with this: RaikoNano. -- rozu


Would you care to share some of the theory behind Raiko's movement? Particularly why you settled on a 1 - x^x curve for probability of direction change and why you chose the x you did? I'm really curious as to why this works so well.. -- Kuuran

Ah, OK... well, it's a simple statistical idea. First assume that you can't get the negative guess factors flat. (After some e-mails with PEZ and a bit of testing I think this is probably not true in general, but it probably is true for a constant-probability-of-turning movement) This allows us to create a boundary condition on flatness, which is that the probability of getting to the edge of the escape area is roughly proportional to 1/n, where n is the number of ticks before a bullet arrives. (That is, the probability of getting to the edge of the escape area should be the same as the probability of getting to any other positive guessfactor.)

If the probability of not turning on a given tick is p, then the probability of not turning in the next n ticks is p^n. This is also the probability of reaching the edge of the escape area.

Equating these conditions :-

p^n = 1/n

=> p = (1/n)^(1/n)

So I knew that for a movement with a constant probability of turning each tick to be truly flat, it had to follow something like that formula. Some simulations indicated that it would flatten well enough, thanks to the fact that it takes time to turn, which smooths out the mid-range positive guessfactors.

Unfortunately I never tuned it properly when I first started, and got sidetracked for Raiko 0.1->0.22, but Tityus 0.9.1 gave me an incentive to get it to work right. :-)

There are different ways to achieve the same result with other movement types, but I'll leave those as an exercise for the reader. ;-) -- Jamougha