Added various control panel options

This commit is contained in:
MaxRobinsonTheGreat
2020-07-09 20:32:25 -06:00
parent c6b0a5bafc
commit 949d46e7c4
10 changed files with 196 additions and 58 deletions

34
dist/bundle.js vendored

File diff suppressed because one or more lines are too long

18
dist/css/style.css vendored
View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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