Index: /experiments/frams/foraminifera/data/scripts/foraminifera.expdef
===================================================================
--- /experiments/frams/foraminifera/data/scripts/foraminifera.expdef	(revision 380)
+++ /experiments/frams/foraminifera/data/scripts/foraminifera.expdef	(revision 380)
@@ -0,0 +1,627 @@
+expdef:
+name:Foraminifera simulation
+info:~
+Genes and parameter values which control reproduction are stored in user1 and user2 fields.
+
+user1:
+genes which are not encoded in ff genotype:
+Vamin - Minimum stored energy necessary for reproduction
+amin - minimal age for reproduction
+
+user2:
+Physiological parameters of foraminifera:
+Va - amount of the stored energy
+gen - generation: 0 haploid, 1 diploid
+~
+code:~
+
+global foodenergywaiting;
+global reprocounter;
+
+// -------------------------------- experiment begin --------------------------------
+
+function onExpDefLoad()
+{
+	// define genotype and creature groups
+	GenePools.clear();
+	Populations.clear();
+	GenePools[0].name = "Unused";
+
+	var pop = Populations[0];
+	pop.name = "Creatures";
+	pop.en_assim = 0;
+	pop.nnsim = 1;
+	pop.enableperf = 1;
+	pop.death = 1;
+	pop.energy = 1;
+	pop.selfmask = 0x10001;
+	pop.othermask = 0x30000;
+	pop.perfperiod = 25;
+
+	pop = Populations.addGroup("Food");
+	pop.nnsim = 0;
+	pop.enableperf = 0;
+	pop.death = 1;
+	pop.energy = 1;
+	pop.selfmask = 0x20002;
+	pop.othermask = 0x30002;
+
+	ExpParams.genh = "/*F*/1,1.1,1.1,1.1,1,1,1";
+	ExpParams.gend = "/*F*/1,1,1,1,1,1,1";
+	ExpParams.p_mut = 50;
+	ExpParams.e_meta = 0.1;
+	ExpParams.feedrate = 0.7;
+	ExpParams.feede0 = 100;
+	ExpParams.feedtrans = 3;
+	ExpParams.creath = -0.99; //just above the bottom
+	ExpParams.foodgen = "//0\np:sh=1,sx=0.1,sy=0.1,sz=0.1";
+	ExpParams.autorestart = 0;
+	ExpParams.psize = 10;
+	ExpParams.ofnumh = 14;
+	ExpParams.ofnumd = 4;
+	ExpParams.v_min_d = 200;
+	ExpParams.v_min_h = 500;
+	ExpParams.age_min_d = 150;
+	ExpParams.age_min_h = 300;
+	ExpParams.crossprob = 0.3;
+	ExpParams.repro_time = 200;
+	ExpParams.repro_thr = 1;
+
+	ExpState.totaltestedcr = 0;
+	ExpState.food = "";
+	foodenergywaiting = ExpParams.feede0;
+	reprocounter = 0;
+
+	ExpParams.wsize = 30;
+
+	World.wrldwat = 50;
+	World.wrldsiz = ExpParams.wsize;
+	World.wrldbnd = 1;
+}
+
+@include "standard_placement.inc"
+
+function onExpInit()
+{
+	Populations[0].clear();
+	Populations[1].clear();
+
+	for (var i = 0; i < ExpParams.psize; i++)
+	{
+		var cr = Populations[0].add(ExpParams.genh);
+		cr.name = "Initial creature" + i;
+		placeCreatureRandomly(cr, 0, 0);
+		cr.energ0 = ExpParams.v_min_h - ExpParams.repro_thr;
+		cr.energy = cr.energ0;
+		cr.user1 = {"vamin" : ExpParams.v_min_h, "amin": ExpParams.age_min_h};
+		cr.user2 = {"Va" : ExpParams.v_min_h - ExpParams.repro_thr, "gen" : 0 };
+	}
+	ExpState.totaltestedcr = 0;
+	foodenergywaiting = ExpParams.feede0;
+}
+
+function onExpLoad()
+{
+	for (var pop in Populations)
+		pop.clear();
+
+	Loader.addClass(sim_params.*);
+	Loader.setBreakLabel(Loader.BeforeUnknown, "onExpLoad_Unknown");
+	Loader.run();
+
+	Simulator.print("Loaded " + Populations[0].size + " creatures and " + Populations[1].size + " food objects");
+}
+
+function onExpLoad_Unknown()
+{
+	if (Loader.objectName == "org") // saved by the old expdef
+	{
+		var g = Genotype.newFromString("");
+		Loader.currentObject = g;
+		Interface.makeFrom(g).setAllDefault();
+		Loader.loadObject();
+		var cr = Populations[0].add(g);
+		if (cr != null)
+		{
+			cr.rotate(0, 0, Math.rnd01*Math.twopi);
+			if ((typeof(g.user1) == "Vector") && (g.user1.size >= 3))
+			{ // [x,y,energy]
+				cr.move(g.user1[0] - cr.center_x, g.user1[1] - cr.center_y, 0);
+				cr.energy = g.user1[2];
+			}
+			else
+			{
+				cr.move(Math.rnd01*World.wrldsiz - cr.center_x, Math.rnd01*World.wrldsiz - cr.center_y, 0);
+			}
+		}
+	}
+	else if (Loader.objectName == "Creature")
+	{
+		Loader.currentObject = CreatureSnapshot.new();
+		Loader.loadObject();
+		Populations[0].add(Loader.currentObject);
+	}
+}
+
+function onExpSave()
+{
+	File.writeComment("saved by '%s.expdef'" % Simulator.expdef);
+
+	var i, tmpvec = [];
+
+	for (var cr in Populations[1])
+		tmpvec.add([cr.center_x, cr.center_y, cr.energy]);
+
+	ExpState.food = tmpvec;
+	File.writeObject(sim_params.*);
+	ExpState.food = null; //vectors are only created for saving and then discarded
+
+	for (var cr in Populations[0])
+		File.writeObject(cr);
+}
+
+// -------------------------------- experiment end --------------------------------
+
+// -------------------------------- creature begin --------------------------------
+
+function onCreaturesBorn(cr)
+{
+	cr.idleen = ExpParams.e_meta;
+}
+
+function readyToRepro(cr)
+{
+	cr.signals.add("repro");
+	cr.signals.get(0).power = 1;
+}
+
+function onCreaturesStep(cr)
+{
+	cr.moveAbs(cr.pos_x, cr.pos_y, 0);
+	var p = cr.getMechPart(0);
+	var n = cr.signals.receiveSet("food", ExpParams.food_range);
+
+	if (n.size > 0)
+	{
+		var i;
+		var mp;
+		var distvec = XYZ.new(0, 0, 0);
+		var dist;
+		var mindist = 100000000000;
+		var mindistvec = null;
+
+		if (Math.rnd01 < 0.8)
+		{
+			for (i = 0;i < n.size;i++)
+			{
+				mp = n[i].value;
+				distvec.set(mp.pos);
+				distvec.sub(p.pos);
+				dist = distvec.length;
+				if (dist < mindist)
+				{
+					mindist = dist;
+					mindistvec = distvec.clone();
+				}
+			}
+		}
+		else
+		{
+			mp = n[Math.random(n.size)].value;
+			distvec.set(mp.pos);
+			distvec.sub(p.pos);
+			mindistvec = distvec.clone();
+		}
+
+		mindistvec.normalize();
+		mindistvec.scale( -0.1);
+		cr.localDrive = mindistvec;
+	}
+	else
+	{
+		cr.localDrive = (0.1, 0, 0);
+	}
+
+	var properAge = cr.user2["Va"] >= cr.user1["vamin"];
+	var properSize = cr.user1["amin"] <= cr.lifespan;
+
+	//if creature has proper age and cytoplasm amount
+	if ( properAge && properSize )
+	{
+		//reproduce with probability repro_prob
+		if (Math.rnd01 <= ExpParams.repro_prob)
+		{
+			readyToRepro(cr);
+		}
+	}
+	if ( properAge && properSize && (cr.signals.receive("repro") > 0))
+	{
+		readyToRepro(cr);
+	}
+}
+
+function onCreaturesDied(cr)
+{
+	//fossilization
+	var geno = GenePools[0].add(cr.genotype);
+	geno.user1 = cr.user1;
+	geno.user2 = cr.user2;
+	Simulator.print("\"" + cr.name + "\" died...");
+	ExpState.totaltestedcr++;
+}
+
+// -------------------------------- creature end --------------------------------
+
+// -------------------------------- food begin --------------------------------
+
+function onFoodStep(cr)
+{
+	cr.moveAbs(cr.pos_x % ExpParams.wsize, cr.pos_y % ExpParams.wsize, 0.5);
+}
+
+function addfood()
+{
+	var cr = Populations[1].add(ExpParams.foodgen);
+
+	cr.name = "Food";
+	cr.idleen = 0;
+	cr.energ0 = ExpParams.feede0;
+	cr.energy = cr.energ0;
+	cr.signals.add("food");
+
+	cr.signals.get(0).value = cr.getMechPart(0);
+
+	var retry = 50; //try 50 times
+	while (retry--)
+	{
+		placeCreatureRandomly(cr, 0, 0);
+		cr.signals.get(0).value = cr.getMechPart(0);
+		if (!cr.boundingBoxCollisions(0))
+			return cr;
+	}
+
+	return cr;
+}
+
+function onFoodCollision()
+{
+	if (Collision.Creature2.user2 != null)
+	{
+		var e = Collision.Part2.ing * ExpParams.feedtrans;
+		//Simulator.print("transferring "+e+" from "+Collision.Creature1.name+" to "+Collision.Creature2.name+" ("+Collision.Creature2.energy+")");
+		Collision.Creature1.energy_m = Collision.Creature1.energy_m + e;
+		Collision.Creature2.energy_p = Collision.Creature2.energy_p + e;
+		var ener = float(Collision.Creature2.user2.get("Va"));
+		var sum = float(float(ener) + float(e));
+		Collision.Creature2.user2.set("Va", float(sum));
+	}
+}
+
+// -------------------------------- food end --------------------------------
+
+// -------------------------------- step begin --------------------------------
+
+function reproduce(repro_list, number)
+{
+	for (var i = 0; i < repro_list.size; i++)
+	{
+		var parent = Populations[0].get(repro_list[i]);
+
+		var energ = parent.user2["Va"] / number;
+		for (var j = 0; j < number; j++)
+		{
+			var cr = Populations[0].add(ExpParams.gend);
+			cr.energ0 = energ;
+			cr.energy = cr.energ0;
+			if (parent.user2["gen"] == 0)
+			{
+				cr.user1 = {"vamin" : ExpParams.v_min_d, "amin": ExpParams.age_min_d};
+			}
+			else
+			{
+				cr.user1 = {"vamin" : ExpParams.v_min_h, "amin": ExpParams.age_min_h};
+			}
+			cr.user2 = { "Va" : cr.energ0, "gen" : 1 - parent.user2["gen"] };
+			placeCreatureRandomly(cr, 0, 0);
+		}
+	}
+	for (var j = 0; j < repro_list.size; j++)
+	{
+		Populations[0].kill(repro_list[j]);
+	}
+}
+
+function log(tolog, fname)
+{
+	var f = File.appendDirect(fname, "description");
+	f.writeString("" + Simulator.stepNumber);
+	for (var i = 0; i < tolog.size; i++)
+	{
+		f.writeString(";" + tolog[i]);
+	}
+	f.writeString("\n");
+	f.close();
+}
+
+
+
+function onStep()
+{
+	var haploids = 0;
+	var diploids = 0;
+	var e_inc_h = 0.0;
+	var e_inc_d = 0.0;
+	var e_nut = 0.0;
+
+	for (var i = 0; i < Populations[0].size; i++)
+	{
+		var cr = Populations[0].get(i);
+		if (cr.user2["gen"] == 0)
+		{
+			haploids += 1;
+			e_inc_h += cr.energy;
+		}
+		else
+		{
+			diploids += 1;
+			e_inc_d += cr.energy;
+		}
+	}
+
+	for (var i = 0; i < Populations[1].size; i++)
+	{
+		var cr = Populations[1].get(i);
+		e_nut += cr.energy;
+	}
+
+	if (haploids < 2 && diploids == 0)
+	{
+		Simulator.print("no more creatures, stopped.");
+		Simulator.stop();
+	}
+
+	var l1 = Vector.new();
+	l1 = [haploids, diploids, Populations[1].size];
+	var l2 = Vector.new();
+	l2 = [e_inc_h, e_inc_d, e_nut];
+
+	log(l1, "log.txt");
+	log(l2, "log2.txt");
+	//food growth ---------------------------------------------
+	foodenergywaiting = foodenergywaiting + ExpParams.feedrate;
+	if (foodenergywaiting > ExpParams.feede0)
+	{
+		for (var i = 0; i < ExpParams.foodPop; i++)
+		{
+			addfood();
+		}
+
+		foodenergywaiting = 0.0;
+		Simulator.checkpoint();
+	}
+
+	//reproduction --------------------------------------------
+	reprocounter += 1;
+	if (reprocounter > ExpParams.repro_time)
+	{
+		reprocounter = 0;
+		var to_repro_h = Vector.new();
+		var to_repro_d = Vector.new();
+		for (var i = 0; i < Populations[0].size; i++)
+		{
+			var cr = Populations[0].get(i);
+			if (cr.signals.size > 0)
+			{
+				if (cr.user2["gen"] == 0)
+				{
+					to_repro_h.add(i);
+				}
+				else
+				{
+					to_repro_d.add(i);
+				}
+			}
+		}
+		if (to_repro_h.size > 0)
+		{
+			reproduce(to_repro_h, ExpParams.ofnumh);
+		}
+		if (to_repro_d.size > 1)
+		{
+			reproduce(to_repro_d, ExpParams.ofnumd);
+		}
+	}
+
+	//check for death -----------------------------------------------
+	if (Populations[0].size == 0)
+	{
+		if (ExpParams.autorestart)
+		{
+			Simulator.print("no more creatures, restarting...");
+			onExpInit();
+		}
+		else
+		{
+			Simulator.print("no more creatures, stopped.");
+			Simulator.stop();
+		}
+	}
+}
+
+// -------------------------------- step end --------------------------------
+
+
+@include "standard_events.inc"
+
+~
+
+prop:
+id:wsize
+name:world size
+type:d 10 1000 20
+
+prop:
+id:foodPop
+name:food size
+type:d 1 1000 10
+
+prop:
+id:age_min
+name:minimal age for reproduction
+type:d 100 10000 200
+
+prop:
+id:age_min_h
+name:Min reproduction age of haploid forams
+type:d 100 10000 300
+group:Foraminifera
+
+prop:
+id:age_min_d
+name:Min reproduction age of diploid forams
+type:d 100 10000 200
+group:Foraminifera
+
+prop:
+id:v_min_h
+name:Min reproduction energy of haploid forams
+type:d 100 10000 300
+group:Foraminifera
+
+prop:
+id:v_min_d
+name:Min reproduction energy of diploid forams
+type:d 100 10000 150
+group:Foraminifera
+
+prop:
+id:repro_thr
+name:treshold for reproduction
+type:d 1 1000 1
+
+prop:
+id:food_range
+name:range of food smell
+type:d 0 10000 10000
+
+prop:
+id:repro_time
+name:time before reproduction
+type:d 0 1000
+
+prop:
+id:psize
+name:initial population size
+type:d 1 1000 100
+
+prop:
+id:ofnumh
+name:Number of offspring from haploid forams
+type:d
+group:Foraminifera
+
+prop:
+id:ofnumd
+name:Number of offspring from diploid forams
+type:d
+group:Foraminifera
+
+prop:
+id:repro_prob
+name:probability of reproduction
+type:f 0 1 0.9
+
+prop:
+id:crossprob
+name:crossover probability
+type:f 0 1
+
+prop:
+id:genh
+name:Initial genotype of haploid forams
+type:s 1
+group:Foraminifera
+
+prop:
+id:gend
+name:Initial genotype of diploid forams
+type:s 1
+group:Foraminifera
+
+prop:
+id:creath
+name:Creation height
+type:f -1 50
+help:~
+Vertical position (above the surface) where new creatures are revived.
+Negative values are only used in the water area:
+  0   = at the surface
+-0.5 = half depth
+-1   = just above the bottom~
+
+prop:
+id:p_mut
+name:Mutations
+type:f 0 100
+
+prop:
+id:e_meta
+name:Idle metabolism
+type:f 0 1
+group:Energy
+help:Each stick consumes this amount of energy in one time step
+
+prop:
+id:feedrate
+name:Feeding rate
+type:f 0 100
+group:Energy
+help:How fast energy is created in the world
+
+prop:
+id:feede0
+name:Food's energy
+group:Energy
+type:f 0 1000
+
+prop:
+id:feedtrans
+name:Ingestion multiplier
+group:Energy
+type:f 0 100
+
+prop:
+id:foodgen
+name:Food's genotype
+group:Energy
+type:s 1
+
+prop:
+id:autorestart
+name:Restart after extinction
+group:
+help:Restart automatically this experiment after the last creature has died?
+type:d 0 1
+
+state:
+id:notes
+name:Notes
+type:s 1
+help:~
+You can write anything here
+(it will be saved to the experiment file)~
+
+state:
+id:totaltestedcr
+name:Evaluated creatures
+help:Total number of the creatures evaluated in the experiment
+type:d
+flags:16
+
+state:
+id:food
+name:Food locations
+help:vector of vectors [x,y,energy]
+type:x
+flags:32
+
