diff --git a/Changelog.md b/Changelog.md
index 6c51ff4..3f8a18a 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,18 +1,20 @@
# Changelog
-## 1.0.3 (current dev)
+## 1.0.3 (4/15/2022)
### UI Enhancements:
-- Improved input box styling
+- Improved styling
### Simulation Enhancements:
--
+- Added ability to save/load organisms
+- Added ability to save/load worlds
### Bug Fixes:
- charste changed to charset
+- Fixed species tracking
-Thanks to contributors:
+Thanks to contributors: @TerraMaster85
## 1.0.2 (12/21/2021)
diff --git a/dist/index.html b/dist/index.html
index d36ce90..e4465e6 100644
--- a/dist/index.html
+++ b/dist/index.html
@@ -6,7 +6,6 @@
-
@@ -21,7 +20,7 @@
@@ -268,7 +279,7 @@
-
+
diff --git a/package-lock.json b/package-lock.json
index d276bd2..7994f85 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -198,9 +198,9 @@
"dev": true
},
"ajv": {
- "version": "6.12.2",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
- "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true,
"requires": {
"fast-deep-equal": "^3.1.1",
@@ -2048,9 +2048,9 @@
}
},
"minimist": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
- "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
+ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
"dev": true
},
"mississippi": {
diff --git a/src/Controllers/ControlPanel.js b/src/Controllers/ControlPanel.js
index f57b94b..46cda00 100644
--- a/src/Controllers/ControlPanel.js
+++ b/src/Controllers/ControlPanel.js
@@ -1,7 +1,6 @@
const Hyperparams = require("../Hyperparameters");
const Modes = require("./ControlModes");
const StatsPanel = require("../Stats/StatsPanel");
-const RandomOrganismGenerator = require("../Organism/RandomOrganismGenerator")
const WorldConfig = require("../WorldConfig");
class ControlPanel {
@@ -48,6 +47,8 @@ class ControlPanel {
defineHotkeys() {
$('body').keydown( (e) => {
+ let focused = document.activeElement;
+ if (focused.tagName === "INPUT" && focused.type === "text") return;
switch (e.key.toLowerCase()) {
// hot bar controls
case 'a':
@@ -206,6 +207,43 @@ class ControlPanel {
this.env_controller.add_new_species = true;
this.env_controller.dropOrganism(org, center[0], center[1])
});
+ $('#save-env').click( () => {
+ let was_running = this.engine.running;
+ this.setPaused(true);
+ let env = this.engine.env.serialize();
+ let data = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(env));
+ let downloadEl = document.getElementById('download-el');
+ downloadEl.setAttribute("href", data);
+ downloadEl.setAttribute("download", $('#save-env-name').val()+".json");
+ downloadEl.click();
+ if (was_running)
+ this.setPaused(false);
+ });
+ $('#load-env').click(() => {
+ $('#upload-env').click();
+ });
+ $('#upload-env').change((e)=>{
+ let files = e.target.files;
+ if (!files.length) {return;};
+ let reader = new FileReader();
+ reader.onload = (e) => {
+ try {
+ let was_running = this.engine.running;
+ this.setPaused(true);
+ let env = JSON.parse(e.target.result);
+ this.engine.env.loadRaw(env);
+ if (was_running)
+ this.setPaused(false);
+ this.updateHyperparamUIValues();
+ this.env_controller.resetView();
+ } catch(except) {
+ console.error(except)
+ alert('Failed to load world');
+ }
+ $('#upload-env')[0].value = '';
+ };
+ reader.readAsText(files[0]);
+ });
}
defineHyperparameterControls() {
@@ -279,9 +317,9 @@ class ControlPanel {
downloadEl.click();
});
$('#load-controls').click(() => {
- $('#upload-el').click();
+ $('#upload-hyperparams').click();
});
- $('#upload-el').change((e)=>{
+ $('#upload-hyperparams').change((e)=>{
let files = e.target.files;
if (!files.length) {return;};
let reader = new FileReader();
@@ -290,7 +328,7 @@ class ControlPanel {
Hyperparams.loadJsonObj(result);
this.updateHyperparamUIValues();
// have to clear the value so change() will be triggered if the same file is uploaded again
- $('#upload-el')[0].value = '';
+ $('#upload-hyperparams')[0].value = '';
};
reader.readAsText(files[0]);
});
@@ -428,7 +466,6 @@ class ControlPanel {
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;
- // console.log(this.env_controller.add_new_species)
}
}
diff --git a/src/Controllers/EditorController.js b/src/Controllers/EditorController.js
index d5c2cb7..38cec42 100644
--- a/src/Controllers/EditorController.js
+++ b/src/Controllers/EditorController.js
@@ -13,6 +13,7 @@ class EditorController extends CanvasController{
this.new_species = false;
this.defineCellTypeSelection();
this.defineEditorDetails();
+ this.defineSaveLoad();
}
mouseMove() {
@@ -109,6 +110,43 @@ class EditorController extends CanvasController{
}.bind(this));
}
+ defineSaveLoad() {
+ $('#save-org').click(()=>{
+ let org = this.env.organism.serialize();
+ let data = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(org));
+ let downloadEl = document.getElementById('download-el');
+ downloadEl.setAttribute("href", data);
+ downloadEl.setAttribute("download", "organism.json");
+ downloadEl.click();
+ });
+ $('#load-org').click(() => {
+ $('#upload-org').click();
+ });
+ $('#upload-org').change((e)=>{
+ let files = e.target.files;
+ if (!files.length) {return;};
+ let reader = new FileReader();
+ reader.onload = (e) => {
+ try {
+ let org=JSON.parse(e.target.result);
+ this.env.clear();
+ this.env.organism.loadRaw(org);
+ this.refreshDetailsPanel();
+ this.env.organism.updateGrid();
+ this.env.renderFull();
+ 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
+ $('#upload-org')[0].value = '';
+ } catch(except) {
+ console.error(except)
+ alert('Failed to load organism');
+ }
+ };
+ reader.readAsText(files[0]);
+ });
+ }
+
clearDetailsPanel() {
$('#organism-details').css('display', 'none');
$('#edit-organism-details').css('display', 'none');
diff --git a/src/Environments/WorldEnvironment.js b/src/Environments/WorldEnvironment.js
index 6f47b27..5e89fef 100644
--- a/src/Environments/WorldEnvironment.js
+++ b/src/Environments/WorldEnvironment.js
@@ -7,6 +7,8 @@ const EnvironmentController = require('../Controllers/EnvironmentController');
const Hyperparams = require('../Hyperparameters.js');
const FossilRecord = require('../Stats/FossilRecord');
const WorldConfig = require('../WorldConfig');
+const SerializeHelper = require('../Utils/SerializeHelper');
+const Species = require('../Stats/Species');
class WorldEnvironment extends Environment{
constructor(cell_size) {
@@ -164,6 +166,57 @@ class WorldEnvironment extends Environment{
this.num_rows = Math.ceil(this.renderer.height / cell_size);
this.grid_map.resize(this.num_cols, this.num_rows, cell_size);
}
+
+ serialize() {
+ let env = SerializeHelper.copyNonObjects(this);
+ env.grid = this.grid_map.serialize();
+ env.organisms = [];
+ for (let org of this.organisms){
+ env.organisms.push(org.serialize());
+ }
+ env.fossil_record = FossilRecord.serialize();
+ env.controls = Hyperparams;
+ return env;
+ }
+
+ loadRaw(env) { // species name->stats map, evolution controls,
+ this.organisms = [];
+ FossilRecord.clear_record();
+ this.resizeGridColRow(this.grid_map.cell_size, env.grid.cols, env.grid.rows)
+ this.grid_map.loadRaw(env.grid);
+
+ // create species map
+ let species = {};
+ for (let name in env.fossil_record.species) {
+ let s = new Species(null, null, 0);
+ SerializeHelper.overwriteNonObjects(env.fossil_record.species[name], s)
+ species[name] = s; // the species needs an anatomy obj still
+ }
+
+ for (let orgRaw of env.organisms) {
+ let org = new Organism(orgRaw.col, orgRaw.row, this);
+ 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
+ s = new Species(org.anatomy, null, env.total_ticks);
+ species[orgRaw.species_name] = s;
+ }
+ if (!s.anatomy) {
+ //if the species doesn't have anatomy we need to initialize it
+ s.anatomy = org.anatomy;
+ s.calcAnatomyDetails();
+ }
+ org.species = s;
+ }
+ for (let name in species)
+ FossilRecord.addSpeciesObj(species[name]);
+ FossilRecord.loadRaw(env.fossil_record);
+ SerializeHelper.overwriteNonObjects(env, this);
+ if ($('#override-controls').is(':checked'))
+ Hyperparams.loadJsonObj(env.controls)
+ this.renderer.renderFullGrid(this.grid_map.grid);
+ }
}
module.exports = WorldEnvironment;
diff --git a/src/Grid/GridMap.js b/src/Grid/GridMap.js
index 84db4ca..6c69773 100644
--- a/src/Grid/GridMap.js
+++ b/src/Grid/GridMap.js
@@ -78,6 +78,34 @@ class GridMap {
r = 0;
return [c, r];
}
+
+ serialize() {
+ // Rather than store every single cell, we will store non organism cells (food+walls)
+ // and assume everything else is empty. Organism cells will be set when the organism
+ // list is loaded. This reduces filesize and complexity.
+ let grid = {cols:this.cols, rows:this.rows};
+ grid.food = [];
+ grid.walls = [];
+ for (let col of this.grid) {
+ for (let cell of col) {
+ if (cell.state===CellStates.wall || cell.state===CellStates.food){
+ let c = {c: cell.col, r: cell.row}; // no need to store state
+ if (cell.state===CellStates.food)
+ grid.food.push(c)
+ else
+ grid.walls.push(c)
+ }
+ }
+ }
+ return grid;
+ }
+
+ loadRaw(grid) {
+ for (let f of grid.food)
+ this.setCellType(f.c, f.r, CellStates.food);
+ for (let w of grid.walls)
+ this.setCellType(w.c, w.r, CellStates.wall);
+ }
}
module.exports = GridMap;
diff --git a/src/Organism/Anatomy.js b/src/Organism/Anatomy.js
index b1c3cb6..ec3173d 100644
--- a/src/Organism/Anatomy.js
+++ b/src/Organism/Anatomy.js
@@ -1,14 +1,19 @@
const CellStates = require("./Cell/CellStates");
const BodyCellFactory = require("./Cell/BodyCells/BodyCellFactory");
+const SerializeHelper = require("../Utils/SerializeHelper");
class Anatomy {
constructor(owner) {
this.owner = owner;
+ this.birth_distance = 4;
+ this.clear();
+ }
+
+ clear() {
this.cells = [];
this.is_producer = false;
this.is_mover = false;
this.has_eyes = false;
- this.birth_distance = 4;
}
canAddCellAt(c, r) {
@@ -61,7 +66,7 @@ class Anatomy {
break;
}
}
- this.checkTypeChange(cell.state);
+ this.checkTypeChange();
return true;
}
@@ -93,9 +98,7 @@ class Anatomy {
}
getNeighborsOfCell(col, row) {
-
var neighbors = [];
-
for (var x = -1; x <= 1; x++) {
for (var y = -1; y <= 1; y++) {
@@ -107,6 +110,37 @@ class Anatomy {
return neighbors;
}
+
+ isEqual(anatomy) { // currently unused helper func. inefficient, avoid usage in prod.
+ if (this.cells.length !== anatomy.cells.length) return false;
+ for (let i in this.cells) {
+ let my_cell = this.cells[i];
+ let their_cell = anatomy.cells[i];
+ if (my_cell.loc_col !== their_cell.loc_col ||
+ my_cell.loc_row !== their_cell.loc_row ||
+ my_cell.state !== their_cell.state)
+ return false;
+ }
+ return true;
+ }
+
+ serialize() {
+ let anatomy = SerializeHelper.copyNonObjects(this);
+ anatomy.cells = [];
+ for (let cell of this.cells) {
+ let newcell = SerializeHelper.copyNonObjects(cell);
+ newcell.state = {name: cell.state.name};
+ anatomy.cells.push(newcell)
+ }
+ return anatomy;
+ }
+
+ loadRaw(anatomy) {
+ this.clear();
+ for (let cell of anatomy.cells){
+ this.addInheritCell(cell);
+ }
+ }
}
module.exports = Anatomy;
\ No newline at end of file
diff --git a/src/Organism/Cell/BodyCells/KillerCell.js b/src/Organism/Cell/BodyCells/KillerCell.js
index ca82218..c7dcd92 100644
--- a/src/Organism/Cell/BodyCells/KillerCell.js
+++ b/src/Organism/Cell/BodyCells/KillerCell.js
@@ -18,7 +18,6 @@ class KillerCell extends BodyCell{
}
killNeighbor(n_cell) {
- // console.log(n_cell)
if(n_cell == null || n_cell.owner == null || n_cell.owner == this.org || !n_cell.owner.living || n_cell.state == CellStates.armor)
return;
var is_hit = n_cell.state == CellStates.killer; // has to be calculated before death
diff --git a/src/Organism/Organism.js b/src/Organism/Organism.js
index 2d00ee6..80f8404 100644
--- a/src/Organism/Organism.js
+++ b/src/Organism/Organism.js
@@ -5,6 +5,7 @@ const Directions = require("./Directions");
const Anatomy = require("./Anatomy");
const Brain = require("./Perception/Brain");
const FossilRecord = require("../Stats/FossilRecord");
+const SerializeHelper = require("../Utils/SerializeHelper");
class Organism {
constructor(col, row, env, parent=null) {
@@ -33,15 +34,12 @@ class Organism {
this.move_range = parent.move_range;
this.mutability = parent.mutability;
this.species = parent.species;
- // this.birth_distance = parent.birth_distance;
for (var c of parent.anatomy.cells){
//deep copy parent cells
this.anatomy.addInheritCell(c);
}
- if(parent.anatomy.is_mover) {
- for (var i in parent.brain.decisions) {
- this.brain.decisions[i] = parent.brain.decisions[i];
- }
+ if(parent.anatomy.is_mover && parent.anatomy.has_eyes) {
+ this.brain.copy(parent.brain);
}
}
@@ -104,7 +102,6 @@ class Organism {
var new_c = this.c + (direction_c*basemovement) + (direction_c*offset);
var new_r = this.r + (direction_r*basemovement) + (direction_r*offset);
- // console.log(org.isClear(new_c, new_r, org.rotation, true))
if (org.isClear(new_c, new_r, org.rotation, true) && org.isStraightPath(new_c, new_r, this.c, this.r, this)){
org.c = new_c;
org.r = new_r;
@@ -118,11 +115,12 @@ class Organism {
}
}
Math.max(this.food_collected -= this.foodNeeded(), 0);
-
}
mutate() {
- let mutated = false;
+ let added = false;
+ let changed = false;
+ let removed = false;
if (this.calcRandomChance(Hyperparams.addProb)) {
let branch = this.anatomy.getRandomCell();
let state = CellStates.getRandomLivingType();//branch.state;
@@ -130,7 +128,7 @@ class Organism {
let c = branch.loc_col+growth_direction[0];
let r = branch.loc_row+growth_direction[1];
if (this.anatomy.canAddCellAt(c, r)){
- mutated = true;
+ added = true;
this.anatomy.addRandomizedCell(state, c, r);
}
}
@@ -138,15 +136,15 @@ class Organism {
let cell = this.anatomy.getRandomCell();
let state = CellStates.getRandomLivingType();
this.anatomy.replaceCell(state, cell.loc_col, cell.loc_row);
- mutated = true;
+ changed = true;
}
if (this.calcRandomChance(Hyperparams.removeProb)){
if(this.anatomy.cells.length > 1) {
let cell = this.anatomy.getRandomCell();
- mutated = this.anatomy.removeCell(cell.loc_col, cell.loc_row);
+ removed = this.anatomy.removeCell(cell.loc_col, cell.loc_row);
}
}
- return mutated;
+ return added || changed || removed;
}
calcRandomChance(prob) {
@@ -319,6 +317,22 @@ class Organism {
return this.env.grid_map.cellAt(real_c, real_r);
}
+ serialize() {
+ let org = SerializeHelper.copyNonObjects(this);
+ org.anatomy = this.anatomy.serialize();
+ if (this.anatomy.is_mover && this.anatomy.has_eyes)
+ org.brain = this.brain.serialize();
+ org.species_name = this.species.name;
+ return org;
+ }
+
+ loadRaw(org) {
+ SerializeHelper.overwriteNonObjects(org, this);
+ this.anatomy.loadRaw(org.anatomy)
+ if (org.brain)
+ this.brain.copy(org.brain)
+ }
+
}
module.exports = Organism;
diff --git a/src/Organism/Perception/Brain.js b/src/Organism/Perception/Brain.js
index 20c6258..4c60b03 100644
--- a/src/Organism/Perception/Brain.js
+++ b/src/Organism/Perception/Brain.js
@@ -20,16 +20,18 @@ class Brain {
this.observations = [];
// corresponds to CellTypes
- this.decisions = [];
- this.decisions[CellStates.empty.name] = Decision.neutral;
+ this.decisions = {};
+ for (let cell of CellStates.all) {
+ this.decisions[cell.name] = Decision.neutral;
+ }
this.decisions[CellStates.food.name] = Decision.chase;
- this.decisions[CellStates.wall.name] = Decision.neutral;
- this.decisions[CellStates.mouth.name] = Decision.neutral;
- this.decisions[CellStates.producer.name] = Decision.neutral;
- this.decisions[CellStates.mover.name] = Decision.neutral;
this.decisions[CellStates.killer.name] = Decision.retreat;
- this.decisions[CellStates.armor.name] = Decision.neutral;
- this.decisions[CellStates.eye.name] = Decision.neutral;
+ }
+
+ copy(brain) {
+ for (let dec in brain.decisions) {
+ this.decisions[dec] = brain.decisions[dec];
+ }
}
randomizeDecisions(randomize_all=false) {
@@ -58,9 +60,7 @@ class Brain {
continue;
}
if (obs.distance < closest) {
- // console.log(obs.cell.state)
decision = this.decisions[obs.cell.state.name];
- // console.log(decision)
move_direction = obs.direction;
closest = obs.distance;
}
@@ -81,6 +81,10 @@ class Brain {
this.decisions[CellStates.getRandomName()] = Decision.getRandom();
this.decisions[CellStates.empty.name] = Decision.neutral; // if the empty cell has a decision it gets weird
}
+
+ serialize() {
+ return {decisions: this.decisions};
+ }
}
Brain.Decision = Decision;
diff --git a/src/Stats/FossilRecord.js b/src/Stats/FossilRecord.js
index 3189f7a..17ec347 100644
--- a/src/Stats/FossilRecord.js
+++ b/src/Stats/FossilRecord.js
@@ -1,4 +1,5 @@
const CellStates = require("../Organism/Cell/CellStates");
+const SerializeHelper = require("../Utils/SerializeHelper");
const Species = require("./Species");
const FossilRecord = {
@@ -18,7 +19,6 @@ const FossilRecord = {
},
addSpecies: function(org, ancestor) {
- // console.log("Adding Species")
var new_species = new Species(org.anatomy, ancestor, this.env.total_ticks);
this.extant_species.push(new_species);
org.species = new_species;
@@ -26,33 +26,30 @@ const FossilRecord = {
},
addSpeciesObj: function(species) {
- // console.log("Adding Species")
this.extant_species.push(species);
return species;
},
fossilize: function(species) {
- // console.log("Extinction")
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);
-
- // console.log("Extant species:", this.extant_species.length)
- // console.log("Extinct species:", this.extinct_species.length)
return true;
}
}
},
resurrect: function(species) {
- // console.log("Resurrecting species")
if (species.extinct) {
for (i in this.extinct_species) {
var s = this.extinct_species[i];
@@ -67,12 +64,13 @@ const FossilRecord = {
setData() {
// all parallel arrays
- this.tick_record = [0];
- this.pop_counts = [0];
- this.species_counts = [0];
- this.av_mut_rates = [0];
- this.av_cells = [0];
- this.av_cell_counts = [this.calcCellCountAverages()];
+ this.tick_record = [];
+ this.pop_counts = [];
+ this.species_counts = [];
+ this.av_mut_rates = [];
+ this.av_cells = [];
+ this.av_cell_counts = [];
+ this.updateData();
},
updateData() {
@@ -121,12 +119,39 @@ const FossilRecord = {
this.av_cell_counts.push(cell_counts);
},
- clear_record: function() {
+ clear_record() {
this.extant_species = [];
this.extinct_species = [];
this.setData();
},
+ serialize() {
+ this.updateData();
+ let record = SerializeHelper.copyNonObjects(this);
+ record.records = {
+ tick_record:this.tick_record,
+ pop_counts:this.pop_counts,
+ species_counts:this.species_counts,
+ av_mut_rates:this.av_mut_rates,
+ av_cells:this.av_cells,
+ av_cell_counts:this.av_cell_counts,
+ };
+ let species = {};
+ for (let s of 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
+ }
+ record.species = species;
+ return record;
+ },
+
+ loadRaw(record) {
+ SerializeHelper.overwriteNonObjects(record, this);
+ for (let key in record.records) {
+ this[key] = record.records[key];
+ }
+ }
+
}
FossilRecord.init();
diff --git a/src/Stats/Species.js b/src/Stats/Species.js
index f251844..6f5b5f9 100644
--- a/src/Stats/Species.js
+++ b/src/Stats/Species.js
@@ -1,19 +1,26 @@
const CellStates = require("../Organism/Cell/CellStates");
+let FossilRecord = undefined; // workaround to a circular dependency problem
+const getFossilRecord = () => {
+ if (!FossilRecord)
+ FossilRecord = require("./FossilRecord");
+ return FossilRecord;
+}
class Species {
constructor(anatomy, ancestor, start_tick) {
this.anatomy = anatomy;
- // this.ancestor = ancestor; // garbage collect ancestors to avoid memory problems
+ this.ancestor = ancestor; // eventually need to garbage collect ancestors to avoid memory problems
this.population = 1;
this.cumulative_pop = 1;
this.start_tick = start_tick;
this.end_tick = -1;
- this.name = '_' + Math.random().toString(36).substr(2, 9);
+ this.name = Math.random().toString(36).substr(2, 10);
this.extinct = false;
this.calcAnatomyDetails();
}
calcAnatomyDetails() {
+ if (!this.anatomy) return;
var cell_counts = {};
for (let c of CellStates.living) {
cell_counts[c.name] = 0;
@@ -33,8 +40,7 @@ class Species {
this.population--;
if (this.population <= 0) {
this.extinct = true;
- const FossilRecord = require("./FossilRecord");
- FossilRecord.fossilize(this);
+ getFossilRecord().fossilize(this);
}
}
diff --git a/src/Utils/SerializeHelper.js b/src/Utils/SerializeHelper.js
new file mode 100644
index 0000000..62930a9
--- /dev/null
+++ b/src/Utils/SerializeHelper.js
@@ -0,0 +1,20 @@
+const SerializeHelper = {
+ copyNonObjects(obj) {
+ let newobj = {};
+ for (let key in obj) {
+ if (typeof obj[key] !== 'object')
+ newobj[key] = obj[key];
+ }
+ return newobj;
+ },
+ overwriteNonObjects(copyFrom, copyTo) {
+ for (let key in copyFrom) {
+ if (typeof copyFrom[key] !== 'object' && typeof copyTo[key] !== 'object') {
+ // only overwrite if neither are objects
+ copyTo[key] = copyFrom[key];
+ }
+ }
+ }
+}
+
+module.exports = SerializeHelper;
\ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
index 62523fd..c0243c9 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,4 +1,5 @@
const path = require('path');
+const webpack = require("webpack");
module.exports = {
entry: './src/index.js',
@@ -6,7 +7,9 @@ module.exports = {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist/js/'),
},
- externals: {
- jquery: 'jQuery'
- }
+ plugins: [
+ new webpack.ProvidePlugin({
+ $: "jquery",
+ })
+ ]
};
\ No newline at end of file