## The Environment Code

The environment source code consists of several main components: resources, reactions, and task triggers, plus the libraries that maintain each of these.

### Task Entries

A task library is composed of a collection of entries, each of which fully describes a single task that can be used to trigger reactions.

      typedef double (cTaskLib::*tTaskTest)() const;

      class cTaskEntry {
      private:
        cString name;  // Short keyword for task
        cString desc;  // For more human-understandable output...
        int id;
        tTaskTest test_fun;
      public:
        cTaskEntry(const cString & _name, const cString & _desc, int _id,
                   tTaskTest _test_fun);
        ~cTaskEntry();

        const cString & GetName()    const { return name; }
        const cString & GetDesc()    const { return desc; }
        const int       GetID()      const { return id; }
        const tTaskTest GetTestFun() const { return test_fun; }
      };

Task entries are very straight-forward. They consist of a name, a description, a unique ID number, and a method from the task library (cTaskLib) that they are associated with. This method looks at the inputs the organism has taken in, the values it has output, and returns a number between 0.0 and 1.0 representing how well the task was performed. Currently, all task tests will return an exact zero or one, but fractions are possible if there is a quality component associated with the task.

### Task Libraries

Here is the task library that manages all of the individual entries:

      class cTaskLib {
      private:
        tArray<cTaskEntry *> task_array;

        // Active task information...
        tBuffer<int> input_buffer;
        tBuffer<int> output_buffer;
        int logic_id;

        void SetupLogicTests(const tBuffer<int> & inputs,
                             const tBuffer<int> & outputs) const;

        double Task_Echo() const;
        double Task_Add()  const;
        double Task_Sub()  const;

        double Task_Not()    const;
        double Task_Nand()   const;
        double Task_And()    const;
        // ... And a whole bunch more ...

      public:
        cTaskLib();
        ~cTaskLib();

        int GetSize() const { return task_array.GetSize(); }

        cTaskEntry * AddTask(const cString & name);
        const cTaskEntry & GetTask(int id) const;

        void SetupTests(const tBuffer<int> & inputs,
      		  const tBuffer<int> & outputs) const;
        double TestOutput(const cTaskEntry & task) const;
      };

The task library contains an array of task entries that define all of the rewarded (or otherwise acted upon) tasks in an environment. This array is the only "long term" variable that is stored here. Whenever an organism outputs a new number that needs to be tested, the SetupTasks() method is called with the appropriate inputs and output for that organism, which are temporarily saved in the data variables input_buffer and output_buffer. Additionally, if there are any logic-based tasks rewarded, SetupTasks() will, in turn, call the private method SetupLogicTasks() to calculate a logic_id. This is a number generated by looking at all bitwise inputs and their associated bit output. It can be later used so that work doesn't need to be repeated for each and every logic task tested.

The TestOutput() method can only be run after the setup has finished. It will test the specific task passed in and return the 0.0 - 1.0 quality measure of how well that task was done with the most recent output.

Below is a sample task-tester implementation:

      double cTaskLib::Task_Add() const
      {
        const int test_output = output_buffer[0];
        for (int i = 0; i < input_buffer.GetNumStored(); i++) {
          for (int j = 0; j < i; j++) {
            if (test_output == input_buffer[i] + input_buffer[j]) return 1.0;
          }
        }
        return 0.0;
      }

This case tests to see if the organism has performed an addition operation. It compares all pairs of inputs summed together against the most recent output of the organism. If there is a match a full reward (1.0) is given. If no match is found, no reward is given (0.0). The logic_id has 256 possible values, each of which can only be associated with a single logic task. These tests look more like:

      double cTaskLib::Task_AndNot() const
      {
        if (logic_id == 10 || logic_id == 12 || logic_id == 34 ||
            logic_id == 48 || logic_id == 68 || logic_id == 80) return 1.0;

        return 0.0;
      }

If the logic ID is on the list, the task has been done, otherwise it hasn't. In each case, the outside world needs to request a test of which tasks have been performed, and the library just replied with a numerical answer.

### Building a Reaction

The reaction class keeps track of all of the information associated with a single possible environmental reaction. The class appears as follows:

      class cReaction {
      private:
        cString name;
        int id;
        cTaskEntry * task;
        tList<cReactionRequisite> requisite_list;
        tList<cReactionProcess> process_list;
      public:
        cReaction(const cString & _name, int _id);
        ~cReaction();

        const cString & GetName() const { return name; }
        int GetID() const { return id; }
        cTaskEntry * GetTask() { return task; }
        const tList<cReactionRequisite> & GetRequisites()
          { return requisite_list; }
        const tList<cReactionProcess> & GetProcesses() { return process_list; }

        void SetTask(cTaskEntry * _task) { task = _task; }
        cReactionProcess * AddProcess();
        cReactionRequisite * AddRequisite();
      };

Each reaction must have a unique name and a unique numerical ID associated with them. In addition to those data, a reaction object also has a task that acts as its trigger, a list of other requisites that must be met for the trigger to work, and a list of processes that will occur if the reaction goes off. The cReaction object acts a a single place to store all of this information.

### Resources

Resources are a little more complicated than task entries to manage and understand. An object of type cResource contains 18 pieces of data, and the associated accessors. Like all of the other individual units we have discussed, resources have a unique name and numerical id. For all resource we store the quantities associated with their inflow, outflow, and initial count (each stored as a double) as well as the geometry of that resource.

For spatial resources we need to be able to describe how a resource exists in space so we store data for:

- inflowX1, inflowX2, inflowY1, and inflowY2 to describe a rectangle where resources flow in
- outflowX1, outflowX2, outflowY1, and outfowY2 for a rectangle where resources flow out of
- xdiffuse and ydiffuse describe how fast resources will flow from cells of higher amounts of that resource to cells with lower amounts of that resource
- xgravity and ygravity describe the preferential flow of resource in a given direction

This class describes the dynamics of a resource, not its current count (since, for example, we might want local resources where each cell would have its own count). However, every time a resource is needed, any changes in its quantity from the last time it was used can be calculated using these numbers.

### Tying it all together: The Environment

The cEnvironment class is used to maintain the details of how the environments work using the classes described above and a few others. Below is an abbreviated version of this class:

    class cEnvironment {
    private:
      // Keep libraries of resources, reactions, and tasks.
      cResourceLib resource_lib;
      cReactionLib reaction_lib;
      cTaskLib task_lib;
      cInstLib inst_lib;
      cMutationRates mut_rates;

    public:
      cEnvironment();
      cEnvironment(const cString & filename);
      ~cEnvironment() { ; }

      bool Load(const cString & filename);

      // Interaction with the organisms
      bool TestOutput(  cReactionResult & result,
                        const tBuffer<int> & inputs,
                        const tBuffer<int> & outputs,
                        const tArray<int> & task_count,
                        const tArray<int> & reaction_count,
                        const tArray<double> & resource_count ) const;
    };

The private data members include all of the libraries needed to specify the environment, plus its mutation rates. The Load() method takes a filename (environment.cfg by default) and will fill out all of the libraries in this environment. The most important feature of this class is the TestOutput() method, which takes in all sorts of information about the current state of the organism that has just done an output and fills out an object of type cReactionResult with information about what happened. It also directly returns a bool that will indicate if there have been any changes at all. The specific information it uses to determine the results are the inputs the organism has taken in and the outputs it has produced -- both needed to determine what tasks have been done, and therefore what reactions may have been triggered. That organism's previous task_count and resource_count are also needed to determine if the reactions requisites have been met. And finally the resource_count available to the organisms is needed to determine how much of each resource can be used in the reactions.

Project hosted by: