Programming Rat Plugins for RatCog

Christopher G. Prince (chris@cprince.com)

Department of Computer Science, University of Minnesota Duluth

http://www.cprince.com/PubRes/RatCog/docs/

24 September 2002

Introduction

Plugins in RatCog are separate threads. That is, they execute in parallel with the rest of the processes of the simulator as the simulator internally is written in a multi-process or multi-threaded manner. For the programmer, this means that you should not expect the same events to occur each time you run your plugin. Running the rat plugins as separate threads makes them more analogous to real biological rats. Real biological rats operate asynchronously from their environment (and from an experimenter if they happen to be a lab rat!).

An obvious exception to this behavior is when an experimenter takes the rat off the maze. The rat can no longer run on the maze. For the RatCog maze simulator, when an experimenter plugin takes the rat plugin off the maze, this causes the rat plugin to block. "blocking" means that the rat stops executing, temporarily. Actually, what happens is if the rat is purely executing “computation” and not perception or action (primary RAT_ calls; see below) calls, the rat is allowed by the simulator to continue that computation. When off the maze, however, if the rat executes a perception or action call, he or she is blocked from proceeding further. For the most part, this blocking is transparent to the writer of a rat plugin. One instance where the writer of a rat plugin can notice the experimenter taking the rat off the maze is when the rat becomes disoriented as a result of being taken off the maze. You can use the PLUGIN_TakeOffMaze function to detect the fact that the rat is taken off the maze (see below).

Another exception to this behavior is when the graphical user interface (GUI) user requests single step mode for the rat plugin. In this case, the rat cannot detect that he or she is in single step mode. In fact, no program code is written specifically in the rat plugins for single step mode. The simulator handles this automatically and transparently from the point of view of the rat.

As a piece of advice, programmer-to-programmer, RatCog is a simulator that has undergone recent rapid development (and has only recently been ported to Linux). Hence, while there are no bugs in RatCog (“Our programs don’t have bugs in them they have rats in them!”), you may notice some behavior that isn’t suitable. Please report any such behavior to me (chris@cprince.com).

Creating a New Rat Folder from the RatCog/Plugins/Rats Directory

In order to create a new rat plugin, you first need to make a new folder (directory) for your plugin, based on files in the directory RatCog/Plugins/Rats. You will also need to make some changes to the files in this directory. Follow these steps:

(1) Copy an existing rat plugin folder and use that as a template. You should use the StupidRat2 folder. This rat plugin has been compiled with "make" (the other rat plugins have been compiled with an IDE that I don't have a site license for). This way, you will get a copy of the Makefile file and have templates for the .h and .cpp files also. That is, make a duplicate of the folder: RatCog/Plugins/Rats/StupidRat2. This will give you the following files:

Makefile
StupidRat.h
StupidRat.cpp

(plus README.TXT & some object files).

(2) You now need to make some modifications for your new rat plugin. Let’s call your new rat plugin “MyRat”. First, rename your “StupidRat2 copy” folder to “MyRat”, and delete the object files (.o files) and the StupidRat.so file within your MyRat folder. Change the names of the above files to:

MyRat.h
MyRat.cpp

(3) You need to change the Makefile also. As a start, replace all occurrences of "StupidRat" in this file with "MyRat".

(4) Various changes are required in the code .h and .cpp files. Various things must be left as they are. Each rat plugin is written as a derived class in C++ of the RatPlugin class. You need to abide by the API (Application Programming Interface) given in

RatCog/PluginIncludes/RatPlugin.h

RatPlugin.h declares the RatPlugin class. You will likely want a hardcopy of RatPlugin.h as you will be referring to it frequently.

The name of the StupidRat2 class is actually StupidRatPlugin. By convention, the plugin class names in RatCog are named ending in “Plugin”. Your changes to the .h file will be to rename StupidRatPlugin where it occurs to MyRatPlugin. In the .h file you should also change the #define constant at the top of the file from STUPID_RAT_PLUGIN_H to to MY_RAT_PLUGIN_H. Your changes to the .cpp file will be to rename StupidRatPlugin where it occurs to MyRatPlugin. You will, of course, also need to recompile. You do this by running the program "make" in your MyRat directory.

(5) Now you can actually start replacing code in the .cpp and .h files with code for your own rat plugin.

Programming a Rat Plugin: The RatPlugin.h API

When you program a rat, your main objective is to write two functions: PLUGIN_Start and PLUGIN_TakeOffMaze. These are redeclarations and overrides of RatPlugin class virtual functions. When your rat is loaded into the maze simulator (by use of the graphical user interface), the simulator calls PLUGIN_Start to start your rat. Here is some code from StupidRat.cpp:

long StupidRatPlugin::PLUGIN_Start()
{
	Debug('n', "StupidRatPlugin::PLUGIN_Start: In the Stupid Rat Thread!!!");

	for (;;) {
		MoveRat();
	}

	return 0;
}

The main work for the rat is done by the MoveRat function, a function that appears in StupidRat.cpp.

All virtual functions you will have to write in RatCog are named with the prefix: PLUGIN_. The PLUGIN_TakeOffMaze function is called when the rat is taken off the maze by the experimenter. You don’t have to write this function (actually, you don’t have write PLUGIN_Start either, but the rat would be pretty boring!), but if you do, your rat can detect when he or she is taken off the maze. Your PLUGIN_TakeOffMaze function has the following form:

// Duration is in hours

void MyRatPlugin::PLUGIN_TakeOffMaze(float duration)
{
/* your program code */
}

You will need to put a member function in your .h file as well.

The single parameter to the PLUGIN_TakeOffMaze function enables the rat to perceive the duration (in simulated time) of his or her time off the maze. This function is called immediately before the rat is taken off the maze. It is possible for your implementation of PLUGIN_TakeOffMaze to contain primary RAT_ function calls, but this should not be done. In fact, if you did have a primary RAT_ call, your RAT_ function call in PLUGIN_TakeOffMaze would immediately block and remain blocked until the rat was placed on the maze. The intent of providing PLUGIN_TakeOffMaze is to enable you to set a flag or increment a counter so that when the rat is placed back on the maze, the rat can observe that, in fact, they were taken off the maze.

The PLUGIN_API_Version function is used by the simulator to detect the API version that the plugin is using (see RatPlugin.h). You should use the standard implementation for this function—Just use the same implementation as given in the StupidRat.

When you program your rat, you have a collection of primary and secondary library calls available to you. These are mainly for perception and action for the rat plugin. These calls are named with the prefix: RAT_ and their documentation is in the file RatPlugin.h. There are also debugging output facilities available (e.g., the Debug function above; see below for Debugging). Briefly, the primary RAT_ calls are:


void RAT_Rotate(int degrees)

Rotate the rat +/- degrees from current heading.

bool RAT_RotateUntilView(float *view)

Rotate the rat until he faces the given view.

bool RAT_IsOpen()

Check if the rat can move ahead.

bool RAT_Move()

Move the rat ahead.

bool RAT_FoodHere()

Check if food is at the rats current location.

bool RAT_EatFood()

Eat the food at the current location.

float *RAT_Look()

Look ahead and return the 9 landmarks currently in view.
Caller owns returned vector and must deallocate it. Deallocate with the operation:
delete [] view;

where view is the pointer returned from RAT_Look.

void RAT_Wait(float secs)

Wait (block) for a number of seconds.

The following secondary functions are part of the rat API, but are not perception and action functions. They are convenience functions for debugging and testing. Your rat model should not depend on information related to these functions. These secondary functions are:

void RAT_UserMessage(char *msg)

Put an alert box onto the GUI.

void RAT_AddDatabaseRecord(

      BMessage *record)

Put a record into the database.

int RAT_GetCurrentTrialType()

If the experimenter sent it, get the current trial type.

Rat Plugin Perception and Action

The rat plugins in RatCog cannot detect their absolute heading, they have only relative heading information. For example, a rat plugin cannot tell that its current heading is NORTH. Rat plugins can detect and maintain relative heading, however. You can use the RAT_Rotate call to rotate the rat from his or her current heading, and keep track of your heading relative to the direction you started on.

While they are not shown graphically in the maze simulator window, the maze has 40 evenly spaced landmarks located (at an infinite distance) off the maze. Each of these landmarks is represented as a single, randomly selected, float number. The rat plugin can use the RAT_Look call to return the current view. The current view is a collection of nine landmarks. The landmark that is directly ahead of the rat, and the four landmarks to either side. This comprises a 90 degree field of view for the rat. The rat plugin’s visual sensors are perfect – there is no noise in the information returned by RAT_Look (e.g., calling RAT_Look twice without changing the heading of the rat will return the same information[1]). You can therefore use the view’s returned from RAT_Look to re-orient the rat to facing a particular direction.

Debugging Your Rats

The Debug functions (RatCog/PluginIncludes/Debug.h) provide output of messages to the Debug window. The Debug window is a separate application that just serves the function of opening a window and receiving debugging text messages. When you start the simulator by typing "./RatCog" in the RatCog/binaries folder, the Debug window will automatically be opened (unfortunately right now, the debug window cannot be closed right now with the simulator running).

When the Debug window is in Verbose mode, all Debug messages are output (see below). The following debugging functions are provided:

void Debug(const char *s=NULL, const char *s=NULL);

Produces a debugging message on the debug window. The two strings are for convenience as this does not provide a general printf format output. The default debugType for this is ‘e’.

void Debug(char debugType, const char *s=NULL, const char *s=NULL);

Same as the above, except that the debugType parameter gives the type of message. RatCog uses three types:

‘e’ – severe error; system user should be notified; produces additional “ERROR” diagnostics on output window.

‘t’ – temporary high priority message; outputs even when Debug window is not in Verbose mode. This is not for errors. This is for temporary, initial, debugging of segments of program code.

‘n’ – normal information output; sometimes you just want state information output from the simulation so that when you want, you can observe that information with the Verbose mode turned on. This is not for errors.

Issues in Debugging Rat Plugins

The rat plugins are compiled as .so dynamically loaded libraries and this can cause problems with resolving linker errors. For example, if you reference a function "foo" but do not have this function defined in your program code, you may not get an error in compilation. The error may exhibit itself only when you are in the RatCog simulator: RatCog will not list the plugin in the plugins setting window! This happens because of undefined references occuring in the dynamic loading procedures. To resolve problems like this, you'll need to look carefully at the Debug window. Look for messages of the form: "DynamicModule::Load: Cannot dlopen". The line after this message gives some information about possible problems. These messages will occur in the Debug window for two files in the plugins directory (".", and ".."). But, if you have a problem with unresolved references in your rat plugin, you will see some additional messages of this form in the Debug window.

Environment Variables

Presently, RatCog provides three environment variables:

RATCOG_ PORTS

Specifies the directory where sockets are created and used. These are used for implementation specific internal communication between threads and processes in RatCog. The default is RatCog/binaries/.ports

RATCOG_PLUGINS

Specifies the directory look for plugins. The default is RatCog/Plugins/binaries

RATCOG_BINARIES Specifies the directory to look for the program code of the simulator. Presently, there are four executable binaries for the simulator (not including plugins). The default is RatCog/binaries

[1] Note that not changing the heading of the rat may be tricky. It is always possible for the experimenter to take the rat off the maze at any point during the rat plugin’s execution, and replace the rat on the maze facing in a different direction.