C++Robots Introduction and Example (Richard Rognlie)

[ This is an article from the Play-By-Electronic-Mail Fanzine, v95n2, which was published on February 13, 1995. ]

[ C++Robots is a game somewhat similar to CoreWars or the earlier CRobots, in which you write a program in C++ that controls a "robot" fighting a battle in an arena against an enemy robot, also controlled by a program. C++Robots is a totally automated game; for details about joining, send email to pbmserv@gamerz.net with the words HELP C++ROBOTS in the *subject* of your message. -ed ]

One of the problems people have with the C++Robots combat simulator has been a misunderstanding of the robot's eye view of the arena and the way the robots actually work. Here's the scoop:

The arena is 10k units wide by 10k units high -- 10 units = 1 meter. The arena is surrounded by a forcefield through which robots may not pass. It acts like a wall for robots, but cannon shots pass right through (as do explosion effects).

Directions are specified in degrees (not radians like the "normal" C math functions). 0 is due east. 90 due north. 180 due west. 270 due south. All functions that use directions work in modulo 360, so the directions -45, 315, 695, etc. are all equivalent.

( 0,9999)                                              (9999,9999)
          +------------------------------------------+
          |                    90                    |
          |                    |                     |
          |             180 ---+--- 0,360            |
          |                    |                     |
          |                   270                    |
          +------------------------------------------+
( 0,   0)                                              (9999,   0)
Each C++Robot is really a simulated Sun SPARC. When a C++Robot is received, it is compiled by the GNU C++ compiler down to Sun SPARC object code. The simulator loads each C++Robot into memory, and randomly positions the robot in the arena. The simulator then begins executing the C++Robots in 100 CPU cycle timeslices. Each timeslice simulates 1 second. This is a *really* slow SPARC! A 2 player bout that fights to a draw simulates 30 minutes of C++Robots activity, but, in reality, takes about 3 minutes of real time.

If, during the execution of a robot's timeslice, it encounters a call to drive(), scan() or cannon(), the call is immediately executed, but any remaining CPU cycles left in that robot's current timeslice are lost. Therefore, an average call to scan() takes ~50 CPU cycles. [Assuming random distribution of scan() invocation times...]

Once all robots have completed their timeslices, the simulator updates their positions in the arena and checks to see if any cannon shots have exploded, etc. This means that the result of scan() is already out of date when your robot continues execution: scan() returns a result based on the other robot's position during the previous timeslice, your timeslice then gets suspended, and when it resumes, it is a new timeslice. The other robot may have moved up to 100 units, and your robot may have moved up to 100 units, as well.

This execution loop continues until one robot is destroyed (or both are destroyed simultaneously) or time runs out.

C++Robots Strategies

There are as many C++Robot strategies are there are C++Robots! Some robots can be very simple, others quite complex. It all depends on your robot's goal.

My robot, tracker, has been quite successful with very little modification since the creation of the C++Robots hill. It algorithm was originally:

  1. Scan for a target. Move towards and shoot at any target found.
  2. If you lose the target, back up the scan a few degrees and start over again.

This algorithm worked quite well for the first few weeks of the hill, but he slowly was replaced by other smarter robots. By tweaking his scan arc, he bounced back up the hill temporarily, but eventually slid off again. (sigh!)

It was time to update the algorithm again. Apparently, the problem was that the tracker would move towards his opponents and they would lock on him as well. This wouldn't do! So, I adopted (stole) the movement algorithm from susan_unit (one of the early leaders of the C++Robots hill). And that was to move about in a box pattern. So, tracker's movement routines are independent of its targeting routines. The current algorithm is:

  1. Move around the arena in a box pattern at full speed, staying about 2000 units inside from the wall.
  2. Scan for a target, and shoot it. If you had seen a target, and have lost sight, back up the scan a bit and continue scanning.

Here's a copy of the current source code for tracker.

// 1) Move around the arena in a box pattern.  Full speed.  staying
//    about 2000 units inside from the wall.
// 2) Scan for a target, and shoot it.  If you had seen a target, and
//    have lost sight, back up the scan a bit and continue scanning.

#include "robots.h"

#define BORDER 2000

// Shoot at a target if it's in range (<= 7000 units) *and* it's far
// enough away that we'll only be slightly damaged (>200 units) by the
// resulting explosion.
inline shoot(int dir,int range)
{
   if (range > 200 && range <= 7000)
      cannon(dir,range);
}

main()
{
   int sdir=0;     // current scan direction
   int dir=0;      // current movement direction
   int range;      // range to opponent
   int hadfix=0;   // did I have a fix on my opponent from the last scan?
   int cx,cy;      // current x and y position

   drive(dir,100); // start moving right away.  Don't *ever* sit still!

   while (1) {
      int tdir=dir;    // save current direction
      cx = loc_x();
      cy = loc_y();

      // do we need to change direction?  (e.g.  are we approaching a wall?)
      if (cx > 10000-BORDER)
         if (cy < 10000-BORDER)
             tdir = 90;              // approaching east wall
         else
             tdir = 180;             // approaching northeast corner
      else if (cx < BORDER)
         if (cy < BORDER)
             tdir = 0;               // approaching southwest corner
         else
             tdir = 270;             // approaching west wall
      else if (cy > 10000-BORDER)
         tdir = 180;                 // approaching north wall
      else if (cy < BORDER)
         tdir = 0;                   // approaching south wall

      // if speed() == 0,    restart the drive unit...
      // if dir != tdir,     we need to change direction...
      if (!speed() || dir != tdir)
         drive(dir=tdir,100);

      if ((range=scan(sdir,10))) {   // scan for a target...
         shoot(sdir,range);          //   got one.  shoot it!
         hadfix=1;                   //   remember we saw a target
      }
      else if (hadfix) {             //   did we lose a target?
         sdir += 40;                 //        back up the scan
         hadfix=0;                   //        forget we had a target
      }
      else
         sdir -= 20;                 //   increment the scan
   }
}

Richard Rognlie <rrognlie@gamerz.net>

[ Next month, we'll have some short articles by some of the top C++ Robots players about their robot's strategies. -ed ]


(Edited by Greg Lindahl) (lindahl@pbm.com)