Skip to main content

Secrets from the Robocode masters: Polymorphic enemy cache

Chris Shorrock (chris@shorrockin.com), Senior Java developer, Make Technologies
Chris Shorrock is a Senior Java developer for Make Technologies in Vancouver, Canada, and has been a Java programmer for four years.

Summary:  This tip was published in the "Cloak and turret: Learn secrets from the Robocode masters" article in the May 2002 issue of the IBM developerWorks journal.

Date:  Jun 2002
Level:  Introductory
Activity:  2210 views

Successful robots maintain a store of information, accessible at any time for making crucial decisions while in battle. This is useful for a variety of reasons, from enemy movement pattern analysis to determining whom to attack based on proximity and strength. This tip explains how you can implement an effective, fast enemy cache while having the convenience of an always up-to-date object using polymorphism. This code is slated for use in the Rapture 3.0 robot.

Polymorphism is a key component of proper object-oriented programming and is a powerful tool you can use when creating robots. Polymorphism basically means that you have one common method that is implemented in different ways, so you can simply call that method without worrying about the actual class you're calling it in. In other words, if you call food.eat(), it may actually call steak.eat() or salad.eat(), which behave differently, but you don't have to worry about it. They are both types of food.

The following code samples showcase how to implement polymorphism in Robocode while adding a great deal of functionality and flexibility. Figure 1 outlines the object structure used by this framework:


Figure 1. UML diagram
UML diagram

The advantages of this structure are obvious: you can acquire a reference to an Enemy object through the EnemyManager, which actually will return an EnemyLog cast as an Enemy. Because the EnemyLog object delegates all responsibility inherited by the Enemy interface to the first object in the log, you know that when you are working with this object, you will be accessing the most recent information.

Listings 1 to 4 use this structure. You would normally want to make your Enemy interface more elaborate, as well as add more functionality to the EnemyManager object, but for learning purposes, we will keep the interface at a minimum. Happy hunting!


Listing 1. Enemy interface
                
package example.enemy;
// This class is used as an interface 
// to define the common methods needed
// to define enemy information.

public interface Enemy {
  // Returns the enemy's name
  public String getName();

  // Gets the energy that our enemy currently has 
  public double getEnergy();

  // Gets the velocity of our enemy
  public double getVelocity();
}


Listing 2. EnemyImpl class
                
package example.enemy;

import robocode.*;

// A class to hold this package's local implementation
// of the enemy interface.

class EnemyImpl extends Object implements Enemy {
  // Members
  private String m_name               = null;
  private double m_energy             = 0;
  private double m_velocity           = 0;

  // Constructor
  private EnemyImpl() {}

  // Set methods
  private void setName( String in_newName )
    { m_name = in_newName; }
  private void setEnergy( double in_newEnergy )
    { m_energy = in_newEnergy; }
  private void setVelocity( double in_newVelocity )
    { m_velocity = in_newVelocity; }

  // Get methods
  public String getName() { return m_name; }
  public double getEnergy() { return m_energy; }
  public double getVelocity() { return m_velocity; }

  // Method used to create an instance of an enemy impl object.
  public static final Enemy createEnemy( 
    ScannedRobotEvent in_event ) {

    EnemyImpl out_enemy = new EnemyImpl();

    out_enemy.setName( in_event.getName() );
    out_enemy.setEnergy( in_event.getEnergy() );
    out_enemy.setVelocity( in_event.getVelocity() );

    return out_enemy;
  }
};


Listing 3. EnemyCollection class
                
package example.enemy;

// A class that maintains a stack of all the enemy information
// objects that we have collected for a certain enemy. It is
// managed by the EnemyManager and can be referenced as an
// Enemy to maintain a reference to the most recent
// information for a certain enemy.

class EnemyCollection implements Enemy {
  /* constant which holds our maximum stack size - the number
     of Enemy information objects to maintain */
  protected static final int MAX_STACK_SIZE = 500;

  private Enemy[] m_information  = new Enemy[ MAX_STACK_SIZE ];
  private int next               = 0; 
  private int size               = 0;

  /* our constructor, which takes in the first object in
     our collection, this ensures that any collection always
     has at least one entry. */
  public EnemyCollection( Enemy in_newEnemy ) {
    add( in_newEnemy );
  }

  // Adds an Enemy to our stack of objects.
  public void add( Enemy in_new ) {
    size = (size == MAX_STACK_SIZE) ? size : size + 1;
    m_information[next] = in_new;
    next = (next + 1) % MAX_STACK_SIZE;
  }
  // Method used to get a enemy represented by an index,
  // where 0 is the last element inserted.
  public Enemy get( int in_index ) {
    int index = next - in_index - 1;
    if( index < 0 ) index += MAX_STACK_SIZE;
    return m_information[ index ];
  }

  // Method that returns the number of Enemy object
  // that we currently are storing.
  public int size() { return size; }

  // Enemy interface methods

  /* NOTE: By having this class implement enemy, we can use it
   * as an enemy object and be ensured that whenever it is
   * updated, we always have the most recent information */

  public String getName() { return get(0).getName(); }
  public double getEnergy() { return get(0).getEnergy(); }
  public double getVelocity() { return get(0).getVelocity(); }
}
 


Listing 4. EnemyManager class
                
package example.enemy;

import robocode.*;
import java.util.*;

// a class which is used to manage and maintain
// all of our enemy information objects.

public class EnemyManager {
  /* holds a reference to a SINGLETON instance of this class
     to ensure that it only ever gets instantiated once */
  private static EnemyManager SINGLETON = new EnemyManager();

  /* holds a map of all our enemy information objects
     as values with the enemies name's used as keys. */
  private Map m_allInformation = null;

  // private constructor used to hide unnecessary instantiation
  private EnemyManager() {
    // We want to use a Hashtable, since it is synchronized so
    // we need not worry about data integrity for the most part.
    m_allInformation = new Hashtable();
  }

  // Method which is used to get an instance of this class, as
  // it exists.
  public static EnemyManager getInstance()
    { return SINGLETON; }

  // Method use to log an enemy object in the manager
  public Enemy log( ScannedRobotEvent in_event ) {
    Enemy newEnemy = EnemyImpl.createEnemy( in_event );
    EnemyCollection col = (EnemyCollection)m_allInformation.get(
      newEnemy.getName() );
    if( null == col ) {
      col = new EnemyCollection( newEnemy );
      m_allInformation.put( newEnemy.getName(), col );
    } else {
      col.add( newEnemy );
    };

    return col;
  }

  // Method used to get a dynamic enemy object.  By dynamic
  // this means, that calls to any of the methods will always
  // get the most recent data. This does not necessary imply
  // that the data is current, it's just the most current.
  public Enemy get( String in_name ) {
    return (Enemy)m_allInformation.get( in_name );
  }

  // Method used to get a reference to an enemy at a certain
  // point in time. 
  public Enemy get( String in_name, int in_time ) {
    EnemyCollection col = (EnemyCollection)m_allInformation.get(
      in_name );
       
    return (null == col) ? null : col.get( in_time );
  }

  // Method used to return the maximum number of cached items
  // we store on any Enemy at any given time.
  public static final int getCacheSize() {
    return EnemyCollection.MAX_STACK_SIZE; }
};


Resources

  • Read all of the Secrets from the Robocode masters . This page will be updated as new tips become available.

  • Robocode's creator, Mathew Nelson, maintains the official Robocode site. This should be the first stop for anyone serious about Robocode.

  • RoboLeague by Christian Schnell is a league and season manager for Robocode. It ensures that all possible groupings indeed play their matches, manages the results, and produces HTML status reports.

  • "Rock 'em, sock 'em Robocode" (developerWorks, January 2002) disarms Robocode and starts you on your way to building your own customized lean, mean, fighting machine.

  • In "Rock 'em, sock 'em Robocode: Round 2" (developerWorks, May 2002), Sing Li looks at advanced robot construction and team play.

  • New to Java? Check out "Introduction to Java programming" (developerWorks, November 2004), a tutorial that steps you through the fundamentals of Java language programming.

  • developerWorks: Hundreds of articles about every aspect of Java programming.

About the author

Chris Shorrock is a Senior Java developer for Make Technologies in Vancouver, Canada, and has been a Java programmer for four years.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=243713
ArticleTitle=Secrets from the Robocode masters: Polymorphic enemy cache
publish-date=062002
author1-email=chris@shorrockin.com
author1-email-cc=jaloi@us.ibm.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers