diff --git a/src/Controllers/ControlPanel.js b/src/Controllers/ControlPanel.js index 46cda00..ca87c2b 100644 --- a/src/Controllers/ControlPanel.js +++ b/src/Controllers/ControlPanel.js @@ -204,7 +204,6 @@ class ControlPanel { if (!env.reset(true, false)) return; let center = env.grid_map.getCenter(); let org = this.editor_controller.env.getCopyOfOrg(); - this.env_controller.add_new_species = true; this.env_controller.dropOrganism(org, center[0], center[1]) }); $('#save-env').click( () => { @@ -464,8 +463,6 @@ class ControlPanel { if (mode == Modes.Clone) { this.env_controller.org_to_clone = this.engine.organism_editor.getCopyOfOrg(); - this.env_controller.add_new_species = this.editor_controller.new_species; - this.editor_controller.new_species = false; } } diff --git a/src/Controllers/EditorController.js b/src/Controllers/EditorController.js index 38cec42..0c4ddd1 100644 --- a/src/Controllers/EditorController.js +++ b/src/Controllers/EditorController.js @@ -3,6 +3,7 @@ const Modes = require("./ControlModes"); const CellStates = require("../Organism/Cell/CellStates"); const Directions = require("../Organism/Directions"); const Hyperparams = require("../Hyperparameters"); +const Species = require("../Stats/Species"); class EditorController extends CanvasController{ constructor(env, canvas) { @@ -10,7 +11,6 @@ class EditorController extends CanvasController{ this.mode = Modes.None; this.edit_cell_type = null; this.highlight_org = false; - this.new_species = false; this.defineCellTypeSelection(); this.defineEditorDetails(); this.defineSaveLoad(); @@ -46,7 +46,6 @@ class EditorController extends CanvasController{ else if (this.right_click) this.env.removeCellFromOrg(this.mouse_c, this.mouse_r); - this.new_species = true; this.setBrainPanelVisibility(); this.setMoveRangeVisibility(); this.updateDetails(); @@ -134,6 +133,9 @@ class EditorController extends CanvasController{ this.refreshDetailsPanel(); this.env.organism.updateGrid(); this.env.renderFull(); + this.env.organism.species = new Species(this.env.organism.anatomy, null, 0); + if (org.species_name) + this.env.organism.species.name = org.species_name; if (this.mode === Modes.Clone) $('#drop-org').click(); // have to clear the value so change() will be triggered if the same file is uploaded again diff --git a/src/Controllers/EnvironmentController.js b/src/Controllers/EnvironmentController.js index 93f3ddb..61a9d57 100644 --- a/src/Controllers/EnvironmentController.js +++ b/src/Controllers/EnvironmentController.js @@ -12,7 +12,6 @@ class EnvironmentController extends CanvasController{ super(env, canvas); this.mode = Modes.FoodDrop; this.org_to_clone = null; - this.add_new_species = false; this.defineZoomControls(); this.scale = 1; } @@ -171,15 +170,15 @@ class EnvironmentController extends CanvasController{ var new_org = new Organism(col, row, this.env, organism); if (new_org.isClear(col, row)) { - if (this.add_new_species){ + let new_species = !FossilRecord.speciesIsExtant(new_org.species.name); + if (new_org.species.extinct) { + FossilRecord.resurrect(new_org.species); + } + else if (new_species) { FossilRecord.addSpeciesObj(new_org.species); new_org.species.start_tick = this.env.total_ticks; - this.add_new_species = false; new_org.species.population = 0; } - else if (this.org_to_clone.species.extinct){ - FossilRecord.resurrect(this.org_to_clone.species); - } this.env.addOrganism(new_org); new_org.species.addPop(); diff --git a/src/Environments/OrganismEditor.js b/src/Environments/OrganismEditor.js index 6aadc0b..f01d9ed 100644 --- a/src/Environments/OrganismEditor.js +++ b/src/Environments/OrganismEditor.js @@ -72,7 +72,6 @@ class OrganismEditor extends Environment{ this.organism = new Organism(center[0], center[1], this, orig_org); this.organism.updateGrid(); this.controller.updateDetails(); - this.controller.new_species = false; } getCopyOfOrg() { @@ -109,7 +108,6 @@ class OrganismEditor extends Environment{ newOrganism.species = new Species(newOrganism.anatomy, null, 0); var col = Math.floor(size + (Math.random() * (env.grid_map.cols-(size*2)) ) ); var row = Math.floor(size + (Math.random() * (env.grid_map.rows-(size*2)) ) ); - env.controller.add_new_species = true; env.controller.dropOrganism(newOrganism, col, row); } } diff --git a/src/Environments/WorldEnvironment.js b/src/Environments/WorldEnvironment.js index 5e89fef..0ca05b5 100644 --- a/src/Environments/WorldEnvironment.js +++ b/src/Environments/WorldEnvironment.js @@ -36,10 +36,10 @@ class WorldEnvironment extends Environment{ to_remove.push(i); } } + this.removeOrganisms(to_remove); if (Hyperparams.foodDropProb > 0) { this.generateFood(); } - this.removeOrganisms(to_remove); this.total_ticks ++; if (this.total_ticks % this.data_update_rate == 0) { FossilRecord.updateData(); @@ -122,6 +122,16 @@ class WorldEnvironment extends Environment{ org.die(); this.organisms = []; } + + clearDeadOrganisms() { + let to_remove = []; + for (let i in this.organisms) { + let org = this.organisms[i]; + if (!org.living) + to_remove.push(i); + } + this.removeOrganisms(to_remove); + } generateFood() { var num_food = Math.max(Math.floor(this.grid_map.cols*this.grid_map.rows*Hyperparams.foodDropProb/50000), 1) @@ -168,6 +178,7 @@ class WorldEnvironment extends Environment{ } serialize() { + this.clearDeadOrganisms(); let env = SerializeHelper.copyNonObjects(this); env.grid = this.grid_map.serialize(); env.organisms = []; @@ -198,7 +209,7 @@ class WorldEnvironment extends Environment{ org.loadRaw(orgRaw); this.addOrganism(org); let s = species[orgRaw.species_name]; - if (!s){ // ideally, every organisms species should exists, but there is a bug somewhere + if (!s){ // ideally, every organisms species should exists, but there is a bug that misses some species sometimes s = new Species(org.anatomy, null, env.total_ticks); species[orgRaw.species_name] = s; } diff --git a/src/Stats/FossilRecord.js b/src/Stats/FossilRecord.js index 17ec347..2a295ae 100644 --- a/src/Stats/FossilRecord.js +++ b/src/Stats/FossilRecord.js @@ -4,8 +4,8 @@ const Species = require("./Species"); const FossilRecord = { init: function(){ - this.extant_species = []; - this.extinct_species = []; + this.extant_species = {}; + this.extinct_species = {}; // if an organism has fewer than this cumulative pop, discard them on extinction this.min_discard = 10; @@ -20,45 +20,44 @@ const FossilRecord = { addSpecies: function(org, ancestor) { var new_species = new Species(org.anatomy, ancestor, this.env.total_ticks); - this.extant_species.push(new_species); + this.extant_species[new_species.name] = new_species; org.species = new_species; return new_species; }, addSpeciesObj: function(species) { - this.extant_species.push(species); + if (this.extant_species[species.name]) { + console.warn('Tried to add already existing species. Add failed.'); + return; + } + this.extant_species[species.name] = species; return species; }, + numExtantSpecies() {return Object.values(this.extant_species).length}, + numExtinctSpecies() {return Object.values(this.extinct_species).length}, + speciesIsExtant(species_name) {return !!this.extant_species[species_name]}, + fossilize: function(species) { - species.end_tick = this.env.total_ticks; - for (i in this.extant_species) { - var s = this.extant_species[i]; - if (s == species) { - this.extant_species.splice(i, 1); - species.ancestor = undefined; // garbage collect dead species - // if (species.ancestor) - // species.ancestor.ancestor = undefined; - if (species.cumulative_pop < this.min_pop) { - return false; - } - // disabled for now, causes memory problems on long runs - // this.extinct_species.push(s); - return true; - } + if (!this.extant_species[species.name]) { + console.warn('Tried to fossilize non existing species.'); + return false; } + species.end_tick = this.env.total_ticks; + species.ancestor = undefined; // garbage collect ancestors + delete this.extant_species[species.name]; + if (species.cumulative_pop >= this.min_discard) { + // TODO: store as extinct species + return true; + } + return false; }, resurrect: function(species) { if (species.extinct) { - for (i in this.extinct_species) { - var s = this.extinct_species[i]; - if (s == species) { - this.extinct_species.splice(i, 1); - this.extant_species.push(species); - species.extinct = false; - } - } + species.extinct = false; + this.extant_species[species.name] = species; + delete this.extinct_species[species.name]; } }, @@ -77,7 +76,7 @@ const FossilRecord = { var tick = this.env.total_ticks; this.tick_record.push(tick); this.pop_counts.push(this.env.organisms.length); - this.species_counts.push(this.extant_species.length); + this.species_counts.push(this.numExtantSpecies()); this.av_mut_rates.push(this.env.averageMutability()); this.calcCellCountAverages(); while (this.tick_record.length > this.record_size_limit) { @@ -97,8 +96,8 @@ const FossilRecord = { cell_counts[c.name] = 0; } var first=true; - for (let s of this.extant_species) { - if (s.cumulative_pop < this.min_discard && !first){ + for (let s of Object.values(this.extant_species)) { + if (!first && this.numExtantSpecies() > 10 && s.cumulative_pop < this.min_discard){ continue; } for (let name in s.cell_counts) { @@ -107,8 +106,11 @@ const FossilRecord = { total_org += s.population; first=false; } - if (total_org == 0) - return cell_counts; + if (total_org == 0) { + this.av_cells.push(0); + this.av_cell_counts.push(cell_counts); + return; + } var total_cells = 0; for (let c in cell_counts) { @@ -137,7 +139,7 @@ const FossilRecord = { av_cell_counts:this.av_cell_counts, }; let species = {}; - for (let s of this.extant_species) { + for (let s of Object.values(this.extant_species)) { species[s.name] = SerializeHelper.copyNonObjects(s); delete species[s.name].name; // the name will be used as the key, so remove it from the value } diff --git a/src/Stats/StatsPanel.js b/src/Stats/StatsPanel.js index 4487861..e7392e4 100644 --- a/src/Stats/StatsPanel.js +++ b/src/Stats/StatsPanel.js @@ -50,7 +50,7 @@ class StatsPanel { updateDetails() { var org_count = this.env.organisms.length; $('#org-count').text("Total Population: " + org_count); - $('#species-count').text("Number of Species: " + FossilRecord.extant_species.length); + $('#species-count').text("Number of Species: " + FossilRecord.numExtantSpecies()); $('#largest-org').text("Largest Organism Ever: " + this.env.largest_cell_count + " cells"); $('#avg-mut').text("Average Mutation Rate: " + Math.round(this.env.averageMutability() * 100) / 100);