added world saving/loading

This commit is contained in:
MaxRobinsonTheGreat
2022-04-15 13:13:19 -05:00
parent dabeb4463d
commit 8df3accff7
9 changed files with 184 additions and 25 deletions

View File

@@ -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() {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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)
}
}
}

View File

@@ -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();

View File

@@ -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;

View File

@@ -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];
}
}
}
}