Build the best - destroy the rest!

Monday, 15 February 2010

Robocode .NET - introduction

Visible Robot differences

between Java and brand new .NET API for Robocode robots.

  • Namespaces start with uppercase Robocode.AdvancedRobot instead of robocode.AdvancedRobot from Java.

  • Methods start with uppercase AdvancedRobot.Execute() instead of AdvancedRobot.execute() from Java.

  • Getter and setter methods are represented by properties BulletHitEvent.VictimEnergy instead of BulletHitEvent.getVictimEnergy() from Java.

  • Droids are marked with Robocode.IDroid interface instead of robocode.Droid from java.

  • Painting robot uses Robocode.IGraphics interface, which is similar to System.Drawing.Graphics.

  • Robocode.Condition could be implemented by delegate, see SampleCs.Target.

  • Interactive robot should use Robocode.Keys constants, see SampleCs.Interactive.

  • team Robot messages should be binary serializable and marked with [Serializable] attribute.

  • .NET robot pays more work to submit each turn into battle (to overcome the interop layer). We will probably adjust CPU time given to robot to keep fair competition. The ratio Java/.NET is for discussion yet.

  • Access to files is done thru AdvancedRobot.GetDataFile() which returns System.IO.Stream

  • there is difference in implementation of floating point between CLR & JVM. The JVM uses strictfp - rounding after each operation, which is more portable. Whereas CLR uses FPU wide registers to achieve better speed and precision. If you recursively recompute same numbers, it will produce a little bit different result. Nothing to worry about.

  • System.Threading.Thread is banned, use Robocode.Thread instead.

About the implementation

Two years ago, when we came with the idea of introducing .NET for Robocode we had no idea what so ever how much work it would require from us. :-O
We had raw idea that the main problem is speed. Robocode version 1.6 and below used shared state in RobotPeer between Battle thread and Robot thread.
Each call on the Robot API was synchronized to one big mutex. Now imagine that each call from a .NET robot will come through interop layer and then through synchronization in Java. Our estimate was that we would need 10.000 calls per second across CLR/transport/JVM boundaries to satisfy all calls which robot does on RobotAPI. That's not very realistic!

After several trials and failures we came to idea that we will make communication between robot and battle chunky rather than chatty. We cache all commands from robot till end of turn (Execute) and then we send it to battle as a message.
The other way around it's the same. At end of turn, the battle produces a message for each robot. The message contains all the information a robot could ever ask for, i.e., all the events, positions - everything. The message is used by robot as cache for all queries on the Robot API.
And that's big success. The Java version of Robocode speedup about 10x in version 1.6.2. This design as well allows for .NET robot to cross the CLR/JVM boundary just once per turn! The messages are serializable to binary form, so the interface between robot and battle could be reused for C++/Delphi/Python/Ruby/Basic/Foxpro :-D robots in the future.

So now, how to connect two virtual machines? Webservice or tcp - slooow, named pipe - slow, COM - yeah maybe, but COM registration, JNI & PIvoke & C++ & in same process - that would be almost it.
In the end as by-product of need for Robocode, I started off new opensource project - jni4net. It's is fast intraprocess bridge, without any native code and it's object oriented. It's generally useful for .NET+Java solutions.

There were challenges about sandboxing the robot in .NET. We use AppDomain, CAS security and static code analysis to prevent you from cheating.

There is plenty of work ahead. I would not name it now, because we would like to hear from you, what you think we should improve?



Nat Pavasant said...

Well, I have always wondered why Robocode 1.7.x was a lot faster than used by Rumble. Now I understand =)

Alex Schultz said...
This comment has been removed by the author.
Alex Schultz said...

Personally, I think the best solution would be, rather than a static ratio, to have each individual robocode install benchmark this overhead just the CPU constant. I say this because this overhead may in the future vary between different platforms (i.e. once jni4net supports Mono), and also the same measurement of overhead could perhaps be used for any other future robocode plugins.

Pavel Šavara said...

@Alex, yep, valid point.

Rick Henderson said...

I'm going through the Tutorial on building a .NET Robot (I'll be using VB) but when you browse for .dll files, all you really have is .jar files. So how can you add a reference to the robocode.dll?

I know this isn't the right spot for this question, I'm a teacher trying to put together an example in a short time.

Keep up the good work!

Pavel Šavara said...

@Rick, you have to install .NET plugin for robocode, for example

tebbaerty said...

.Net debugging is not working. I searched everywhere for some help, but it seems there are not to much programmers over c# and visual studio. Do you know where can i find some help ? The main problem is that java.exe process is not does not show up as managed,x86. is showing up just as x86 in attach to process windows. So even if i attach my breakpoints are not hitting. I added the Ddebug=true. What else ?

Flemming N. Larsen said...

With a similar issue, I proposed a suggestion here:

That is using the 'devenv' tool from Microsoft:

Alternatively use the DebugExe tool:

I should like to hear if this works?

tebbaerty said...