Techkriti’09 will be hosting another Gimmick event this year. This year’s problem statement is same as the last year’s statement. You have to code a bot that will fight it out against other bots in a battle.
The problem statement is available here.
Lemme share a few tricks for those who have just started with Gimmick. I am assuming you have read these FAQs from the Techkriti site.
Creating a Bot
Creating a bot is fairly easy. However, coding a winning bot will take some effort. Once you get going, you’ll learn how to take those crucial shots, which bots to avoid, where to hide etc. with experience in the battle field. So the first thing that I will discuss is how you can make your first robot. A number of sample bots have been provided with Gimmick that you can look at for ideas, and to see how things work. Here, I will guide you to build your own brand new bot.
The first steps
Consider the simplest bot shown below.
import gimmick.*;
public class FirstBot {
public static void main (String args[]) {
String ip = args[0];
int port = Integer.parseInt(args[1]);
BI myBot = new BI(ip,port);
int myID = myBot.myid();
while (true) {
GameCycle GC = myBot.getGC();
myBot.move(40, 90, myBot.maxDis);
myBot.turn(180);
myBot.waitLong();
}
}
}
First let me describe the code that you will need in all your bots.
| import gimmick.*; |
Tells Java that you will be using objects from the gimmick package in your code. |
| public class FirstBot { |
Tells java that the object that you are describing is called FirstBot |
| public static void main (String args[]) { |
Java calls your main() method when the game runs, passing server ip and port number as arguments. |
| String ip = args[0]; |
Gets IP address of server passed as first argument |
| int port = Integer.parseInt(args[1]); |
Gets Port Number to connect to. Each bot gets a separate port number |
| BI myBot = new BI(ip,port); |
Creates a new object for BI or Bot Interface.This object will take care of all communication with the server. This is the most important class that you will use. |
| int myID = myBot.myid(); |
Get a unique ID for this bot. Will be used to identify information about this bot from GameCycle objects. |
Now let me describe the rest of the code.
First we have a while loop that is always true. The Bot keeps executing the code inside the while loop if it is alive.
Inside the loop, the first statement calls the getGC() method of the BI object. This in turn fetches the latest GameCycle object which is referred to by GC variable. For the uninitiated, GameCycle object contains all the information about the battlefield that your bot can see at a given time.
The second line calls the move method of the myBot object. The first parameter is the distance to be moved, the second tells the heading, relative to the direction the bot is currently facing and the third tells it at what speed should the bot move. In the example, we move a distance of 40 at a angle of 90 degrees. Setting the third parameter to myBot.maxDistance makes the bot move at maximum possible speed.
The third line tells the bot to turn 180 degrees. The command to turn will be pending till the bot has moved. So before looping again we call the waitLong() method of myBot object. This method blocks till all pending moves are completed. After this method returns, we loop again.
So far so good. Now let us compile this code and test it against some other bots.
- First save the file as FirstBot.java and place it in the root directory that contains gimmick, javadoc and SampleBot folders.
- Go to command line/terminal and change the working directory to the above directory.
- Compile your Java code using Java SE Development Kit (I am assuming you have one and path to javac is set in your environment variables).
javac FirstBot.java
- Edit the run.bat (on windows) or run.sh (on linux) file and change replace the names of the sample bots by “FirstBot” on the first and the third lines.
Here’s what your run.bat filemay look like:
start java FirstBot localhost 8000
start java MySittingDuck localhost 8001
start java FirstBot localhost 8002
start java MySittingDuck localhost 8003
- Run ’server run.bat’ to start the server and ‘run.bat’ to start the bots.
You wil see that the bot moves and turns, and that is all that it does. Not bad for a start.But we are still missing the part where we fire!
Fire At Will
What we have done til now is implemented a simple movement algorithm. We will now add a simple targeting algorithm which I will call “Fire At Will”.
What we will do is look at the GameCycle object that we fetch once every time we loop and look for enemies that are visible. If any enemy is visible, then we turn and shoot straight at it. Here is the code to implement this:
import gimmick.*;
public class FirstBot {
public static void main (String args[]) {
// Get Server IP and Port number from command line
String ip = args[0];
int port = Integer.parseInt(args[1]);
// Create a BI object
BI myBot = new BI(ip,port);
// Get ID for this bot
int myID = myBot.myid();
// Get team ID
int teamID = myID % 2;
// Loop
while (true) {
// get new GameCycle
GameCycle GC = myBot.getGC();
// loop and heck if any enemy is visible
for (int i=0; i< myBot.numBot; i++) {
if(GC.visibleBot[i] && (i % 2)!= teamID) {
// Firing at Bot i
double newAlpha = fireAt(GC.bots[i], GC.bots[myID]);
// Turn to aim at enemy
myBot.turn(newAlpha - GC.bots[myID].alpha);
// Fire!
myBot.fire();
// Wait for turning and firing to complete
myBot.waitLong();
}
}
myBot.move(40, 90, myBot.maxDis);
myBot.turn(180);
myBot.waitLong();
}
}
/* Helper Method. Uses info about position of enemy to calculate at what angle to turn */
private static double fireAt(BotSpec enemy, BotSpec self) {
double diff_vector_x = enemy.r * cos2(enemy.theta) - self.r * cos2(self.theta);
double diff_vector_y = enemy.r * sin2(enemy.theta) - self.r * sin2(self.theta);
double newAlpha = Math.toDegrees(Math.atan2(diff_vector_y, diff_vector_x));
return newAlpha;
}
/* Helper Method. Returns sine of given angle in degrees */
private static double sin2(double angle) {
return Math.sin(Math.toRadians(angle));
}
/* Helper Method. Returns cosine of given angle in degrees */
private static double cos2(double angle) {
return Math.cos(Math.toRadians(angle));
}
}
Try it out against the Sitting Duck sample bot. Works fine, huh? Not quite. Notice that while it is moving/turning, it ‘overlooks’ all visible enemies. We can do better if we use the information about these game cycles that were skipped while the bot was turning/moving. One approach to use this info is to call the getSkippedGC() method of BI class. This will return an array of all the GCs that we missed. However, this is more complicated approach and should be used only by experienced players. What we will do today is that we will move /turn by smaller amounts, and check for visible enemies in between. For example, instead of turning 180 degrees, turn, let say, 45 degrees in each loop. When you turn less, you spend less game cycles waiting for these moves to finish at the call to waitLong() method. I will leave this as an exercise to you.
When you have made changes, you may want to try out new movement and targeting algorithms on your own. Test out your bot against other sample bots and get ready for the challenge other players when the online rounds begin!