From e0d7f02454e8d4784b843aa40684fa1965247470 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 13 Feb 2022 16:52:55 +0000 Subject: [PATCH 1/8] Bump ajv from 6.12.2 to 6.12.6 Bumps [ajv](https://github.com/ajv-validator/ajv) from 6.12.2 to 6.12.6. - [Release notes](https://github.com/ajv-validator/ajv/releases) - [Commits](https://github.com/ajv-validator/ajv/compare/v6.12.2...v6.12.6) --- updated-dependencies: - dependency-name: ajv dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index d276bd2..13b82a4 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", From 0da27abd37306409f6b53d6d8394ed252c9a2df7 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Sat, 19 Mar 2022 14:25:24 -0500 Subject: [PATCH 2/8] fixed species tracking bug --- src/Organism/Anatomy.js | 17 ++++++++++++++--- src/Organism/Organism.js | 14 ++++++++------ src/Stats/FossilRecord.js | 5 +++-- src/Stats/Species.js | 11 ++++++++--- 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/Organism/Anatomy.js b/src/Organism/Anatomy.js index b1c3cb6..1e8cebb 100644 --- a/src/Organism/Anatomy.js +++ b/src/Organism/Anatomy.js @@ -61,7 +61,7 @@ class Anatomy { break; } } - this.checkTypeChange(cell.state); + this.checkTypeChange(); return true; } @@ -93,9 +93,7 @@ class Anatomy { } getNeighborsOfCell(col, row) { - var neighbors = []; - for (var x = -1; x <= 1; x++) { for (var y = -1; y <= 1; y++) { @@ -107,6 +105,19 @@ 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; + } } module.exports = Anatomy; \ No newline at end of file diff --git a/src/Organism/Organism.js b/src/Organism/Organism.js index 2d00ee6..dcaabaa 100644 --- a/src/Organism/Organism.js +++ b/src/Organism/Organism.js @@ -118,19 +118,21 @@ 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)) { + // console.log('add') let branch = this.anatomy.getRandomCell(); let state = CellStates.getRandomLivingType();//branch.state; let growth_direction = Neighbors.all[Math.floor(Math.random() * Neighbors.all.length)] 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 +140,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) { diff --git a/src/Stats/FossilRecord.js b/src/Stats/FossilRecord.js index 3189f7a..e9007fa 100644 --- a/src/Stats/FossilRecord.js +++ b/src/Stats/FossilRecord.js @@ -18,7 +18,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,7 +25,6 @@ const FossilRecord = { }, addSpeciesObj: function(species) { - // console.log("Adding Species") this.extant_species.push(species); return species; }, @@ -38,6 +36,9 @@ const FossilRecord = { 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; } diff --git a/src/Stats/Species.js b/src/Stats/Species.js index f251844..050e70b 100644 --- a/src/Stats/Species.js +++ b/src/Stats/Species.js @@ -1,9 +1,15 @@ 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; @@ -33,8 +39,7 @@ class Species { this.population--; if (this.population <= 0) { this.extinct = true; - const FossilRecord = require("./FossilRecord"); - FossilRecord.fossilize(this); + getFossilRecord().fossilize(this); } } From 8005b5312c633ce282d204607bca0552b394ee05 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Sat, 19 Mar 2022 14:36:10 -0500 Subject: [PATCH 3/8] made jquery a formal plugin --- dist/index.html | 1 - webpack.config.js | 9 ++++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/dist/index.html b/dist/index.html index d2fe274..9022567 100644 --- a/dist/index.html +++ b/dist/index.html @@ -6,7 +6,6 @@ - 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 From 12d0f4dab58fae4f33812a8ffdf1baa3095817bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 27 Mar 2022 00:51:44 +0000 Subject: [PATCH 4/8] Bump minimist from 1.2.5 to 1.2.6 Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6. - [Release notes](https://github.com/substack/minimist/releases) - [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6) --- updated-dependencies: - dependency-name: minimist dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index d276bd2..00906d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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": { From dabeb4463dd42ad208efe797ad20b15475715deb Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Sun, 10 Apr 2022 13:07:10 -0500 Subject: [PATCH 5/8] organism saving/loading --- dist/index.html | 5 +++- src/Controllers/ControlPanel.js | 8 +++--- src/Controllers/EditorController.js | 38 +++++++++++++++++++++++++++++ src/Organism/Anatomy.js | 25 ++++++++++++++++++- src/Organism/Organism.js | 29 +++++++++++++++++----- src/Organism/Perception/Brain.js | 24 ++++++++++-------- src/Utils/SerializeHelper.js | 12 +++++++++ 7 files changed, 118 insertions(+), 23 deletions(-) create mode 100644 src/Utils/SerializeHelper.js diff --git a/dist/index.html b/dist/index.html index 9022567..70f160b 100644 --- a/dist/index.html +++ b/dist/index.html @@ -77,6 +77,9 @@ + + +
@@ -237,7 +240,7 @@ - +
diff --git a/src/Controllers/ControlPanel.js b/src/Controllers/ControlPanel.js index f57b94b..11e81eb 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 { @@ -279,9 +278,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 +289,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 +427,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/Organism/Anatomy.js b/src/Organism/Anatomy.js index 1e8cebb..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) { @@ -118,6 +123,24 @@ class Anatomy { } 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/Organism.js b/src/Organism/Organism.js index dcaabaa..0461f27 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) { @@ -38,10 +39,8 @@ class Organism { //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 +103,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; @@ -125,7 +123,6 @@ class Organism { let changed = false; let removed = false; if (this.calcRandomChance(Hyperparams.addProb)) { - // console.log('add') let branch = this.anatomy.getRandomCell(); let state = CellStates.getRandomLivingType();//branch.state; let growth_direction = Neighbors.all[Math.floor(Math.random() * Neighbors.all.length)] @@ -321,6 +318,26 @@ 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.brain) + org.brain = this.brain.serialize(); + return org; + } + + loadRaw(org) { + for (let key in org) + if (typeof org[key] !== 'object') + this[key] = org[key]; + this.anatomy.loadRaw(org.anatomy) + console.log(org) + if (org.brain) { + console.log('load 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/Utils/SerializeHelper.js b/src/Utils/SerializeHelper.js new file mode 100644 index 0000000..c96c8c2 --- /dev/null +++ b/src/Utils/SerializeHelper.js @@ -0,0 +1,12 @@ +const SerializeHelper = { + copyNonObjects(obj) { + let newobj = {}; + for (let key in obj) { + if (typeof obj[key] !== 'object') + newobj[key] = obj[key]; + } + return newobj; + } +} + +module.exports = SerializeHelper; \ No newline at end of file From 8df3accff7660e149871355dd2209ada73a4d559 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Fri, 15 Apr 2022 13:13:19 -0500 Subject: [PATCH 6/8] added world saving/loading --- dist/index.html | 13 +++++- src/Controllers/ControlPanel.js | 39 ++++++++++++++++ src/Environments/WorldEnvironment.js | 56 +++++++++++++++++++++++ src/Grid/GridMap.js | 28 ++++++++++++ src/Organism/Cell/BodyCells/KillerCell.js | 1 - src/Organism/Organism.js | 13 ++---- src/Stats/FossilRecord.js | 48 ++++++++++++++----- src/Stats/Species.js | 3 +- src/Utils/SerializeHelper.js | 8 ++++ 9 files changed, 184 insertions(+), 25 deletions(-) diff --git a/dist/index.html b/dist/index.html index 70f160b..8e89ab6 100644 --- a/dist/index.html +++ b/dist/index.html @@ -20,7 +20,7 @@
- +
@@ -189,6 +189,15 @@
+
+ + + .json +
+ + + +
@@ -270,7 +279,7 @@
- + diff --git a/src/Controllers/ControlPanel.js b/src/Controllers/ControlPanel.js index 11e81eb..46cda00 100644 --- a/src/Controllers/ControlPanel.js +++ b/src/Controllers/ControlPanel.js @@ -47,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': @@ -205,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() { diff --git a/src/Environments/WorldEnvironment.js b/src/Environments/WorldEnvironment.js index 6f47b27..8ad7276 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,60 @@ 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); + if (!orgRaw.anatomy) console.log(orgRaw) + org.loadRaw(orgRaw); + this.addOrganism(org); + let s = species[orgRaw.species_name]; + if (!s){ + console.log("Created:", orgRaw.species_name) + console.log(org.anatomy) + 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/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 0461f27..80f8404 100644 --- a/src/Organism/Organism.js +++ b/src/Organism/Organism.js @@ -34,7 +34,6 @@ 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); @@ -321,21 +320,17 @@ class Organism { serialize() { let org = SerializeHelper.copyNonObjects(this); org.anatomy = this.anatomy.serialize(); - if (this.brain) + if (this.anatomy.is_mover && this.anatomy.has_eyes) org.brain = this.brain.serialize(); + org.species_name = this.species.name; return org; } loadRaw(org) { - for (let key in org) - if (typeof org[key] !== 'object') - this[key] = org[key]; + SerializeHelper.overwriteNonObjects(org, this); this.anatomy.loadRaw(org.anatomy) - console.log(org) - if (org.brain) { - console.log('load brain') + if (org.brain) this.brain.copy(org.brain) - } } } diff --git a/src/Stats/FossilRecord.js b/src/Stats/FossilRecord.js index e9007fa..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 = { @@ -30,7 +31,6 @@ const FossilRecord = { }, 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]; @@ -44,16 +44,12 @@ const FossilRecord = { } // 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]; @@ -68,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() { @@ -122,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 050e70b..6f5b5f9 100644 --- a/src/Stats/Species.js +++ b/src/Stats/Species.js @@ -14,12 +14,13 @@ class Species { 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; diff --git a/src/Utils/SerializeHelper.js b/src/Utils/SerializeHelper.js index c96c8c2..62930a9 100644 --- a/src/Utils/SerializeHelper.js +++ b/src/Utils/SerializeHelper.js @@ -6,6 +6,14 @@ const SerializeHelper = { 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]; + } + } } } From 8b398ff99f262ed52af54726e78d6600274a09fb Mon Sep 17 00:00:00 2001 From: Max Robinson Date: Fri, 15 Apr 2022 14:11:30 -0500 Subject: [PATCH 7/8] Update Changelog.md --- Changelog.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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) From a85491ce39cacbe11e27e3d378501592bddbdd02 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Fri, 15 Apr 2022 14:14:00 -0500 Subject: [PATCH 8/8] removed log message --- src/Environments/WorldEnvironment.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Environments/WorldEnvironment.js b/src/Environments/WorldEnvironment.js index 8ad7276..5e89fef 100644 --- a/src/Environments/WorldEnvironment.js +++ b/src/Environments/WorldEnvironment.js @@ -195,13 +195,10 @@ class WorldEnvironment extends Environment{ for (let orgRaw of env.organisms) { let org = new Organism(orgRaw.col, orgRaw.row, this); - if (!orgRaw.anatomy) console.log(orgRaw) org.loadRaw(orgRaw); this.addOrganism(org); let s = species[orgRaw.species_name]; - if (!s){ - console.log("Created:", orgRaw.species_name) - console.log(org.anatomy) + 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; }