Tuesday, September 8, 2009

Robocode Newbie

Last week was our first introduction to Robocode. Robocode is a game where players create software robots using the Java programming language. These robots can either go head to head with another robot or be placed into a free-for-all melee with many other robots. Games like Robocode are a great way to get Computer Science students interested in programming. If I were teaching an AP Computer Science course in high school, I would have them make robots after they take their AP test as a reward for sticking it out for the entire year.

While Robocode is a great educational tool, it is also complex and deep. The phrase "easy to play, difficult to master" comes to mind. This is especially apparent when viewing the source code of the various movement and targeting strategies on the Robocode wiki. As part of our brief introduction, we were asked to implement the simple robots listed on the assignment page. While implementing the robots, I got to know the Robocode API really well. I did implement all of them, but not without some trouble.
Moving the robot to a specified point is not as simple as "moveRobot(a, b)". While I could've written an implementation that doesn't require trigonometry, I chose to brush up on my old math skills. I created two functions; one that determines how much the robot should turn and one to actually turn the robot.

/**
  * Gets the angle to a point on the battlefield.
  * @param x X coordinate of the point to move to.
  * @param y Y coordinate of the point to move to.
  *
  * @return The angle between the point and the Y axis in degrees.
  */
private double getAngleToPoint(double x, double y) {
  
//Get heading to the center.
  
double distX = x - this.getX();
  
double distY = y - this.getY();
  
  
double angle = Math.atan(distX / distY) * (180.0/Math.PI);
  
  
//Check if the robot needs to move south instead of north.
  
if(distY < 0) {
     angle
+= 180.0;
  
}
  
  
return angle;
}

/**
  * Turns the robot to set its heading to point (x, y).
  * @param x X coordinate of the point.
  * @param y Y coordinate of the point.
  */
private void turnToPoint(double x, double y) {
  
double turnAngle = this.getAngleToPoint(x, y) + (360 - this.getHeading());
  
  
this.turnRight(turnAngle);
}

The trickiest part was implementing Firing04. To track the robot, we needed to predict where the enemy robot was going next and then turn in that direction. Fortunately, the ScannedRobotEvent class provides a lot of information about where the robot is going. We just needed to calculate the angle at which we should turn to track the robot. Using both the law of cosines and the law of sines from trigonometry, I think I came up with a reasonable solution to tracking the robot.

//Steps through turning the gun by this interval.
private static final double TURN_INTERVAL = 20.0;
private ScannedRobotEvent currentEvent;

public void run() {
  
this.setAdjustRadarForGunTurn(false);
  
  
while(true) {
    
//Scan for a robot.
    
if(this.currentEvent == null) {
      
this.turnGunRight(TURN_INTERVAL);
    
}
    
    
//If a robot is found, track it.
    
else {
      
double absoluteHeading = this.getHeading() + currentEvent.getBearing();
      
//Use the law of cosines to approximate the new distance after the enemy moves.
      
double predictedAngle = 360.0 - currentEvent.getHeading() - (180 - absoluteHeading);
      
double predictedDist = Math.pow(currentEvent.getDistance(), 2) + Math.pow(currentEvent.getVelocity(), 2) -
                 (
2 * currentEvent.getDistance() * currentEvent.getVelocity() * Math.cos(predictedAngle));
      
      
//Use the law of sines to approximate the new absolute heading
      
double turnAngle = Math.asin((currentEvent.getVelocity() / predictedDist) * Math.sin(predictedAngle));
      
      
//With this information, turn the gun to find the enemy
      
this.turnGunRight(absoluteHeading + turnAngle - this.getGunHeading());
      
      
//Reset the current event and find the enemy again.
      
this.currentEvent = null;
      
this.scan();
    
}
   }
}

public void onScannedRobot(ScannedRobotEvent event) {
  
this.currentEvent = event;
}

With Firing04, I think I can make a robot that tries to predict the enemy's location and then fires a bullet there. Robots with complicated movements will probably throw it off, but I think I can make a reasonable guess if I incorporate the enemy's size. Implementing a complex movement pattern seems to be the best way to stay alive as well.

(I did post code and I'm done a little early. Please be nice and attribute me if you use it.)

No comments:

Post a Comment