expdef: name:Reproduction of benthic foraminifera 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:~ /* changes 2015-06-24: - xxx.get(...) changed to xxx[...] - xxx.set(...,...) changed to xxx[...]=... - function placeRandomlyNotColliding(cr) explicitly called when adding a new creature, and removed collision checking from onCreaturesBorn() which is also called when "growing" (since "growing" is implemented as creating a new creature in place of the old one, so onCreaturesBorn() is also called) - use creature's center (not the bbox corner) when growing - added "S" receptor to visualize the difference between diplo and haplo generations - signal.value is a MechPart (a reference) so it does not have to be updated after changing the object location */ /* TODO fix [ERROR] Population.kill: Invalid index 35 (allowed range is 0..34) [ERROR] Population.kill: ... called from reproduce_haploid() [ERROR] Population.kill: ... called from onStep() */ global foodenergywaiting; global reprocounter; @include "foraminifera.inc" // -------------------------------- experiment begin -------------------------------- function onExpDefLoad() { // define genotype and creature groups GenePools.clear(); Populations.clear(); GenePools[0].name = "Unused"; SignalView.mode = 1; var pop = Populations[0]; pop.name = "Creatures"; pop.en_assim = 0; pop.nnsim = 0; pop.enableperf = 1; pop.death = 1; pop.energy = 1; pop.selfmask = 0x10001; pop.othermask = 0x20001; //pop.selfmask = 0x20002; pop.othermask = 0x10002; pop.perfperiod = 25; pop = Populations.addGroup("Food"); pop.nnsim = 0; pop.enableperf = 0; pop.death = 1; pop.energy = 1; pop.selfmask = 0x20002; pop.othermask = 0x10000; //radius of the chamber ExpParams.rads = [1.2, 0.6]; //inital genotypes ExpParams.genh = "//0\np:sh=1,sx=" + ExpParams.rads[0] + ",sy=" + ExpParams.rads[0] + ",sz=" + ExpParams.rads[0] + ", rz=3.14159265358979"; ExpParams.gend = "//0\np:sh=1,sx=" + ExpParams.rads[1] + ",sy=" + ExpParams.rads[1] + ",sz=" + ExpParams.rads[1] + ", rz=3.14159265358979"; ExpParams.gend += "\nn:p=0,d=\"S\""; ExpParams.e_meta = 0.1; ExpParams.feedrate = 0.5; ExpParams.feede0 = 100; ExpParams.feedtrans = 3; ExpParams.creath = -0.99; //just above the bottom ExpParams.foodgen = "//0\np:sh=2,sx=0.1,sy=0.1,sz=0.1\nn:d=T,p=0"; ExpParams.autorestart = 0; ExpParams.psize = 10; ExpParams.logging = 0; //number of offspring ExpParams.ofnumh = 40; ExpParams.ofnumd = 25; //minial volume for reproduction ExpParams.v_min_d = 300; ExpParams.v_min_h = 300; //minimal age for reproduction ExpParams.age_min_d = 100; ExpParams.age_min_h = 100; //crossover probability ExpParams.crossprob = 0.4; //mutation probability ExpParams.mutationprob = 0.2; ExpParams.repro_time = 200; ExpParams.repro_thr = 1; //grwoth speed in time steps ExpParams.growth_step = 50; ExpState.totaltestedcr = 0; ExpState.food = ""; foodenergywaiting = ExpParams.feede0; reprocounter = 0; ExpParams.wsize = 50; 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, "growth_step" : ExpParams.growth_step, "rsize" : ExpParams.rads[0], "vinit" : ExpParams.v_min_h - ExpParams.repro_thr}; } 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 tmpvec = [], i; 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 placeRandomlyNotColliding(cr) { var retry = 100; //try 100 times while (retry--) { placeCreatureRandomly(cr, 0, 0); if (!cr.boundingBoxCollisions(0)) return cr; } Populations[0].delete(cr); } function readyToRepro(cr) { cr.signals.add("repro"); cr.signals[0].power = 1; } function onCreaturesStep(cr) { //TODO moving inside sediment? cr.moveAbs(cr.pos_x, cr.pos_y, 0); //adjustment in z axis var p = cr.getMechPart(0); var n = cr.signals.receiveSet("food", ExpParams.food_range); //if signals are received find the source of the nearest if (n.size > 0) { var i; var mp; var distvec = XYZ.new(0, 0, 0); var dist; var mindist = 100000000000; var mindistvec = null; 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(); } } mindistvec.normalize(); mindistvec.scale(-0.08); cr.localDrive = mindistvec; } else { cr.localDrive = (0.1 * Math.rnd01, 0.1 * Math.rnd01, 0); } //energy costs depend on size if (cr.energy > 100) { //TODO energy costs dependent on size // cr.energy_m = cr.user2["Va"]/cr.user2["vinit"]; } if (cr.lifespan >= ExpParams.max_age) { //TODO what is max age value? should there be one? Populations[0].kill(cr); return; } //foram growth if (cr.lifespan == cr.user2["growth_step"]) { //TODO how size is related to the energy? cr.user2["rsize"] = ExpParams.rads[cr.user2["gen"]] * Math.min(Math.max(float(cr.user2["Va"] / cr.user2["vinit"]) * 0.5, 1.0), 2.5); var geno = "//0\np:sh=1,sx=" + cr.user2["rsize"] + ",sy=" + cr.user2["rsize"] + ",sz=" + cr.user2["rsize"] + ", rz=3.14159265358979"; if (cr.user2["gen"] == 1) { geno += "\nn:p=0,d=\"S\""; //TODO why initial genotypes are not used as defined in ExpParams? //TODO maybe it would be nice if they rotated so the "S" would show where they are going (direction/intention) } var cr2 = Populations[0].add(geno); cr2.energy = cr.energy; setGenotype(cr2, cr.user1, cr.user2); cr2.moveAbs(cr.center_x - cr2.size_x / 2, cr.center_y - cr2.size_y / 2, cr.pos_z); Populations[0].delete(cr); } else { var properSize = 0; if (cr.user2["gen"] == 0) { properSize = cr.user2["Va"] >= cr.user1["vamin"]; } else { properSize = cr.user2["Va"] >= cr.user1[0]["vamin"]; } //if creature has proper age and cytoplasm amount if ( properSize ) { //reproduce with probability repro_prob if (Math.rnd01 <= ExpParams.repro_prob) { readyToRepro(cr); } } if ( 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++; } function setGenotype(cr, new_user1, new_user2) { cr.user2 = {"Va" : new_user2["Va"], "gen" : new_user2["gen"], "growth_step" : new_user2["growth_step"], "rsize" : new_user2["rsize"], "vinit": new_user2["vinit"]}; if (cr.user2["gen"] == 0) { cr.user1 = {"vamin" : new_user1["vamin"], "amin": new_user1["amin"] }; } else if (cr.user2["gen"] == 1) { cr.user1 = [ {"vamin" : new_user1[0]["vamin"], "amin": new_user1[0]["amin"] }, {"vamin" : new_user1[1]["vamin"], "amin": new_user1[1]["amin"] }]; } } // -------------------------------- 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[0].value = cr.getMechPart(0); placeRandomlyNotColliding(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; Collision.Creature2.user2["Va"] = float(Collision.Creature2.user2["Va"]) + float(e); } } // -------------------------------- food end -------------------------------- function log(tolog, fname) { var f = File.appendDirect(fname, "forams data"); f.writeString("" + Simulator.stepNumber); for (var i = 0; i < tolog.size; i++) { f.writeString(";" + tolog[i]); } f.writeString("\n"); f.close(); } @include "standard_events.inc" ~ prop: id:max_age name:Maximal age type:d 100 1000 500 prop: id:wsize name:World size type:d 10 1000 20 prop: id:growth_step name:Growth step type:d 10 10000 1000 prop: id:rads name:Haploid and diploid radius type:x 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:f 10 10000 300 group:Foraminifera prop: id:v_min_d name:Min reproduction energy of diploid forams type:f 10 10000 150 group:Foraminifera prop: id:repro_thr name:Energy creatures need to gather to reproduce 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:logging name:Log statistics to file type:d 0 1 0 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 0 prop: id:mutationprob name:Mutation probability type:f 0 1 0 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: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 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 prop: id:foodPop name:Food size type:d 1 1000 10 state: id:food name:Food locations help:vector of vectors [x,y,energy] type:x flags:32