expdef: name:Benthic foraminifera reproduction 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 */ global foodenergywaiting; global reprocounter; global delta_change; @include "forams_repro.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 = 1; 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; var size_h = ExpParams.haplo_rad * ExpParams.delta_rate + ExpParams.haplo_rad; var size_d = ExpParams.diplo_rad * ExpParams.delta_rate + ExpParams.diplo_rad; ExpParams.gend = "//0\np:sh=1,sx=" + ExpParams.diplo_rad + ",sy=" + ExpParams.diplo_rad + ",sz=" + ExpParams.diplo_rad + ", rz=3.14159265358979"; ExpParams.genh = "//0\np:sh=1,sx=" + size_h + ",sy=" + size_h + ",sz=" + size_h + ", rz=3.14159265358979"; 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; //radius of the chamber ExpParams.haplo_rad = 1.2; ExpParams.diplo_rad = 0.6; //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; //size change rate ExpParams.delta_rate = 0.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; delta_change = 0.5; } @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, "delta_h" : ExpParams.haplo_rad * ExpParams.delta_rate, "delta_d" : ExpParams.diplo_rad * ExpParams.delta_rate}; cr.user2 = {"Va" : ExpParams.v_min_h - ExpParams.repro_thr, "gen" : 0, "growth_step" : ExpParams.growth_step, "rsize" : ExpParams.haplo_rad, "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) { 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) { // cr.energy_m = cr.user2["Va"]/cr.user2["vinit"]; } if (cr.lifespan >= ExpParams.max_age) { Populations[0].kill(cr); } //foram growth else if (cr.lifespan == cr.user2["growth_step"]) { var pos = [cr.center_x, cr.center_y, cr.center_z]; var energy = cr.energy; var cr2 = null; if (cr.user2["gen"] == 0) { var new_r = ExpParams.haplo_rad * Math.min(Math.max(cr.user2["Va"] / cr.user2["vinit"] * 0.5, 1), 2.5); cr.user2["rsize"] = new_r; cr2 = Populations[0].add("//0\np:sh=1,sx=" + cr.user2["rsize"] + ",sy=" + cr.user2["rsize"] + ",sz=" + cr.user2["rsize"] + ", rz=3.14159265358979"); cr2.user1 = {"vamin" : cr.user1["vamin"], "amin": cr.user1["amin"], "delta_h" : cr.user1["delta_h"], "delta_d" : cr.user1["delta_d"]}; } else if (cr.user2["gen"] == 1) { var new_r = ExpParams.diplo_rad * Math.min(Math.max(cr.user2["Va"] / cr.user2["vinit"] * 0.5, 1), 2.5); cr.user2["rsize"] = new_r; var geno = "//0\np:sh=1,sx=" + cr.user2["rsize"] + ",sy=" + cr.user2["rsize"] + ",sz=" + cr.user2["rsize"] + ", rz=3.14159265358979"; geno += "\nn:p=0,d=\"S\""; //TODO is this the only difference with haploid code? 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) cr2 = Populations[0].add(geno); cr2.user1 = [ {"vamin" : cr.user1[0]["vamin"], "amin": cr.user1[0]["amin"], "delta_h" : cr.user1[0].get("delta_h"), "delta_d" : cr.user1[0]["delta_d"] }, {"vamin" : cr.user1[1]["vamin"], "amin": cr.user1[1]["amin"], "delta_h" : cr.user1[1]["delta_h"], "delta_d" : cr.user1[1]["delta_d"] }]; } cr2.energy = energy; cr2.user2 = {"Va" : cr.user2["Va"], "gen" : cr.user2["gen"], "growth_step" : cr.user2["growth_step"], "rsize" : cr.user2["rsize"], "vinit": cr.user2["vinit"]}; 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++; } // -------------------------------- 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); var retry = 100; //try 100 times while (retry--) { placeCreatureRandomly(cr, 0, 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["Va"]); var sum = float(float(ener) + float(e)); Collision.Creature2.user2["Va"] = float(sum); } } // -------------------------------- 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:haplo_rad name:Haploid radius type:f 0.1 3 1.2 prop: id:growth_step name:Growth step type:d 50 10000 1000 prop: id:delta_rate name:Delta rate type:f 0.0001 0.1 0.001 prop: id:diplo_rad name:Diploid radius type:f 0.1 3 0.6 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: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:Threshold 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: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 state: id:food name:Food locations help:vector of vectors [x,y,energy] type:x flags:32