april  1.0.0
...
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
Example 5 - DNA

The DNA represents storage space for information that is read-only from the point of view of actors - the entities that live in our worlds. We will see what are actors and how to use them later. For now, let's concentrate on DNA, as it is a central concept of the library.

DNA has storage space for two kinds of values: 64-bit unsigned integers and double-precision floating points.

The integer part is somehow restrictive and has a rigid structure. It stores informations about the Actor, like its kind, the cost of running it, average age, amount of energy needed at birth, ... It also stores lists of IDs representing the component parts of an actor, grouped by categories (sensors, reflexes, brains, actuators).

The real part is more flexible. It is also subject to random variations and merging (creating a new DNA sequence from two parent sequences). The space of values is divided into parts, each assigned to an ID. These parts have variable lenght (they may grow) and are relocatable, so that the only reliable way of addressing the is through the ID.

The list of parts is not searchable - the caller must know the ID. If the ID is not found then the Fabric is employed in creating a new, default sequence that is appended, a new part is generated and returned to the caller.

To demonstrate the usage of DNA we will make use of a derived class to gain access to the initialisation method. This is not regular use, since this initialisation is performed by the ActorFactory.

class DNAProxy : public DNA {
public:
void initDNAProxy ( const InitData & init )
{
initDNA( init );
}
};
DNAProxy dna;
cout << "Valid DNA instance: " << dna.isValid() << "\n";

The initial instance is invalid. It needs to be initialised with the kind and other fixed values:

DNA::InitData initd;
initd.kind_ = IdKind;
initd.cost_ = 1;
initd.age_ = 100;
initd.energy_ = 10;
dna.initDNAProxy( initd );
cout << "Valid DNA instance: " << dna.isValid() << "\n";
cout << " - kind ID: " << dna.kind() << "\n";
cout << " - cost: " << dna.cost() << "\n";
cout << " - age: " << dna.age() << "\n";
cout << " - energy: " << dna.energy() << "\n";

The initialisation creates empty lists for the components. An Actor that would be created from this DNA would be an empty one.

cout << " - number of sensors: " << dna.sensors().count() << "\n";
cout << " - number of reflexes: " << dna.reflexes().count() << "\n";
cout << " - number of brains: " << dna.brains().count() << "\n";
cout << " - number of actuators: " << dna.actuators().count() << "\n";

Adding components to the structure is done in straight-forward fashion:

dna.addBrain( IdBrain );
dna.addActuator( IdActuator );
dna.addSensor( IdSensor );
dna.addReflex( IdReflex );
cout << "DNA instance after component insertion: \n";
cout << " - number of sensors: " << dna.sensors().count() << "\n";
cout << " - number of reflexes: " << dna.reflexes().count() << "\n";
cout << " - number of brains: " << dna.brains().count() << "\n";
cout << " - number of actuators: " << dna.actuators().count() << "\n";

Now for the real, variable part, we also need to relly on a proxy Factory class:

class FactoryProxy : public Factory {
public:
FactoryProxy( World * w ) : Factory( w )
{ }
virtual QList<qreal> averageDNA ( ID id ) const
{
cout << "Default data requested for id " << id << "\n";
QList<qreal> ql;
ql << 1.5 << 1.6 << 1.7;
return ql;
}
};

This is because when requested for an ID that it can not find, the DNA will use default values from the Factory.

FactoryProxy * fact = new FactoryProxy( w );
DNAView v = dna.getView( IdBrain, fact );
cout << "DNA sequence for the brain ID " << v.identificator() << ":\n";
cout << " " << v.value( 0 );
cout << " " << v.value( 1 );
cout << " " << v.value( 2 );
cout << "\n";
DEC_REF(fact,fact);




Example 4 - IDs april-core library Example 6 - Factories