Multithreading
Threads can be used to offload big chunks of work without skipping turns. Robocode imposes some specific constraints on robots using threads:
- You can only run 5 threads (not including your main thread) at a time. Previous threads have to terminate before the security manager will let you run more.
- This does not affect thread creation. You can create as many Thread objects as you wish, but you may only run 5 at a time.
- You must stop your threads at the end of each round, or you will throw an Exception (and all the nasties that entails).
- This means you must override both
onWin(...)
andonDeath(...)
to ensure your child threads get stopped at the end of every round, win or lose.
- This means you must override both
- You must also stop your threads at the end of the battle, or your robot will throw an Exception if the user stops a battle before it finishes. It's also annoying having to wait for robots to be killed when they don't do this (especially if you're debugging them!)
- To ensure your robot also gets stopped when the user aborts a battle, you must override
onBattleEnded(...)
and stop your threads if they haven't stopped already.
- To ensure your robot also gets stopped when the user aborts a battle, you must override
Needless to say, threading is not for the faint of heart.
Fairness Concerns
Since threads aren't stopped during the enemy's turn, they can be used to lower the enemy's score. You could spawn a thread which does heavy work during the enemy's turn to increase the chances of them skipping a turn. Needless to say, this is not cool.
Because of these concerns, robot multithreading may not always be supported by Robocode into the future.
There is also a consensus that for such reasons, threading should not be used in bots meant to compete in RoboRumble. While there is for now one bot (Toad) which is in the rumble currently and uses multithreading, one should expect opposition to trying to enter a new multithreaded bot.
Sample Code
The following code spawns 5 threads (one per turn) which print a message each turn for 100 turns, then terminate.
import robocode.*;
public class ThreadDemo extends AdvancedRobot {
// This will be used to tell our threads to terminate. To do this, we must
// be able to modify its value, so a simple Boolean object will not do.
// It will also be used to make our threads wait until the next round. For
// this, it must inherit from Object to get at its notifyAll() method.
final boolean[] token = {true};
// Number of threads we have spawned.
int threadCount = 0;
@Override
public void run() {
// Get the radar spinning so we can get ScannedRobotEvents
setTurnRadarRightRadians(Double.POSITIVE_INFINITY);
while (true) {
out.println("Robot time: " + getTime());
if (threadCount < 5) {
final long spawnTime = getTime();
// Quick and dirty code to create a new thread. If you don't
// already know how to do this, you probably haven't learned all
// the intricacies involved in multithreading on the JVM yet.
new Thread(new Runnable() {
int runCount = 0;
public void run() {
synchronized(token) {
while (token[0] == true && runCount < 100) {
System.out.printf(
"\tHi from Thread#%d (current time: %d). Repeat count: %d\n",
spawnTime, getTime(), ++runCount);
try {
// Sleep until re-awakened in next turn
token.wait();
} catch (InterruptedException e) {}
}
}
}
}).start();
threadCount++;
}
execute();
}
}
@Override
public void onScannedRobot(ScannedRobotEvent event) {
synchronized(token) {
// Wake up threads! It's a new turn!
token.notifyAll();
}
}
// The following events MUST be overriden if you plan to multithread your robot.
// Failure to do so can cause exceptions and general annoyance.
@Override
public void onWin(WinEvent e) {
// Politely tell our threads to stop because the round is over
synchronized(token) {
token[0] = false;
}
}
@Override
public void onDeath(DeathEvent e) {
// Politely tell our threads to stop because the round is over
synchronized(token) {
token[0] = false;
}
}
@Override
public void onBattleEnded(BattleEndedEvent e) {
// Politely tell our threads to stop because the battle has been ended.
// This gets called whether the battle was aborted or ended naturally,
// so beware of duplication with onDeath/onWin (if that is important to you).
synchronized(token) {
token[0] = false;
}
}
}