Posts Tagged object orientation

Proramming Stuff: Genome

It occurs to me I never really explained the horrible occult things I was doing to the poor innocent Genome class for 0.7.0, so why not? Hopefully the Guild of Evil Programmers won’t react too badly to me sharing their evil programming trade secrets. Of evil.

In 0.6.0, the Genome class was fairly simple: a list of floating point numbers and not much else. These floating point numbers were the ones you see in the .txt file when you export a creature: they represent every hereditable trait a creature has, plus a few “stop codons” (-1) which don’t really do anything at this stage. There’s 61 in total, plus 5 for every facial feature.

The thing is, though, the creature’s don’t use this list. They have their own values for Limb1Bicep and HeadType and Aggression, and they use those instead. The values in the genetic list are only really used for things like speciation and exporting and colour coding, where I need to be able to enumerate through the list. For everything else, theres mastercard local variables.

This is, to be blunt, a STUPID DUMBASS PINEAPPLE way of doing things. I need to actively keep the list and the local variables synchronised at all times. Forget to update a variable, and my computer could spontaniously explode in a shower of napalm and internets.

So for Genome Step 1, I’ve fixed that. There are no longer any hereditable local variables anywhere: everything references back to the same GeneticValues list. To avoid making the code unreadable, values are accessed through appropriately named public accessors:

public float Limb_1_Bicep
get { return GeneticValues[42]; }
set { GeneticValues[42] = value; }

There are a lot of these accessors, but now that they’re set up, I can use similar code to before, but only store and retrieve from one copy of each genetic value per creature. And the copy-genome-from-parent-to-child routine is literally a call to CopyTo().

It’s much cleaner than before.

Genome Step 2, which I’m currently working on, involves modularising this further. I’ve created a Gene base class, which contains hereditary data for a macrostructural feature like a limb. Well, okay that’s not quite accurate: it contains the indexers for the data, and a few basic items like number of values and start index. The aforementioned GeneticValues list contains the genetic data itself.

The goal of this system is to make the genome fully enumerable and extensible. Enumerability is great for outputting genetic values, along with data like their names, descriptions and formulae, to the UI. It also makes things like mutation easier to read: rather than 120-something lines of this sort of thing:

 ChestWidth += (float)(rand.NextDouble() - 0.5f) * 0.1f * mutationRate;
ChestWidth = MathHelper.Max(ChestWidth, 0.01f);
StomachWidth += (float)(rand.NextDouble() - 0.5f) * 0.1f * mutationRate;
StomachWidth = MathHelper.Max(StomachWidth, 0.01f);
HipWidth += (float)(rand.NextDouble() - 0.5f) * 0.1f * mutationRate;
HipWidth = MathHelper.Max(HipWidth, 0.01f);
ChestHeight += (float)(rand.NextDouble() - 0.5f) * 0.1f * mutationRate;
ChestHeight = MathHelper.Max(ChestHeight, 0.01f);
StomachHeight += (float)(rand.NextDouble() - 0.5f) * 0.1f * mutationRate;
StomachHeight = MathHelper.Max(StomachHeight, 0.01f);
HipHeight += (float)(rand.NextDouble() - 0.5f) * 0.1f * mutationRate;
HipHeight = MathHelper.Max(HipHeight, 0.01f);

(oh yeah, I renamed “shoulder” and “thigh” to “chest” and “hip” respectively. This is because… well, I’ll go into more detail below)

… rather than that, we end up with something more like this:

foreach(Limb limb in limbGeneCollection)

or, if I take it another step further and add a virtual method to the Gene class, this:

foreach(Gene gene in GeneCollection)

So enumerability is a vera goot ting.

The other benefit will be extensibility. Previously (before step 1), if I wanted to add a new gene, I had to declare the local variable, ensure it was added to GeneticValues, ensure it was copied to the child/genetically-recombined/mutated in the various hereditability methods, and then implement how it was drawn and affected survival.

Step 1 removed the local variable and the need to manually copy data to the child, but it added a new problem: if I add it to the genome at any location other than the end, it will screw up every one of the accessors indexes after that and I’ll have to find and re-write all of them. Yeesh.

With step 2 in place, though, so long as each gene object has the right startIndex, the accessors can be built from there. Worst case scenario, that’s maybe 10 values to specify, rather than 60 something. But it’s better than that: since the startIndices aren’t hardcoded in the accessors, they can be built on the fly: with a small bit of extra code I should be able to literally just add gene objects willy-nilly and let the accessors sort themselves out!

It means a (hopefully tiny) performance hit in that the accessors are now adding two values together and pointing to the result, rather than going straight to a hardcoded location, but for the ability to add genes with a few lines, I’ll happily take that hit.

All of this will be immediately useful because, as mentioned above, for 0.7.0 we’re making a change to the way “Shoulders” and “Thighs” are represented. These elements will now be a part of the limbs genetic structure. You know those inevitable giant shoulderballs you sometimes see on creature’s with large but skinny legs? Those are what we’re calling shoulders now, and the extra gene for them will give them the extra versatility to be large or small. (As mentioned, the existing “shoulder” gene has been renamed to “chest”)

This change should give the creature’s slightly more variation in forms, as well as providing the potential for large, stick-legged creature’s to be even more insectoid and terrifying. Yay!

I’m actually quite happy with the way this is coming together. It feels like Species is finally moving from Rapid Prototype to fully armed and operational Game Engine.

Implementing future features should happen much faster than it would otherwise thanks to this update.


“We have 5 nuclear warheads, 2 laser bombs and some sort of giant squid robot inbound, and the Guild of Evil Programmers has unfriended us on Facebook. What did you do this time?”

, , , ,