From 949d46e7c412da524c233033c61d38c1e1dccb29 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Thu, 9 Jul 2020 20:32:25 -0600 Subject: [PATCH] Added various control panel options --- dist/bundle.js | 34 ++++++++++++++++++++----- dist/css/style.css | 18 +++++++++++-- dist/html/index.html | 15 +++++++++++ src/Cell.js | 58 +++++++++++++++--------------------------- src/ControlPanel.js | 40 +++++++++++++++++++++++++++++ src/Engine.js | 28 +++++++++++++++----- src/Environment.js | 1 - src/Hyperparameters.js | 24 +++++++++++++++++ src/Neighbors.js | 21 +++++++++++++++ src/Organism.js | 15 +++++++---- 10 files changed, 196 insertions(+), 58 deletions(-) create mode 100644 src/Hyperparameters.js create mode 100644 src/Neighbors.js diff --git a/dist/bundle.js b/dist/bundle.js index ce1fe87..0ad7f2b 100644 --- a/dist/bundle.js +++ b/dist/bundle.js @@ -93,7 +93,7 @@ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { -eval("const CellTypes = __webpack_require__(/*! ./CellTypes */ \"./src/CellTypes.js\");\r\n\r\n// A cell exists in a grid system.\r\nclass Cell{\r\n constructor(type, col, row, x, y){\r\n this.owner = null;\r\n this.setType(type);\r\n this.col = col;\r\n this.row = row;\r\n this.x = x;\r\n this.y = y;\r\n }\r\n\r\n setType(type) {\r\n this.type = type;\r\n }\r\n\r\n performFunction(env) {\r\n switch(this.type){\r\n case CellTypes.mouth:\r\n eatFood(this, env);\r\n break;\r\n case CellTypes.producer:\r\n growFood(this, env);\r\n break;\r\n case CellTypes.killer:\r\n killNeighbors(this, env);\r\n break;\r\n }\r\n }\r\n\r\n getColor() {\r\n return CellTypes.colors[this.type];\r\n }\r\n\r\n isLiving() {\r\n return this.type != CellTypes.empty && \r\n this.type != CellTypes.food && \r\n this.type != CellTypes.wall;\r\n }\r\n}\r\n\r\nfunction eatFood(self, env){\r\n eatNeighborFood(env.grid_map.cellAt(self.col+1, self.row), self, env);\r\n eatNeighborFood(env.grid_map.cellAt(self.col-1, self.row), self, env);\r\n eatNeighborFood(env.grid_map.cellAt(self.col, self.row+1), self, env);\r\n eatNeighborFood(env.grid_map.cellAt(self.col, self.row-1), self, env);\r\n}\r\n\r\nfunction eatNeighborFood(n_cell, self, env){\r\n if (n_cell == null)\r\n return;\r\n if (n_cell.type == CellTypes.food){\r\n env.changeCell(n_cell.col, n_cell.row, CellTypes.empty, null);\r\n self.owner.food_collected++;\r\n }\r\n}\r\n\r\nfunction growFood(self, env){\r\n if (self.owner.is_mover)\r\n return;\r\n for (var c=-1; c<=1; c++){\r\n for (var r=-1; r<=1; r++){\r\n if (r==0 && c==0)\r\n continue;\r\n var cell = env.grid_map.cellAt(self.col+c, self.row+r);\r\n if (cell != null && cell.type == CellTypes.empty && Math.random() * 100 <= 1){\r\n env.changeCell(self.col+c, self.row+r, CellTypes.food, null);\r\n return;\r\n }\r\n }\r\n }\r\n}\r\n\r\nfunction killNeighbors(self, env) {\r\n killNeighbor(self, env.grid_map.cellAt(self.col+1, self.row));\r\n killNeighbor(self, env.grid_map.cellAt(self.col-1, self.row));\r\n killNeighbor(self, env.grid_map.cellAt(self.col, self.row+1));\r\n killNeighbor(self, env.grid_map.cellAt(self.col, self.row-1));\r\n}\r\n\r\nfunction killNeighbor(self, n_cell) {\r\n if(n_cell == null) {\r\n // console.log(\"null cell\")\r\n return;\r\n }\r\n if(n_cell.owner == null) {\r\n // console.log(\"is no one's cell\")\r\n return;\r\n }\r\n if(n_cell.owner == self.owner) {\r\n // console.log(\"is my cell\")\r\n return;\r\n }\r\n if (!n_cell.owner.living) {\r\n // console.log(\"cell is dead\")\r\n return;\r\n }\r\n if (n_cell.type == CellTypes.armor) {\r\n // console.log(\"armor block\")\r\n // self.owner.die();\r\n return\r\n }\r\n var should_die = n_cell.type == CellTypes.killer; // has to be calculated before death\r\n n_cell.owner.die();\r\n if (should_die){\r\n self.owner.die();\r\n }\r\n}\r\n\r\nmodule.exports = Cell;\r\n\n\n//# sourceURL=webpack:///./src/Cell.js?"); +eval("const CellTypes = __webpack_require__(/*! ./CellTypes */ \"./src/CellTypes.js\");\r\nvar Hyperparams = __webpack_require__(/*! ./Hyperparameters */ \"./src/Hyperparameters.js\");\r\n\r\n// A cell exists in a grid system.\r\nclass Cell{\r\n constructor(type, col, row, x, y){\r\n this.owner = null;\r\n this.setType(type);\r\n this.col = col;\r\n this.row = row;\r\n this.x = x;\r\n this.y = y;\r\n }\r\n\r\n setType(type) {\r\n this.type = type;\r\n }\r\n\r\n performFunction(env) {\r\n switch(this.type){\r\n case CellTypes.mouth:\r\n eatFood(this, env);\r\n break;\r\n case CellTypes.producer:\r\n growFood(this, env);\r\n break;\r\n case CellTypes.killer:\r\n killNeighbors(this, env);\r\n break;\r\n }\r\n }\r\n\r\n getColor() {\r\n return CellTypes.colors[this.type];\r\n }\r\n\r\n isLiving() {\r\n return this.type != CellTypes.empty && \r\n this.type != CellTypes.food && \r\n this.type != CellTypes.wall;\r\n }\r\n}\r\n\r\nfunction eatFood(self, env){\r\n for (var loc of Hyperparams.edibleNeighbors){\r\n var cell = env.grid_map.cellAt(self.col+loc[0], self.row+loc[1]);\r\n eatNeighborFood(self, cell, env);\r\n }\r\n}\r\n\r\nfunction eatNeighborFood(self, n_cell, env){\r\n if (n_cell == null)\r\n return;\r\n if (n_cell.type == CellTypes.food){\r\n env.changeCell(n_cell.col, n_cell.row, CellTypes.empty, null);\r\n self.owner.food_collected++;\r\n }\r\n}\r\n\r\nfunction growFood(self, env){\r\n if (self.owner.is_mover)\r\n return;\r\n var prob = Hyperparams.foodProdProb;\r\n // console.log(prob)\r\n for (var loc of Hyperparams.growableNeighbors){\r\n var c=loc[0];\r\n var r=loc[1];\r\n var cell = env.grid_map.cellAt(self.col+c, self.row+r);\r\n if (cell != null && cell.type == CellTypes.empty && Math.random() * 100 <= prob){\r\n env.changeCell(self.col+c, self.row+r, CellTypes.food, null);\r\n return;\r\n }\r\n }\r\n}\r\n\r\nfunction killNeighbors(self, env) {\r\n for (var loc of Hyperparams.killableNeighbors){\r\n var cell = env.grid_map.cellAt(self.col+loc[0], self.row+loc[1]);\r\n killNeighbor(self, cell);\r\n }\r\n}\r\n\r\nfunction killNeighbor(self, n_cell) {\r\n if(n_cell == null || n_cell.owner == null || n_cell.owner == self.owner || !n_cell.owner.living || n_cell.type == CellTypes.armor) \r\n return;\r\n var should_die = n_cell.type == CellTypes.killer; // has to be calculated before death\r\n n_cell.owner.die();\r\n if (should_die){\r\n self.owner.die();\r\n }\r\n}\r\n\r\nmodule.exports = Cell;\r\n\n\n//# sourceURL=webpack:///./src/Cell.js?"); /***/ }), @@ -113,9 +113,9 @@ eval("const CellTypes = {\r\n empty: 0,\r\n food: 1,\r\n wall: 2,\r\n !*** ./src/ControlPanel.js ***! \*****************************/ /*! no static exports found */ -/***/ (function(module, exports) { +/***/ (function(module, exports, __webpack_require__) { -eval("\r\nclass ControlPanel {\r\n constructor(engine) {\r\n this.engine = engine;\r\n this.defineEngineSpeedControls();\r\n this.fps = engine.fps;\r\n }\r\n\r\n defineEngineSpeedControls(){\r\n this.slider = document.getElementById(\"slider\");\r\n this.slider.oninput = function() {\r\n this.fps = this.slider.value\r\n if (this.engine.running) {\r\n this.changeEngineSpeed(this.fps);\r\n }\r\n }.bind(this);\r\n $('#pause-button').click(function() {\r\n if ($('#pause-button').text() == \"Pause\" && this.engine.running) {\r\n $('#pause-button').text(\"Play\")\r\n this.engine.stop();\r\n }\r\n else if (!this.engine.running){\r\n $('#pause-button').text(\"Pause\")\r\n this.engine.start(this.fps);\r\n }\r\n console.log()\r\n }.bind(this));\r\n }\r\n\r\n changeEngineSpeed(change_val) {\r\n this.engine.stop();\r\n this.engine.start(change_val)\r\n this.fps = this.engine.fps;\r\n }\r\n\r\n}\r\n\r\n\r\nmodule.exports = ControlPanel;\n\n//# sourceURL=webpack:///./src/ControlPanel.js?"); +eval("var Hyperparams = __webpack_require__(/*! ./Hyperparameters */ \"./src/Hyperparameters.js\");\r\n\r\nclass ControlPanel {\r\n constructor(engine) {\r\n this.engine = engine;\r\n this.defineEngineSpeedControls();\r\n this.defineHyperparameterControls();\r\n this.fps = engine.fps;\r\n this.organism_record=0;\r\n }\r\n\r\n defineEngineSpeedControls(){\r\n this.slider = document.getElementById(\"slider\");\r\n this.slider.oninput = function() {\r\n this.fps = this.slider.value\r\n if (this.engine.running) {\r\n this.changeEngineSpeed(this.fps);\r\n \r\n }\r\n $('#fps').text(\"Target FPS: \"+this.fps);\r\n }.bind(this);\r\n $('#pause-button').click(function() {\r\n if ($('#pause-button').text() == \"Pause\" && this.engine.running) {\r\n $('#pause-button').text(\"Play\")\r\n this.engine.stop();\r\n }\r\n else if (!this.engine.running){\r\n $('#pause-button').text(\"Pause\")\r\n this.engine.start(this.fps);\r\n }\r\n console.log()\r\n }.bind(this));\r\n }\r\n\r\n defineHyperparameterControls() {\r\n $('#food-prod-prob').change(function() {\r\n var food_prob = $('#food-prod-prob').val();\r\n if ($('#fixed-ratio').is(\":checked\")) {\r\n Hyperparams.foodProdProb = food_prob;\r\n Hyperparams.calcProducerFoodRatio(false);\r\n $('#lifespan-multiplier').val(Hyperparams.lifespanMultiplier);\r\n }\r\n else{\r\n Hyperparams.foodProdProb = food_prob;\r\n }\r\n }.bind(this));\r\n $('#lifespan-multiplier').change(function() {\r\n var lifespan = $('#lifespan-multiplier').val();\r\n if ($('#fixed-ratio').is(\":checked\")) {\r\n Hyperparams.lifespanMultiplier = lifespan;\r\n Hyperparams.calcProducerFoodRatio(true);\r\n $('#food-prod-prob').val(Hyperparams.foodProdProb);\r\n }\r\n else {\r\n Hyperparams.lifespanMultiplier = lifespan;\r\n }\r\n }.bind(this));\r\n }\r\n\r\n changeEngineSpeed(change_val) {\r\n this.engine.stop();\r\n this.engine.start(change_val)\r\n this.fps = this.engine.fps;\r\n }\r\n\r\n update() {\r\n $('#fps-actual').text(\"Actual FPS: \" + Math.floor(this.engine.actual_fps));\r\n var org_count = this.engine.env.organisms.length;\r\n $('#org-count').text(\"Organism count: \" + org_count);\r\n if (org_count > this.organism_record) \r\n this.organism_record = org_count;\r\n $('#org-record').text(\"Highest count: \" + this.organism_record);\r\n\r\n }\r\n\r\n}\r\n\r\n\r\nmodule.exports = ControlPanel;\n\n//# sourceURL=webpack:///./src/ControlPanel.js?"); /***/ }), @@ -126,7 +126,7 @@ eval("\r\nclass ControlPanel {\r\n constructor(engine) {\r\n this.engi /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { -eval("const Environment = __webpack_require__(/*! ./Environment */ \"./src/Environment.js\");\r\nconst ControlPanel = __webpack_require__(/*! ./ControlPanel */ \"./src/ControlPanel.js\");\r\n\r\nclass Engine{\r\n constructor(){\r\n this.fps = 0;\r\n this.environment = new Environment(5);\r\n this.controlpanel = new ControlPanel(this);\r\n this.environment.OriginOfLife();\r\n this.last_update = Date.now();\r\n this.delta_time = 0;\r\n this.actual_fps = 0;\r\n this.running = false;\r\n }\r\n\r\n start(fps=60) {\r\n if (fps <= 0)\r\n fps = 1;\r\n if (fps > 500)\r\n fps = 500;\r\n this.fps = fps;\r\n this.game_loop = setInterval(function(){this.update();}.bind(this), 1000/fps);\r\n this.running = true;\r\n }\r\n \r\n stop() {\r\n clearInterval(this.game_loop);\r\n this.running = false;\r\n }\r\n\r\n\r\n update() {\r\n this.delta_time = Date.now() - this.last_update;\r\n this.last_update = Date.now();\r\n this.environment.update(this.delta_time);\r\n this.environment.render();\r\n this.actual_fps = 1/this.delta_time*1000;\r\n }\r\n\r\n}\r\n\r\nmodule.exports = Engine;\r\n\n\n//# sourceURL=webpack:///./src/Engine.js?"); +eval("const Environment = __webpack_require__(/*! ./Environment */ \"./src/Environment.js\");\r\nconst ControlPanel = __webpack_require__(/*! ./ControlPanel */ \"./src/ControlPanel.js\");\r\n\r\nclass Engine{\r\n constructor(){\r\n this.fps = 0;\r\n this.env = new Environment(5);\r\n this.controlpanel = new ControlPanel(this);\r\n this.env.OriginOfLife();\r\n this.last_update = Date.now();\r\n this.delta_time = 0;\r\n this.actual_fps = 0;\r\n this.running = false;\r\n }\r\n\r\n start(fps=60) {\r\n if (fps <= 0)\r\n fps = 1;\r\n if (fps > 300)\r\n fps = 300;\r\n this.fps = fps;\r\n this.game_loop = setInterval(function(){this.update();}.bind(this), 1000/fps);\r\n this.running = true;\r\n if (this.fps >= 45) {\r\n if (this.render_loop != null) {\r\n clearInterval(this.render_loop);\r\n this.render_loop = null;\r\n }\r\n }\r\n else\r\n this.setRenderLoop();\r\n }\r\n \r\n stop() {\r\n clearInterval(this.game_loop);\r\n this.running = false;\r\n this.setRenderLoop();\r\n }\r\n\r\n setRenderLoop() {\r\n if (this.render_loop == null) {\r\n this.render_loop = setInterval(function(){this.env.render();}.bind(this), 1000/45);\r\n }\r\n }\r\n\r\n\r\n update() {\r\n this.delta_time = Date.now() - this.last_update;\r\n this.last_update = Date.now();\r\n this.env.update(this.delta_time);\r\n this.env.render();\r\n this.actual_fps = 1/this.delta_time*1000;\r\n this.controlpanel.update();\r\n }\r\n\r\n}\r\n\r\nmodule.exports = Engine;\r\n\n\n//# sourceURL=webpack:///./src/Engine.js?"); /***/ }), @@ -137,7 +137,7 @@ eval("const Environment = __webpack_require__(/*! ./Environment */ \"./src/Envir /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { -eval("const Grid = __webpack_require__(/*! ./GridMap */ \"./src/GridMap.js\");\r\nconst Renderer = __webpack_require__(/*! ./Rendering/Renderer */ \"./src/Rendering/Renderer.js\");\r\nconst GridMap = __webpack_require__(/*! ./GridMap */ \"./src/GridMap.js\");\r\nconst Organism = __webpack_require__(/*! ./Organism */ \"./src/Organism.js\");\r\nconst CellTypes = __webpack_require__(/*! ./CellTypes */ \"./src/CellTypes.js\");\r\nconst Cell = __webpack_require__(/*! ./Cell */ \"./src/Cell.js\");\r\nconst EnvironmentController = __webpack_require__(/*! ./EnvironmentController */ \"./src/EnvironmentController.js\");\r\n\r\nclass Environment{\r\n constructor(cell_size) {\r\n this.renderer = new Renderer('canvas', this, cell_size);\r\n this.controller = new EnvironmentController(this, this.renderer.canvas);\r\n this.grid_rows = Math.floor(this.renderer.height / cell_size);\r\n this.grid_cols = Math.floor(this.renderer.width / cell_size);\r\n this.grid_map = new GridMap(this.grid_cols, this.grid_rows, cell_size);\r\n this.renderer.renderFullGrid();\r\n this.organisms = [];\r\n }\r\n\r\n update(delta_time) {\r\n var to_remove = [];\r\n for (var i in this.organisms) {\r\n var org = this.organisms[i];\r\n if (!org.update()) {\r\n to_remove.push(i);\r\n }\r\n }\r\n this.removeOrganisms(to_remove);\r\n }\r\n\r\n render() {\r\n // console.log(this.cells_to_render);\r\n this.renderer.renderCells();\r\n this.renderer.renderHighlights();\r\n }\r\n\r\n removeOrganisms(org_indeces) {\r\n for (var i of org_indeces.reverse()){\r\n this.organisms.splice(i, 1);\r\n }\r\n }\r\n\r\n OriginOfLife() {\r\n var center = this.grid_map.getCenter();\r\n var org = new Organism(center[0], center[1], this);\r\n org.addCell(CellTypes.mouth, -1, -1);\r\n org.addCell(CellTypes.producer, 0, 0);\r\n org.addCell(CellTypes.mouth, 1, 1);\r\n this.addOrganism(org);\r\n }\r\n\r\n addOrganism(organism) {\r\n organism.updateGrid();\r\n this.organisms.push(organism);\r\n }\r\n\r\n changeCell(c, r, type, owner) {\r\n this.grid_map.setCellType(c, r, type);\r\n this.grid_map.setCellOwner(c, r, owner);\r\n this.renderer.addToRender(this.grid_map.cellAt(c, r));\r\n }\r\n}\r\n\r\nmodule.exports = Environment;\r\n\r\n\n\n//# sourceURL=webpack:///./src/Environment.js?"); +eval("const Grid = __webpack_require__(/*! ./GridMap */ \"./src/GridMap.js\");\r\nconst Renderer = __webpack_require__(/*! ./Rendering/Renderer */ \"./src/Rendering/Renderer.js\");\r\nconst GridMap = __webpack_require__(/*! ./GridMap */ \"./src/GridMap.js\");\r\nconst Organism = __webpack_require__(/*! ./Organism */ \"./src/Organism.js\");\r\nconst CellTypes = __webpack_require__(/*! ./CellTypes */ \"./src/CellTypes.js\");\r\nconst Cell = __webpack_require__(/*! ./Cell */ \"./src/Cell.js\");\r\nconst EnvironmentController = __webpack_require__(/*! ./EnvironmentController */ \"./src/EnvironmentController.js\");\r\n\r\nclass Environment{\r\n constructor(cell_size) {\r\n this.renderer = new Renderer('canvas', this, cell_size);\r\n this.controller = new EnvironmentController(this, this.renderer.canvas);\r\n this.grid_rows = Math.floor(this.renderer.height / cell_size);\r\n this.grid_cols = Math.floor(this.renderer.width / cell_size);\r\n this.grid_map = new GridMap(this.grid_cols, this.grid_rows, cell_size);\r\n this.renderer.renderFullGrid();\r\n this.organisms = [];\r\n }\r\n\r\n update(delta_time) {\r\n var to_remove = [];\r\n for (var i in this.organisms) {\r\n var org = this.organisms[i];\r\n if (!org.update()) {\r\n to_remove.push(i);\r\n }\r\n }\r\n this.removeOrganisms(to_remove);\r\n }\r\n\r\n render() {\r\n this.renderer.renderCells();\r\n this.renderer.renderHighlights();\r\n }\r\n\r\n removeOrganisms(org_indeces) {\r\n for (var i of org_indeces.reverse()){\r\n this.organisms.splice(i, 1);\r\n }\r\n }\r\n\r\n OriginOfLife() {\r\n var center = this.grid_map.getCenter();\r\n var org = new Organism(center[0], center[1], this);\r\n org.addCell(CellTypes.mouth, -1, -1);\r\n org.addCell(CellTypes.producer, 0, 0);\r\n org.addCell(CellTypes.mouth, 1, 1);\r\n this.addOrganism(org);\r\n }\r\n\r\n addOrganism(organism) {\r\n organism.updateGrid();\r\n this.organisms.push(organism);\r\n }\r\n\r\n changeCell(c, r, type, owner) {\r\n this.grid_map.setCellType(c, r, type);\r\n this.grid_map.setCellOwner(c, r, owner);\r\n this.renderer.addToRender(this.grid_map.cellAt(c, r));\r\n }\r\n}\r\n\r\nmodule.exports = Environment;\r\n\r\n\n\n//# sourceURL=webpack:///./src/Environment.js?"); /***/ }), @@ -163,6 +163,17 @@ eval("const Cell = __webpack_require__(/*! ./Cell */ \"./src/Cell.js\");\r\ncons /***/ }), +/***/ "./src/Hyperparameters.js": +/*!********************************!*\ + !*** ./src/Hyperparameters.js ***! + \********************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("const Neighbors = __webpack_require__(/*! ./Neighbors */ \"./src/Neighbors.js\");\r\n\r\nvar Hyperparams = {\r\n lifespanMultiplier: 100,\r\n foodProdProb: 1,\r\n killableNeighbors: Neighbors.adjacent,\r\n edibleNeighbors: Neighbors.adjacent,\r\n growableNeighbors: Neighbors.adjacent,\r\n\r\n\r\n // calculates the optimal ratio where a producer cell is most likely to produce 1 food in its lifespan.\r\n calcProducerFoodRatio : function(lifespan_fixed=true) {\r\n if (lifespan_fixed) {\r\n // change the foodProdProb\r\n this.foodProdProb = 100 / this.lifespanMultiplier;\r\n }\r\n else {\r\n // change the lifespanMultiplier\r\n this.lifespanMultiplier = Math.floor(100 / this.foodProdProb);\r\n }\r\n }\r\n}\r\n\r\nmodule.exports = Hyperparams;\n\n//# sourceURL=webpack:///./src/Hyperparameters.js?"); + +/***/ }), + /***/ "./src/LocalCell.js": /*!**************************!*\ !*** ./src/LocalCell.js ***! @@ -174,6 +185,17 @@ eval("const CellTypes = __webpack_require__(/*! ./CellTypes */ \"./src/CellTypes /***/ }), +/***/ "./src/Neighbors.js": +/*!**************************!*\ + !*** ./src/Neighbors.js ***! + \**************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +eval("// contains local cell values for the following:\r\n\r\n//all ...\r\n// .x.\r\n// ...\r\n\r\n//adjacent .\r\n// .x.\r\n// .\r\n\r\n//corners . .\r\n// x\r\n// . .\r\n\r\nconst Neighbors = {\r\n all: [[0, 1],[0, -1],[1, 0],[-1, 0],[-1, -1],[1, 1],[-1, 1],[1, -1]],\r\n adjacent: [[0, 1],[0, -1],[1, 0],[-1, 0]],\r\n corners: [[-1, -1],[1, 1],[-1, 1],[1, -1]]\r\n}\r\n\r\nmodule.exports = Neighbors;\n\n//# sourceURL=webpack:///./src/Neighbors.js?"); + +/***/ }), + /***/ "./src/Organism.js": /*!*************************!*\ !*** ./src/Organism.js ***! @@ -181,7 +203,7 @@ eval("const CellTypes = __webpack_require__(/*! ./CellTypes */ \"./src/CellTypes /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { -eval("const CellTypes = __webpack_require__(/*! ./CellTypes */ \"./src/CellTypes.js\");\r\nconst Cell = __webpack_require__(/*! ./Cell */ \"./src/Cell.js\");\r\nconst GridMap = __webpack_require__(/*! ./GridMap */ \"./src/GridMap.js\");\r\nconst LocalCell = __webpack_require__(/*! ./LocalCell */ \"./src/LocalCell.js\");\r\nconst { producer } = __webpack_require__(/*! ./CellTypes */ \"./src/CellTypes.js\");\r\n\r\nclass Organism {\r\n constructor(col, row, env, parent=null) {\r\n this.c = col;\r\n this.r = row;\r\n this.env = env;\r\n this.lifetime = 0;\r\n this.food_collected = 0;\r\n this.living = true;\r\n this.cells = [];\r\n this.is_producer = false;\r\n this.is_mover = false;\r\n this.direction = this.getRandomDirection();\r\n this.move_count = 0;\r\n this.move_range = 1;\r\n this.mutability = 5;\r\n if (parent != null) {\r\n this.inherit(parent);\r\n }\r\n }\r\n\r\n addCell(type, c, r) {\r\n for (var cell of this.cells) {\r\n if (cell.loc_col == c && cell.loc_row == r)\r\n return false;\r\n }\r\n this.checkProducerMover(type);\r\n this.cells.push(new LocalCell(type, c, r));\r\n return true;\r\n }\r\n\r\n removeCell(c, r) {\r\n var check_change = false;\r\n for (var i=0; i 1) {\r\n this.cells.splice(Math.floor(Math.random() * this.cells.length), 1);\r\n return true;\r\n }\r\n }\r\n\r\n if (this.is_mover) {\r\n this.move_range += Math.floor(Math.random() * 4) - 2;\r\n }\r\n return false;\r\n }\r\n\r\n attemptMove(direction) {\r\n var direction_c = direction[0];\r\n var direction_r = direction[1];\r\n var new_c = this.c + direction_c;\r\n var new_r = this.r + direction_r;\r\n if (this.isClear(new_c, new_r)) {\r\n for (var cell of this.cells) {\r\n var real_c = this.c + cell.loc_col;\r\n var real_r = this.r + cell.loc_row;\r\n this.env.changeCell(real_c, real_r, CellTypes.empty, null);\r\n }\r\n this.c = new_c;\r\n this.r = new_r;\r\n this.updateGrid();\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n getRandomDirection(){\r\n var directions = [[0,1],[0,-1],[1,0],[-1,0]]\r\n return directions[Math.floor(Math.random() * directions.length)];\r\n }\r\n\r\n // assumes either c1==c2 or r1==r2, returns true if there is a clear path from point a to b\r\n isStraightPath(c1, r1, c2, r2, parent){\r\n // TODO FIX!!!\r\n if (c1 == c2) {\r\n if (r1 > r2){\r\n var temp = r2;\r\n r2 = r1;\r\n r1 = temp;\r\n }\r\n for (var i=r1; i!=r2; i++) {\r\n var cell = this.env.grid_map.cellAt(c1, i)\r\n if (!this.isPassableCell(cell, parent)){\r\n return false;\r\n }\r\n }\r\n return true;\r\n }\r\n else {\r\n if (c1 > c2){\r\n var temp = c2;\r\n c2 = c1;\r\n c1 = temp;\r\n }\r\n for (var i=c1; i!=c2; i++) {\r\n var cell = this.env.grid_map.cellAt(i, r1);\r\n if (!this.isPassableCell(cell, parent)){\r\n return false;\r\n }\r\n }\r\n return true;\r\n }\r\n }\r\n\r\n isPassableCell(cell, parent){\r\n return cell.type == CellTypes.empty || cell.owner == this || cell.owner == parent;\r\n }\r\n\r\n isClear(col, row) {\r\n for(var loccell of this.cells) {\r\n var cell = this.getRealCell(loccell, col, row);\r\n if(cell == null || cell.type != CellTypes.empty && cell.owner != this) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n }\r\n\r\n die() {\r\n for (var cell of this.cells) {\r\n var real_c = this.c + cell.loc_col;\r\n var real_r = this.r + cell.loc_row;\r\n this.env.changeCell(real_c, real_r, CellTypes.food, null);\r\n }\r\n this.living = false;\r\n }\r\n\r\n updateGrid() {\r\n for (var cell of this.cells) {\r\n var real_c = this.c + cell.loc_col;\r\n var real_r = this.r + cell.loc_row;\r\n this.env.changeCell(real_c, real_r, cell.type, this);\r\n }\r\n }\r\n\r\n update() {\r\n // this.food_collected++;\r\n this.lifetime++;\r\n if (this.lifetime > this.lifespan()) {\r\n this.die();\r\n return this.living;\r\n }\r\n if (this.food_collected >= this.foodNeeded()) {\r\n this.reproduce();\r\n }\r\n for (var cell of this.cells) {\r\n this.getRealCell(cell).performFunction(this.env);\r\n }\r\n if (!this.living){\r\n return this.living\r\n }\r\n if (this.is_mover) {\r\n this.move_count++;\r\n var success = this.attemptMove(this.direction);\r\n if (this.move_count > this.move_range || !success){\r\n this.move_count = 0;\r\n this.direction = this.getRandomDirection()\r\n }\r\n }\r\n\r\n return this.living;\r\n }\r\n\r\n getRealCell(local_cell, c=this.c, r=this.r){\r\n var real_c = c + local_cell.loc_col;\r\n var real_r = r + local_cell.loc_row;\r\n return this.env.grid_map.cellAt(real_c, real_r);\r\n }\r\n\r\n}\r\n\r\nmodule.exports = Organism;\r\n\n\n//# sourceURL=webpack:///./src/Organism.js?"); +eval("const CellTypes = __webpack_require__(/*! ./CellTypes */ \"./src/CellTypes.js\");\r\nconst Cell = __webpack_require__(/*! ./Cell */ \"./src/Cell.js\");\r\nconst GridMap = __webpack_require__(/*! ./GridMap */ \"./src/GridMap.js\");\r\nconst LocalCell = __webpack_require__(/*! ./LocalCell */ \"./src/LocalCell.js\");\r\nconst { producer } = __webpack_require__(/*! ./CellTypes */ \"./src/CellTypes.js\");\r\nconst Neighbors = __webpack_require__(/*! ./Neighbors */ \"./src/Neighbors.js\");\r\nvar Hyperparams = __webpack_require__(/*! ./Hyperparameters */ \"./src/Hyperparameters.js\");\r\n\r\nconst directions = [[0,1],[0,-1],[1,0],[-1,0]]\r\n\r\nclass Organism {\r\n constructor(col, row, env, parent=null) {\r\n this.c = col;\r\n this.r = row;\r\n this.env = env;\r\n this.lifetime = 0;\r\n this.food_collected = 0;\r\n this.living = true;\r\n this.cells = [];\r\n this.is_producer = false;\r\n this.is_mover = false;\r\n this.direction = this.getRandomDirection();\r\n this.move_count = 0;\r\n this.move_range = 5;\r\n this.mutability = 5;\r\n if (parent != null) {\r\n this.inherit(parent);\r\n }\r\n }\r\n\r\n addCell(type, c, r) {\r\n for (var cell of this.cells) {\r\n if (cell.loc_col == c && cell.loc_row == r)\r\n return false;\r\n }\r\n this.checkProducerMover(type);\r\n this.cells.push(new LocalCell(type, c, r));\r\n return true;\r\n }\r\n\r\n removeCell(c, r) {\r\n var check_change = false;\r\n for (var i=0; i 1) {\r\n this.cells.splice(Math.floor(Math.random() * this.cells.length), 1);\r\n return true;\r\n }\r\n }\r\n\r\n if (this.is_mover) {\r\n this.move_range += Math.floor(Math.random() * 4) - 2;\r\n }\r\n return false;\r\n }\r\n\r\n attemptMove(direction) {\r\n var direction_c = direction[0];\r\n var direction_r = direction[1];\r\n var new_c = this.c + direction_c;\r\n var new_r = this.r + direction_r;\r\n if (this.isClear(new_c, new_r)) {\r\n for (var cell of this.cells) {\r\n var real_c = this.c + cell.loc_col;\r\n var real_r = this.r + cell.loc_row;\r\n this.env.changeCell(real_c, real_r, CellTypes.empty, null);\r\n }\r\n this.c = new_c;\r\n this.r = new_r;\r\n this.updateGrid();\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n getRandomDirection(){\r\n return directions[Math.floor(Math.random() * directions.length)];\r\n }\r\n\r\n // assumes either c1==c2 or r1==r2, returns true if there is a clear path from point a to b\r\n isStraightPath(c1, r1, c2, r2, parent){\r\n // TODO FIX!!!\r\n if (c1 == c2) {\r\n if (r1 > r2){\r\n var temp = r2;\r\n r2 = r1;\r\n r1 = temp;\r\n }\r\n for (var i=r1; i!=r2; i++) {\r\n var cell = this.env.grid_map.cellAt(c1, i)\r\n if (!this.isPassableCell(cell, parent)){\r\n return false;\r\n }\r\n }\r\n return true;\r\n }\r\n else {\r\n if (c1 > c2){\r\n var temp = c2;\r\n c2 = c1;\r\n c1 = temp;\r\n }\r\n for (var i=c1; i!=c2; i++) {\r\n var cell = this.env.grid_map.cellAt(i, r1);\r\n if (!this.isPassableCell(cell, parent)){\r\n return false;\r\n }\r\n }\r\n return true;\r\n }\r\n }\r\n\r\n isPassableCell(cell, parent){\r\n return cell.type == CellTypes.empty || cell.owner == this || cell.owner == parent;\r\n }\r\n\r\n isClear(col, row) {\r\n for(var loccell of this.cells) {\r\n var cell = this.getRealCell(loccell, col, row);\r\n if(cell == null || cell.type != CellTypes.empty && cell.owner != this) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n }\r\n\r\n die() {\r\n for (var cell of this.cells) {\r\n var real_c = this.c + cell.loc_col;\r\n var real_r = this.r + cell.loc_row;\r\n this.env.changeCell(real_c, real_r, CellTypes.food, null);\r\n }\r\n this.living = false;\r\n }\r\n\r\n updateGrid() {\r\n for (var cell of this.cells) {\r\n var real_c = this.c + cell.loc_col;\r\n var real_r = this.r + cell.loc_row;\r\n this.env.changeCell(real_c, real_r, cell.type, this);\r\n }\r\n }\r\n\r\n update() {\r\n // this.food_collected++;\r\n this.lifetime++;\r\n if (this.lifetime > this.lifespan()) {\r\n this.die();\r\n return this.living;\r\n }\r\n if (this.food_collected >= this.foodNeeded()) {\r\n this.reproduce();\r\n }\r\n for (var cell of this.cells) {\r\n this.getRealCell(cell).performFunction(this.env);\r\n }\r\n if (!this.living){\r\n return this.living\r\n }\r\n if (this.is_mover) {\r\n this.move_count++;\r\n var success = this.attemptMove(this.direction);\r\n if (this.move_count > this.move_range || !success){\r\n this.move_count = 0;\r\n this.direction = this.getRandomDirection()\r\n }\r\n }\r\n\r\n return this.living;\r\n }\r\n\r\n getRealCell(local_cell, c=this.c, r=this.r){\r\n var real_c = c + local_cell.loc_col;\r\n var real_r = r + local_cell.loc_row;\r\n return this.env.grid_map.cellAt(real_c, real_r);\r\n }\r\n\r\n}\r\n\r\nmodule.exports = Organism;\r\n\n\n//# sourceURL=webpack:///./src/Organism.js?"); /***/ }), diff --git a/dist/css/style.css b/dist/css/style.css index ea26f29..19e6844 100644 --- a/dist/css/style.css +++ b/dist/css/style.css @@ -32,18 +32,32 @@ canvas { padding: 5px; border: 10px; border-color: black; - grid-row: 1; - height: 100%; + background-color: lightgray; + /* height: 100%; */ } #speed-controller { grid-column: 1; + grid-row: 1; + /* background-color: blue; */ +} + +#stats { + grid-column: 1; + grid-row: 2; + /* background-color: blue; */ } #hyperparameters { grid-column: 2; + grid-row: 1 / 2; + /* background-color: green; */ + } #powers { grid-column: 3; + grid-row: 1 / 2; + /* background-color: yellow; */ + } \ No newline at end of file diff --git a/dist/html/index.html b/dist/html/index.html index 751fa52..c3f5e7b 100644 --- a/dist/html/index.html +++ b/dist/html/index.html @@ -17,10 +17,25 @@

Simulation Speed

+

Target FPS: 60

+

+ + +
+

Stats

+

Organism count:

+

Highest count:

Hyperparameters

+ + + + +
+ +
diff --git a/src/Cell.js b/src/Cell.js index 2239b16..4a1d7f5 100644 --- a/src/Cell.js +++ b/src/Cell.js @@ -1,4 +1,5 @@ const CellTypes = require("./CellTypes"); +var Hyperparams = require("./Hyperparameters"); // A cell exists in a grid system. class Cell{ @@ -41,13 +42,13 @@ class Cell{ } function eatFood(self, env){ - eatNeighborFood(env.grid_map.cellAt(self.col+1, self.row), self, env); - eatNeighborFood(env.grid_map.cellAt(self.col-1, self.row), self, env); - eatNeighborFood(env.grid_map.cellAt(self.col, self.row+1), self, env); - eatNeighborFood(env.grid_map.cellAt(self.col, self.row-1), self, env); + for (var loc of Hyperparams.edibleNeighbors){ + var cell = env.grid_map.cellAt(self.col+loc[0], self.row+loc[1]); + eatNeighborFood(self, cell, env); + } } -function eatNeighborFood(n_cell, self, env){ +function eatNeighborFood(self, n_cell, env){ if (n_cell == null) return; if (n_cell.type == CellTypes.food){ @@ -59,48 +60,29 @@ function eatNeighborFood(n_cell, self, env){ function growFood(self, env){ if (self.owner.is_mover) return; - for (var c=-1; c<=1; c++){ - for (var r=-1; r<=1; r++){ - if (r==0 && c==0) - continue; - var cell = env.grid_map.cellAt(self.col+c, self.row+r); - if (cell != null && cell.type == CellTypes.empty && Math.random() * 100 <= 1){ - env.changeCell(self.col+c, self.row+r, CellTypes.food, null); - return; - } + var prob = Hyperparams.foodProdProb; + // console.log(prob) + for (var loc of Hyperparams.growableNeighbors){ + var c=loc[0]; + var r=loc[1]; + var cell = env.grid_map.cellAt(self.col+c, self.row+r); + if (cell != null && cell.type == CellTypes.empty && Math.random() * 100 <= prob){ + env.changeCell(self.col+c, self.row+r, CellTypes.food, null); + return; } } } function killNeighbors(self, env) { - killNeighbor(self, env.grid_map.cellAt(self.col+1, self.row)); - killNeighbor(self, env.grid_map.cellAt(self.col-1, self.row)); - killNeighbor(self, env.grid_map.cellAt(self.col, self.row+1)); - killNeighbor(self, env.grid_map.cellAt(self.col, self.row-1)); + for (var loc of Hyperparams.killableNeighbors){ + var cell = env.grid_map.cellAt(self.col+loc[0], self.row+loc[1]); + killNeighbor(self, cell); + } } function killNeighbor(self, n_cell) { - if(n_cell == null) { - // console.log("null cell") + if(n_cell == null || n_cell.owner == null || n_cell.owner == self.owner || !n_cell.owner.living || n_cell.type == CellTypes.armor) return; - } - if(n_cell.owner == null) { - // console.log("is no one's cell") - return; - } - if(n_cell.owner == self.owner) { - // console.log("is my cell") - return; - } - if (!n_cell.owner.living) { - // console.log("cell is dead") - return; - } - if (n_cell.type == CellTypes.armor) { - // console.log("armor block") - // self.owner.die(); - return - } var should_die = n_cell.type == CellTypes.killer; // has to be calculated before death n_cell.owner.die(); if (should_die){ diff --git a/src/ControlPanel.js b/src/ControlPanel.js index 48b907a..b1d8613 100644 --- a/src/ControlPanel.js +++ b/src/ControlPanel.js @@ -1,9 +1,12 @@ +var Hyperparams = require("./Hyperparameters"); class ControlPanel { constructor(engine) { this.engine = engine; this.defineEngineSpeedControls(); + this.defineHyperparameterControls(); this.fps = engine.fps; + this.organism_record=0; } defineEngineSpeedControls(){ @@ -12,7 +15,9 @@ class ControlPanel { this.fps = this.slider.value if (this.engine.running) { this.changeEngineSpeed(this.fps); + } + $('#fps').text("Target FPS: "+this.fps); }.bind(this); $('#pause-button').click(function() { if ($('#pause-button').text() == "Pause" && this.engine.running) { @@ -27,12 +32,47 @@ class ControlPanel { }.bind(this)); } + defineHyperparameterControls() { + $('#food-prod-prob').change(function() { + var food_prob = $('#food-prod-prob').val(); + if ($('#fixed-ratio').is(":checked")) { + Hyperparams.foodProdProb = food_prob; + Hyperparams.calcProducerFoodRatio(false); + $('#lifespan-multiplier').val(Hyperparams.lifespanMultiplier); + } + else{ + Hyperparams.foodProdProb = food_prob; + } + }.bind(this)); + $('#lifespan-multiplier').change(function() { + var lifespan = $('#lifespan-multiplier').val(); + if ($('#fixed-ratio').is(":checked")) { + Hyperparams.lifespanMultiplier = lifespan; + Hyperparams.calcProducerFoodRatio(true); + $('#food-prod-prob').val(Hyperparams.foodProdProb); + } + else { + Hyperparams.lifespanMultiplier = lifespan; + } + }.bind(this)); + } + changeEngineSpeed(change_val) { this.engine.stop(); this.engine.start(change_val) this.fps = this.engine.fps; } + update() { + $('#fps-actual').text("Actual FPS: " + Math.floor(this.engine.actual_fps)); + var org_count = this.engine.env.organisms.length; + $('#org-count').text("Organism count: " + org_count); + if (org_count > this.organism_record) + this.organism_record = org_count; + $('#org-record').text("Highest count: " + this.organism_record); + + } + } diff --git a/src/Engine.js b/src/Engine.js index 78b8d18..7d5c3a8 100644 --- a/src/Engine.js +++ b/src/Engine.js @@ -4,9 +4,9 @@ const ControlPanel = require('./ControlPanel'); class Engine{ constructor(){ this.fps = 0; - this.environment = new Environment(5); + this.env = new Environment(5); this.controlpanel = new ControlPanel(this); - this.environment.OriginOfLife(); + this.env.OriginOfLife(); this.last_update = Date.now(); this.delta_time = 0; this.actual_fps = 0; @@ -16,25 +16,41 @@ class Engine{ start(fps=60) { if (fps <= 0) fps = 1; - if (fps > 500) - fps = 500; + if (fps > 300) + fps = 300; this.fps = fps; this.game_loop = setInterval(function(){this.update();}.bind(this), 1000/fps); this.running = true; + if (this.fps >= 45) { + if (this.render_loop != null) { + clearInterval(this.render_loop); + this.render_loop = null; + } + } + else + this.setRenderLoop(); } stop() { clearInterval(this.game_loop); this.running = false; + this.setRenderLoop(); + } + + setRenderLoop() { + if (this.render_loop == null) { + this.render_loop = setInterval(function(){this.env.render();}.bind(this), 1000/45); + } } update() { this.delta_time = Date.now() - this.last_update; this.last_update = Date.now(); - this.environment.update(this.delta_time); - this.environment.render(); + this.env.update(this.delta_time); + this.env.render(); this.actual_fps = 1/this.delta_time*1000; + this.controlpanel.update(); } } diff --git a/src/Environment.js b/src/Environment.js index 3f33ecc..eb9cd6e 100644 --- a/src/Environment.js +++ b/src/Environment.js @@ -29,7 +29,6 @@ class Environment{ } render() { - // console.log(this.cells_to_render); this.renderer.renderCells(); this.renderer.renderHighlights(); } diff --git a/src/Hyperparameters.js b/src/Hyperparameters.js new file mode 100644 index 0000000..68ae58f --- /dev/null +++ b/src/Hyperparameters.js @@ -0,0 +1,24 @@ +const Neighbors = require("./Neighbors"); + +var Hyperparams = { + lifespanMultiplier: 100, + foodProdProb: 1, + killableNeighbors: Neighbors.adjacent, + edibleNeighbors: Neighbors.adjacent, + growableNeighbors: Neighbors.adjacent, + + + // calculates the optimal ratio where a producer cell is most likely to produce 1 food in its lifespan. + calcProducerFoodRatio : function(lifespan_fixed=true) { + if (lifespan_fixed) { + // change the foodProdProb + this.foodProdProb = 100 / this.lifespanMultiplier; + } + else { + // change the lifespanMultiplier + this.lifespanMultiplier = Math.floor(100 / this.foodProdProb); + } + } +} + +module.exports = Hyperparams; \ No newline at end of file diff --git a/src/Neighbors.js b/src/Neighbors.js new file mode 100644 index 0000000..6aa1762 --- /dev/null +++ b/src/Neighbors.js @@ -0,0 +1,21 @@ +// contains local cell values for the following: + +//all ... +// .x. +// ... + +//adjacent . +// .x. +// . + +//corners . . +// x +// . . + +const Neighbors = { + all: [[0, 1],[0, -1],[1, 0],[-1, 0],[-1, -1],[1, 1],[-1, 1],[1, -1]], + adjacent: [[0, 1],[0, -1],[1, 0],[-1, 0]], + corners: [[-1, -1],[1, 1],[-1, 1],[1, -1]] +} + +module.exports = Neighbors; \ No newline at end of file diff --git a/src/Organism.js b/src/Organism.js index 66d286c..7c0b544 100644 --- a/src/Organism.js +++ b/src/Organism.js @@ -3,6 +3,10 @@ const Cell = require("./Cell"); const GridMap = require("./GridMap"); const LocalCell = require("./LocalCell"); const { producer } = require("./CellTypes"); +const Neighbors = require("./Neighbors"); +var Hyperparams = require("./Hyperparameters"); + +const directions = [[0,1],[0,-1],[1,0],[-1,0]] class Organism { constructor(col, row, env, parent=null) { @@ -17,7 +21,7 @@ class Organism { this.is_mover = false; this.direction = this.getRandomDirection(); this.move_count = 0; - this.move_range = 1; + this.move_range = 5; this.mutability = 5; if (parent != null) { this.inherit(parent); @@ -77,7 +81,8 @@ class Organism { } lifespan() { - return this.cells.length * 100; + // console.log(Hyperparams.lifespanMultiplier) + return this.cells.length * Hyperparams.lifespanMultiplier; } reproduce() { @@ -122,8 +127,9 @@ class Organism { var num_to_add = Math.floor(Math.random() * 3) + 1; for (var i=0; i