Revised 2006-09-05 DMB

Return to the Index


Environment Task Implementation Checklist

This document discusses how to implement your own tasks to use as triggers for reactions in the environment.

 

1. Build the prototype of the method that will be used to test the new task

For this step, you will be editing the task library found in the files cTaskLib.cc and cTaskLib.h in the directory source/main/. Start by adding the prototype of your new test function to the cTaskLib class in the header file. The data that will be tested is all stored within task context that is passed into this function. This function will output a double that represents the quality with which the task is performed. This is a number between 0 and 1 that determines the fraction of the bonus that should be received. For tasks that are either successful or not, this will only return 0.0 or 1.0.

For example, if we were going to create a task that tests if the organisms could double one of their inputs, we might call that task 'times2'. We would then add the line to this file:

double Task_Times2(cTaskContext* ctx) const;

If possible, place it near other tasks of the same type. In this case, I choose to place it directly after Task_Echo(), since this is also an easy task for the organisms to perform.

 

2. Build the body of the method that will be used to test the new task

We next go into the code (cTaskLib.cc) file, and add the body of our new method. We search for cTaskLib::Task_Echo(), since our new prototype followed this method in the header file, and place the body of our function immediately after it.

double cTaskLib::Task_Times2(cTaskContext* ctx) const
{
  const int test_output = ctx->output_buffer[0];
  for (int i = 0; i < ctx->input_buffer.GetNumStored(); i++) {
    if (2 * ctx->input_buffer[i] == test_output) {
      return 1.0;
    }
  }
  return 0.0;
}

The most recent output is always placed at the beginning of the output buffer, so we store it in the variable test_output to compare it against all of the different inputs. We then have a for-loop that goes from 0 to the number of inputs stored in the input buffer. Inside the body of the loop, we test for each input if twice that input is equal to the output. If so, then the task was successful and we return a 1.0. If all of the tests fail and we exit the loop, then we return a 0.0.

These test methods should be carefully written so that they run as fast as possible. In particular, if a task requires that an output be compared to multiple inputs at a time (i.e., the tasks Add or Subtract), then this can become combinartoically explosive. For the moment, we keep control on this problem by only allowing three different inputs in the input buffer, but in the future this number may need to become higher.

 

3. Attach our new method to the name of its trigger

This next step is also done in the code file, inside the cTaskLib::AddTask() method. Again, we want this to be in the same place, so we locate the task 'echo' that its supposed to follow, and add in the new line.

else if (name == "times2")
  NewTask(name, "Times2", &cTaskLib::Task_Times2);

This line will attach the name to the description "Times2" (which could have been a little more detailed, but should be 40 characters or less) as well as the function that should be called when that name is listed as a trigger to a reaction.

You are now ready to use your task!


Return to the Index