Added various control panel options
This commit is contained in:
34
dist/bundle.js
vendored
34
dist/bundle.js
vendored
File diff suppressed because one or more lines are too long
18
dist/css/style.css
vendored
18
dist/css/style.css
vendored
@@ -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; */
|
||||
|
||||
}
|
||||
15
dist/html/index.html
vendored
15
dist/html/index.html
vendored
@@ -17,10 +17,25 @@
|
||||
<h2>Simulation Speed</h2>
|
||||
<input id="slider" type="range" min="1" max="300" value="60">
|
||||
<button id='pause-button'>Pause</button>
|
||||
<p id='fps'>Target FPS: 60</p>
|
||||
<p id='fps-actual'></p>
|
||||
</div>
|
||||
|
||||
<div id='stats' class='control-set'>
|
||||
<h2>Stats</h2>
|
||||
<p id='org-count'>Organism count: </p>
|
||||
<p id='org-record'>Highest count: </p>
|
||||
</div>
|
||||
|
||||
<div id='hyperparameters' class='control-set'>
|
||||
<h2>Hyperparameters</h2>
|
||||
<label for="food-prod-prob">Probability of producing food:</label>
|
||||
<input type="number" id="food-prod-prob" min=".001" max="100" value=1 step="1">
|
||||
<label for="lifespan-multiplier">Lifespan multiplier:</label>
|
||||
<input type="number" id="lifespan-multiplier" min="1" max="10000" value=100 step="1">
|
||||
<br/>
|
||||
<label for="fixed-ratio">Use fixed ratio</label>
|
||||
<input type="checkbox" id="fixed-ratio" name="scales" checked>
|
||||
</div>
|
||||
|
||||
<div id='powers' class='control-set'>
|
||||
|
||||
58
src/Cell.js
58
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){
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ class Environment{
|
||||
}
|
||||
|
||||
render() {
|
||||
// console.log(this.cells_to_render);
|
||||
this.renderer.renderCells();
|
||||
this.renderer.renderHighlights();
|
||||
}
|
||||
|
||||
24
src/Hyperparameters.js
Normal file
24
src/Hyperparameters.js
Normal file
@@ -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;
|
||||
21
src/Neighbors.js
Normal file
21
src/Neighbors.js
Normal file
@@ -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;
|
||||
@@ -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<num_to_add; i++){
|
||||
var branch = this.cells[Math.floor(Math.random() * this.cells.length)];
|
||||
var c = branch.loc_col+Math.floor(Math.random() * 2) - 1;
|
||||
var r = branch.loc_row+Math.floor(Math.random() * 2) - 1;
|
||||
var growth_direction = Neighbors.all[Math.floor(Math.random() * Neighbors.all.length)]
|
||||
var c = branch.loc_col+growth_direction[0];
|
||||
var r = branch.loc_row+growth_direction[1];
|
||||
return this.addCell(type, c, r);
|
||||
}
|
||||
}
|
||||
@@ -168,7 +174,6 @@ class Organism {
|
||||
}
|
||||
|
||||
getRandomDirection(){
|
||||
var directions = [[0,1],[0,-1],[1,0],[-1,0]]
|
||||
return directions[Math.floor(Math.random() * directions.length)];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user