A first stab at gene expression

In the last post I talked a bit about how I might approach expressing genes in SnailLife Go, and how this worked in the PHP prototype of the simulation.

I gave a rough example of how I’m thinking of handling gene expression - via “expresser” interfaces that organs are to implement. I got as far as having my outer shell organ implement SizeA1Expresser, but nothing actually happened yet.

Something happens now.

First, on gene init, we tell the gene what expresser interface is associated with it. It would have been nice to avoid this and then grab the expresser type by string, since they will always be called geneNameExpresser, but in Go this isn’t an option as far as I know (which is also why I have organ and gene string-keyed type maps)

func (g *SizeA1) Init() {
	g.Id = "size_a1"
	g.DominanceType = organism.Complete

	var e *SizeA1Expresser
	g.Expresser = reflect.TypeOf(e).Elem()
	g.Alleles = g.getVariations()
}

I have also renamed OrganType to OrganKind to avoid confusion with the actual type system…

In my snail package, I’ve added some initial update steps. These will run on each tick to check a snail’s status - organ function, brain checks, etc:

func (m *Snail) UpdateOrganism() error {
	err := m.UpdateOrgans()
	return err
}

In UpdateOrgans we loop through each organ, and then through each gene. We check if the OrganKind implements the gene’s Expresser interface. If so, we get the methods of the interface (which should only be one at all times for now), find them in the organ, and run them.

func (m *Snail) UpdateOrgans() error {
	for _, o := range m.Organs {
		// Loop through each gene to find its expesser interface
		for _, v := range m.genes {
			organKindType := reflect.TypeOf(o.Kind())
			if !organKindType.Implements(v.Expresser) {
				continue
			}
			for i := 0; i < v.Expresser.NumMethod(); i++ {
				methodName := v.Expresser.Method(i).Name
				method := reflect.ValueOf(o.Kind()).MethodByName(methodName)

				valOfOrgan := reflect.ValueOf(o)
				method.Call([]reflect.Value{valOfOrgan})
			}
		}
	}
	return nil
}

The first basic test for this looks like this:

func TestOrganUpdate(t *testing.T) {
	snail := snail.Snail{}
	snail.Id = 1
	snail.Init()
	outerShell := organ.NewOuterShell()
	outerShell.OwnerId = snail.Id
	outerShell.SetOwner(&snail)
	snail.Organs = append(snail.Organs, &outerShell)

	sizeA1Gene := gene.SizeA1{}
	sizeA1Gene.Init()
	snail.Genes()[sizeA1Gene.Id] = &sizeA1Gene.Gene

	err := snail.UpdateOrgans()
	if err != nil {
		t.Errorf(err.Error())
	} else if outerShell.WeightMg != 1 {
		t.Errorf("Expected size_a1 gene to increase outer shell weight by 1; actual weight is %d", outerShell.WeightMg)
	}
}

Here we expect the weight of the outer shell organ to be 1 by the time the organ update is complete, because it started off at 0 by default and we create the gene with its default variations, where we know there will be a dominant allele which will increase the weight by 1.

Anyway, it’s all pretty up in the air by now; this approach might change tomorrow, but I’ll see how it goes.

comments powered by Disqus