avida-ed-library-build/docs/documentation/Development-|-Tutorial-|-En...

269 lines
14 KiB
Markdown

<p>
The environment source code consists of several main components: resources,
reactions, and task triggers, plus the libraries that maintain each of these.
</p>
<p>&nbsp;</p>
<h2>Task Entries</h2>
<p>
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.
</p>
<pre>
typedef <span class="class">double</span> (<span class="class">cTaskLib</span>::*<span class="object">tTaskTest</span>)(<span class="class">cTaskContext</span>*) const;
class <span class="class">cTaskEntry</span> {
private:
<span class="class">cString</span> <span class="object">m_name</span>; <span class="comment">// Short keyword for task</span>
<span class="class">cString</span> <span class="object">m_desc</span>; <span class="comment">// For more human-understandable output...</span>
<span class="class">int</span> <span class="object">m_id</span>;
<span class="class">tTaskTest</span> <span class="object">m_test_fun</span>;
<span class="class">cString</span> <span class="object">m_info</span>; <span class="comment">// extra info (like the string or whatever to match)</span>
public:
<span class="method">cTaskEntry</span>(const <span class="class">cString</span>&amp; <span class="object">name</span>, const <span class="class">cString</span>&amp; <span class="object">desc</span>, int <span class="object">in_id</span>, <span class="class">tTaskTest</span> <span class="object">test_fun</span>, const <span class="class">cString</span>&amp; <span class="object">info</span>);
: m_name(name), m_desc(desc), m_id(in_id), m_test_fun(test_fun), m_info(info)
{
}
~<span class="method">cTaskEntry</span>() { ; }
const <span class="class">cString</span>&amp; <span class="method">GetName</span>() const { return <span class="object">m_name</span>; }
const <span class="class">cString</span>&amp; <span class="method">GetDesc</span>() const { return <span class="object">m_desc</span>; }
const int <span class="method">GetID</span>() const { return <span class="object">m_id</span>; }
const <span class="class">tTaskTest</span> <span class="method">GetTestFun</span>() const { return <span class="object">m_test_fun</span>; }
const <span class="class">cString</span>&amp; <span class="method">GetInfo</span>() const { return <span class="object">m_info</span>; }
};
</pre>
<p>
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.
</p>
<p>&nbsp;</p>
<h2>Task Libraries</h2>
<p>
Here is an abridged version of the task library class that manages all of the
individual entries:
</p>
<pre>
class <span class="class">cTaskLib</span> {
private:
<span class="class">Apto::Array</span>&lt;<span class="class">cTaskEntry</span>*&gt; <span class="object">task_array</span>;
public:
int <span class="method">GetSize</span>() const { return <span class="object">task_array</span>.<span class="method">GetSize</span>(); }
<span class="class">cTaskEntry</span>* <span class="method">AddTask</span>(const <span class="class">cString</span>&amp; <span class="object">name</span>, const <span class="class">cString</span>&amp; <span class="object">info</span>);
const <span class="class">cTaskEntry</span>&amp; <span class="method">GetTask</span>(<span class="class">int</span> <span class="object">id</span>) const;
void <span class="method">SetupTests</span>(<span class="class">cTaskContext</span>&amp; <span class="object">ctx</span>) const;
inline double <span class="method">TestOutput</span>(const <span class="class">cTaskEntry</span>&amp; <span class="object">task</span>, <span class="class">cTaskContext</span>* ctx) const;
private:
double <span class="method">Task_Echo</span>(<span class="class">cTaskContext</span>* <span class="object">ctx</span>) const;
double <span class="method">Task_Add</span>(<span class="class">cTaskContext</span>* <span class="object">ctx</span>) const;
double <span class="method">Task_Sub</span>(<span class="class">cTaskContext</span>* <span class="object">ctx</span>) const;
double <span class="method">Task_Not</span>(<span class="class">cTaskContext</span>* <span class="object">ctx</span>) const;
double <span class="method">Task_Nand</span>(<span class="class">cTaskContext</span>* <span class="object">ctx</span>) const;
double <span class="method">Task_And</span>(<span class="class">cTaskContext</span>* <span class="object">ctx</span>) const;
<span class="comment">// ... And a whole bunch more ...</span>
};
</pre>
<p>
The task library contains an array of task entries that define all of the
rewarded (or otherwise acted upon) tasks in an environment.
</p>
<p>
The <span class="method">TestOutput</span>() method can only be run with
as <span class="class">cTaskContext</span> object that has been initialized
with the <span class="method">SetupTests</span> method. 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.
</p>
<p>
Below is a sample task-tester implementation:
<pre>
double <span class="class">cTaskLib</span>::<span class="method">Task_Add</span>(<span class="class">cTaskContext</span>* <span class="object">ctx</span>) const
{
const int <span class="object">test_output</span> = <span class="object">ctx</span>-&gt;<span class="object">output_buffer</span>[0];
for (<span class="class">int</span> <span class="object">i</span> = 0; <span class="object">i</span> &lt; <span class="object">ctx</span>-&gt;<span class="object">input_buffer</span>.<span class="method">GetNumStored</span>(); <span class="object">i</span>++) {
for (<span class="class">int</span> <span class="object">j</span> = 0; <span class="object">j</span> < <span class="object">i</span>; <span class="object">j</span>++) {
if (<span class="object">test_output</span> == <span class="object">ctx</span>-&gt;<span class="object">input_buffer</span>[<span class="object">i</span>] + <span class="object">ctx</span>-&gt;<span class="object">input_buffer</span>[<span class="object">j</span>]) return 1.0;
}
}
return 0.0;
}
</pre>
<p>
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).
</p>
<p>
The <span class="method">SetupTests</span> method performs some
precomptution for all of the logic tasks, creating the value
<span class="object">logic_id</span> within the task context. The
<span class="object">logic_id</span> has 256 possible values, each of which
can only be associated with a single logic task. These tests look more like:
</p>
<pre>
double <span class="class">cTaskLib</span>::<span class="method">Task_AndNot</span>(<span class="class">cTaskContext</span>* <span class="object">ctx</span>) const
{
const int <span class="object">logic_id</span> = <span class="object">ctx</span>-&gt;<span class="object">logic_id</span>;
if (<span class="object">logic_id</span> == 10 || <span class="object">logic_id</span> == 12 || <span class="object">logic_id</span> == 34 ||
<span class="object">logic_id</span> == 48 || <span class="object">logic_id</span> == 68 || <span class="object">logic_id</span> == 80) return 1.0;
return 0.0;
}
</pre>
<p>
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.
</p>
<p>&nbsp;</p>
<h2>Building a Reaction</h2>
<p>
The reaction class keeps track of all of the information associated with
a single possible environmental reaction. 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.
</p>
<p>&nbsp;</p>
<h2>Resources</h2>
<p>
Resources are a little more complicated than task entries to manage
and understand. An object of type <span class="class">cResource</span>
contains 19 pieces of data, and the associated accessors. Like all of the
other individual units we have discussed, resources have a unique
<span class="object">name</span> and numerical
<span class="object">id</span>. For all resource we store the quantities
associated with their <span class="object">inflow</span>,
<span class="object">outflow</span>, and
<span class="object">initial</span> count (each stored as a
double) as well as the <span class="object">geometry</span> of that resource.
<p>
For spatial resources we need to be able to describe how a resource
exists in space so we store data for:
<ul>
<li>
<span class="object">inflowX1</span>,
<span class="object">inflowX2</span>,
<span class="object">inflowY1</span>, and
<span class="object">inflowY2</span> to describe a rectangle where
resources flow in.
</li>
<li>
<span class="object">outflowX1</span>,
<span class="object">outflowX2</span>,
<span class="object">outflowY1</span>, and
<span class="object">outfowY2</span> for a rectangle where resources
flow out.
</li>
<li>
<span class="object">cell_list</span> is a list of individual cells with
their own initial, inflow and outflow values.
<li>
<span class="object">xdiffuse</span> and
<span class="object">ydiffuse</span> describe how fast resources will
flow from cells of higher amounts of that resource to cells with
lower amounts of that resource.
</li>
<li>
<span class="object">xgravity</span> and
<span class="object">ygravity</span> describe the preferential flow of
resource in a given direction.
</li>
</ul>
<p>
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.
</p>
<p>&nbsp;</p>
<h2>Tying it all together: The Environment</h2>
<p>
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:
</p>
<pre>
class <span class="class">cEnvironment</span> {
private:
<span class="comment">// Keep libraries of resources, reactions, and tasks.</span>
<span class="class">cResourceLib</span> <span class="object">resource_lib</span>;
<span class="class">cReactionLib</span> <span class="object">reaction_lib</span>;
<span class="class">cTaskLib</span> <span class="object">task_lib</span>;
<span class="class">cInstLib</span> <span class="object">inst_lib</span>;
<span class="class">cMutationRates</span> <span class="object">mut_rates</span>;
public:
<span class="class">bool</span> <span class="method">Load</span>(const <span class="class">cString</span>&amp; <span class="object">filename</span>);
<span class="comment">// Interaction with the organisms</span>
<span class="class">bool</span> <span class="method">TestOutput</span>(<span class="class">cAvidaContext</span>&amp; <span class="object">ctx</span>, <span class="class">cReactionResult</span>&amp; <span class="object">result</span>, <span class="class">cTaskContext</span>&amp; <span class="object">taskctx</span>,
const <span class="class">tBuffer</span>&lt;int&gt;&amp; <span class="object">send_buf</span>, const <span class="class">tBuffer</span>&lt;int&gt;&amp; <span class="object">receive_buf</span>,
const <span class="class">Apto::Array</span>&lt;int&gt;&amp; <span class="object">task_count</span>, const <span class="class">Apto::Array</span>&lt;int&gt;&amp; <span class="object">reaction_count</span>,
const <span class="class">Apto::Array</span>&lt;double&gt;&amp; <span class="object">resource_count</span>) const;
};
</pre>
<p>
The private data members include all of the libraries needed to specify
the environment, plus its mutation rates. The
<span class="method">Load</span>() method takes a filename
(<kbd>environment.cfg</kbd> by default) and will fill out all of the libraries
in this environment. The most important feature of this class is the
<span class="method">TestOutput</span>() 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
<span class="class">cReactionResult</span> 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. This information is encapsulated in
the task context <span class="object">taskctx</span>. The organism's
previous <span class="object">task_count</span> and
<span class="object">resource_count</span> are also needed to determine
if the reactions requisites have been met. And finally the
<span class="object">resource_count</span> available to the organisms is
needed to determine how much of each resource can be used in the reactions.
</p>