merged species tracking
This commit is contained in:
24
dist/css/style.css
vendored
24
dist/css/style.css
vendored
@@ -9,7 +9,7 @@ body{
|
|||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
#env {
|
#env {
|
||||||
@@ -39,12 +39,12 @@ body{
|
|||||||
background-color: #3a4b68;
|
background-color: #3a4b68;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
opacity: 0.8;
|
/* opacity: 0.8; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.control-panel:hover {
|
/* .control-panel:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
} */
|
||||||
|
|
||||||
.control-set {
|
.control-set {
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
@@ -226,6 +226,12 @@ button:hover{
|
|||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#chartContainer {
|
||||||
|
height: 200px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.hot-controls {
|
.hot-controls {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 10px;
|
bottom: 10px;
|
||||||
@@ -233,6 +239,16 @@ button:hover{
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#headless-notification {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
color: white;
|
||||||
|
font-size: 200px;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
#minimize {
|
#minimize {
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
float: right;
|
float: right;
|
||||||
|
|||||||
36
dist/index.html
vendored
36
dist/index.html
vendored
@@ -7,6 +7,7 @@
|
|||||||
<link rel="stylesheet" href="./css/style.css">
|
<link rel="stylesheet" href="./css/style.css">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||||
|
<script type="text/javascript" src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
<h2>Simulation Speed</h2>
|
<h2>Simulation Speed</h2>
|
||||||
<input id="slider" type="range" min="1" max="300" value="60">
|
<input id="slider" type="range" min="1" max="300" value="60">
|
||||||
<button class='pause-button'><i class="fa fa-pause"></i></button>
|
<button class='pause-button'><i class="fa fa-pause"></i></button>
|
||||||
|
<button class="headless" title="Toggle rendering"><i class="fa fa-eye-slash"></i></button>
|
||||||
<p id='fps'>Target FPS: 60</p>
|
<p id='fps'>Target FPS: 60</p>
|
||||||
<p id='fps-actual'></p>
|
<p id='fps-actual'></p>
|
||||||
<button id='reset-env'>Reset Environment</button>
|
<button id='reset-env'>Reset Environment</button>
|
||||||
@@ -50,10 +52,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<button id='resize'>Resize and Reset</button>
|
<button id='resize'>Resize and Reset</button>
|
||||||
<div class='icon-links'>
|
|
||||||
<a href="https://www.youtube.com/channel/UCwBhBDsqiQflTMLy2epbQVw"><i class="fa fa-youtube"></i></a>
|
|
||||||
<a href="https://twitter.com/max_romana"><i class="fa fa-twitter"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id='tab-container' class='control-set'>
|
<div id='tab-container' class='control-set'>
|
||||||
@@ -91,6 +89,10 @@
|
|||||||
<a href="https://github.com/MaxRobinsonTheGreat/EvolutionSimulator/blob/master/V2README.md">readme</a>.
|
<a href="https://github.com/MaxRobinsonTheGreat/EvolutionSimulator/blob/master/V2README.md">readme</a>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class='icon-links'>
|
||||||
|
<a href="https://www.youtube.com/channel/UCwBhBDsqiQflTMLy2epbQVw"><i class="fa fa-youtube"></i></a>
|
||||||
|
<a href="https://twitter.com/max_romana"><i class="fa fa-twitter"></i></a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id='editor' class='tab'>
|
<div id='editor' class='tab'>
|
||||||
@@ -220,13 +222,24 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id='stats' class='tab'>
|
<div id='stats' class='tab'>
|
||||||
<div class='left-half'>
|
<div class='left-half'>
|
||||||
<h2>Stats</h2>
|
<h2>Statistics</h2>
|
||||||
<p id='org-count'>Organism count: </p>
|
<p id='org-count'>Total Population: </p>
|
||||||
<p id='org-record'>Highest count: </p>
|
<p id='species-count'>Number of Species: </p>
|
||||||
|
<p id='largest-org'>Largest Organism Ever: </p>
|
||||||
<p id='avg-mut'>Average Mutation Rate: </p>
|
<p id='avg-mut'>Average Mutation Rate: </p>
|
||||||
<p id='largest-org'>Largest Organism: </p>
|
<label for="chart-option">Chart: </label>
|
||||||
|
<select name="chart-option" id="chart-option">
|
||||||
|
<option value="population">Population</option>
|
||||||
|
<option value="species">Species</option>
|
||||||
|
<option value="mut-rate">Organism Structure</option>
|
||||||
|
<option value="mut-rate">Mutation Rate</option>
|
||||||
|
</select>
|
||||||
|
<p id='chart-note'></p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class='right-half'>
|
||||||
|
<div id="chartContainer"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class='right-half'></div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div id='challenges' class='tab'>
|
<div id='challenges' class='tab'>
|
||||||
@@ -254,7 +267,10 @@
|
|||||||
<button class="edit-mode-btn" id="food-drop" title="Drop/Remove Food"><i class="fa fa-cutlery"></i></button>
|
<button class="edit-mode-btn" id="food-drop" title="Drop/Remove Food"><i class="fa fa-cutlery"></i></button>
|
||||||
<button class="edit-mode-btn" id="click-kill" title="Click to kill"><i class="fa fa-bolt"></i></button>
|
<button class="edit-mode-btn" id="click-kill" title="Click to kill"><i class="fa fa-bolt"></i></button>
|
||||||
<button class='pause-button'><i class="fa fa-pause"></i></button>
|
<button class='pause-button'><i class="fa fa-pause"></i></button>
|
||||||
|
<button class="headless" title="Toggle rendering"><i class="fa fa-eye-slash"></i></button>
|
||||||
|
</div>
|
||||||
|
<div id="headless-notification">
|
||||||
|
<i class="fa fa-eye-slash" ></i>
|
||||||
</div>
|
</div>
|
||||||
<div id='maximize-hot-control' class='hot-controls'>
|
<div id='maximize-hot-control' class='hot-controls'>
|
||||||
<button id="maximize" title="Show Control Panel"><i class="fa fa-plus-square"></i></button>
|
<button id="maximize" title="Show Control Panel"><i class="fa fa-plus-square"></i></button>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
const Hyperparams = require("../Hyperparameters");
|
const Hyperparams = require("../Hyperparameters");
|
||||||
const Modes = require("./ControlModes");
|
const Modes = require("./ControlModes");
|
||||||
|
const StatsPanel = require("../Stats/StatsPanel");
|
||||||
|
|
||||||
class ControlPanel {
|
class ControlPanel {
|
||||||
constructor(engine) {
|
constructor(engine) {
|
||||||
@@ -17,6 +18,10 @@ class ControlPanel {
|
|||||||
this.editor_controller = this.engine.organism_editor.controller;
|
this.editor_controller = this.engine.organism_editor.controller;
|
||||||
this.env_controller.setControlPanel(this);
|
this.env_controller.setControlPanel(this);
|
||||||
this.editor_controller.setControlPanel(this);
|
this.editor_controller.setControlPanel(this);
|
||||||
|
this.stats_panel = new StatsPanel(this.engine.env);
|
||||||
|
this.headless_opacity = 1;
|
||||||
|
this.opacity_change_rate = -0.8;
|
||||||
|
this.paused=false;
|
||||||
}
|
}
|
||||||
|
|
||||||
defineMinMaxControls(){
|
defineMinMaxControls(){
|
||||||
@@ -26,11 +31,15 @@ class ControlPanel {
|
|||||||
$('.control-panel').css('display', 'none');
|
$('.control-panel').css('display', 'none');
|
||||||
$('.hot-controls').css('display', 'block');
|
$('.hot-controls').css('display', 'block');
|
||||||
this.control_panel_active = false;
|
this.control_panel_active = false;
|
||||||
|
this.stats_panel.stopAutoRender();
|
||||||
});
|
});
|
||||||
$('#maximize').click ( () => {
|
$('#maximize').click ( () => {
|
||||||
$('.control-panel').css('display', 'grid');
|
$('.control-panel').css('display', 'grid');
|
||||||
$('.hot-controls').css('display', 'none');
|
$('.hot-controls').css('display', 'none');
|
||||||
this.control_panel_active = true;
|
this.control_panel_active = true;
|
||||||
|
if (this.tab_id == 'stats') {
|
||||||
|
this.stats_panel.startAutoRender();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
const V_KEY = 118;
|
const V_KEY = 118;
|
||||||
$('body').keypress( (e) => {
|
$('body').keypress( (e) => {
|
||||||
@@ -38,6 +47,9 @@ class ControlPanel {
|
|||||||
if (this.no_hud) {
|
if (this.no_hud) {
|
||||||
let control_panel_display = this.control_panel_active ? 'grid' : 'none';
|
let control_panel_display = this.control_panel_active ? 'grid' : 'none';
|
||||||
let hot_control_display = !this.control_panel_active ? 'block' : 'none';
|
let hot_control_display = !this.control_panel_active ? 'block' : 'none';
|
||||||
|
if (this.control_panel_active && this.tab_id == 'stats') {
|
||||||
|
this.stats_panel.startAutoRender();
|
||||||
|
};
|
||||||
$('.control-panel').css('display', control_panel_display);
|
$('.control-panel').css('display', control_panel_display);
|
||||||
$('.hot-controls').css('display', hot_control_display);
|
$('.hot-controls').css('display', hot_control_display);
|
||||||
}
|
}
|
||||||
@@ -48,6 +60,19 @@ class ControlPanel {
|
|||||||
this.no_hud = !this.no_hud;
|
this.no_hud = !this.no_hud;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// var self = this;
|
||||||
|
// $('#minimize').click ( function() {
|
||||||
|
// $('.control-panel').css('display', 'none');
|
||||||
|
// $('.hot-controls').css('display', 'block');
|
||||||
|
|
||||||
|
// }.bind(this));
|
||||||
|
// $('#maximize').click ( function() {
|
||||||
|
// $('.control-panel').css('display', 'grid');
|
||||||
|
// $('.hot-controls').css('display', 'none');
|
||||||
|
// if (self.tab_id == 'stats') {
|
||||||
|
// self.stats_panel.startAutoRender();
|
||||||
|
// }
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
defineEngineSpeedControls(){
|
defineEngineSpeedControls(){
|
||||||
@@ -56,13 +81,13 @@ class ControlPanel {
|
|||||||
this.fps = this.slider.value
|
this.fps = this.slider.value
|
||||||
if (this.engine.running) {
|
if (this.engine.running) {
|
||||||
this.changeEngineSpeed(this.fps);
|
this.changeEngineSpeed(this.fps);
|
||||||
|
|
||||||
}
|
}
|
||||||
$('#fps').text("Target FPS: "+this.fps);
|
$('#fps').text("Target FPS: "+this.fps);
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
$('.pause-button').click(function() {
|
$('.pause-button').click(function() {
|
||||||
$('.pause-button').find("i").toggleClass("fa fa-pause");
|
$('.pause-button').find("i").toggleClass("fa fa-pause");
|
||||||
$('.pause-button').find("i").toggleClass("fa fa-play");
|
$('.pause-button').find("i").toggleClass("fa fa-play");
|
||||||
|
this.paused = !this.paused;
|
||||||
if (this.engine.running) {
|
if (this.engine.running) {
|
||||||
this.engine.stop();
|
this.engine.stop();
|
||||||
}
|
}
|
||||||
@@ -70,6 +95,18 @@ class ControlPanel {
|
|||||||
this.engine.start(this.fps);
|
this.engine.start(this.fps);
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
$('.headless').click(function() {
|
||||||
|
$('.headless').find("i").toggleClass("fa fa-eye");
|
||||||
|
$('.headless').find("i").toggleClass("fa fa-eye-slash");
|
||||||
|
if (Hyperparams.headless){
|
||||||
|
$('#headless-notification').css('display', 'none');
|
||||||
|
this.engine.env.renderFull();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$('#headless-notification').css('display', 'block');
|
||||||
|
}
|
||||||
|
Hyperparams.headless = !Hyperparams.headless;
|
||||||
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
defineGridSizeControls() {
|
defineGridSizeControls() {
|
||||||
@@ -91,24 +128,31 @@ class ControlPanel {
|
|||||||
var rows = $('#row-input').val();
|
var rows = $('#row-input').val();
|
||||||
this.engine.env.resizeGridColRow(cell_size, cols, rows);
|
this.engine.env.resizeGridColRow(cell_size, cols, rows);
|
||||||
}
|
}
|
||||||
|
this.engine.env.reset();
|
||||||
|
this.stats_panel.reset();
|
||||||
|
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
defineTabNavigation() {
|
defineTabNavigation() {
|
||||||
|
this.tab_id = 'about';
|
||||||
var self = this;
|
var self = this;
|
||||||
$('.tabnav-item').click(function() {
|
$('.tabnav-item').click(function() {
|
||||||
$('.tab').css('display', 'none');
|
$('.tab').css('display', 'none');
|
||||||
var tab = '#'+this.id+'.tab';
|
var tab = '#'+this.id+'.tab';
|
||||||
self.engine.organism_editor.is_active = (this.id == 'editor');
|
|
||||||
$(tab).css('display', 'grid');
|
$(tab).css('display', 'grid');
|
||||||
|
self.engine.organism_editor.is_active = (this.id == 'editor');
|
||||||
|
self.stats_panel.stopAutoRender();
|
||||||
|
if (this.id == 'stats') {
|
||||||
|
self.stats_panel.startAutoRender();
|
||||||
|
}
|
||||||
|
self.tab_id = this.id;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
defineHyperparameterControls() {
|
defineHyperparameterControls() {
|
||||||
$('#food-prod-prob').change(function() {
|
$('#food-prod-prob').change(function() {
|
||||||
var food_prob =
|
Hyperparams.foodProdProb = $('#food-prod-prob').val();
|
||||||
Hyperparams.foodProdProb = $('#food-prod-prob').val();;
|
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
$('#lifespan-multiplier').change(function() {
|
$('#lifespan-multiplier').change(function() {
|
||||||
Hyperparams.lifespanMultiplier = $('#lifespan-multiplier').val();
|
Hyperparams.lifespanMultiplier = $('#lifespan-multiplier').val();
|
||||||
@@ -142,7 +186,7 @@ class ControlPanel {
|
|||||||
Hyperparams.useGlobalMutability = !this.checked;
|
Hyperparams.useGlobalMutability = !this.checked;
|
||||||
});
|
});
|
||||||
$('#global-mutation').change( function() {
|
$('#global-mutation').change( function() {
|
||||||
Hyperparams.globalMutability = $('#global-mutation').val();
|
Hyperparams.globalMutability = parseInt($('#global-mutation').val());
|
||||||
});
|
});
|
||||||
$('.mut-prob').change( function() {
|
$('.mut-prob').change( function() {
|
||||||
switch(this.id){
|
switch(this.id){
|
||||||
@@ -182,6 +226,9 @@ class ControlPanel {
|
|||||||
$('#remove-prob').val(Hyperparams.removeProb);
|
$('#remove-prob').val(Hyperparams.removeProb);
|
||||||
$('#movers-produce').prop('checked', Hyperparams.moversCanProduce);
|
$('#movers-produce').prop('checked', Hyperparams.moversCanProduce);
|
||||||
$('#food-blocks').prop('checked', Hyperparams.foodBlocksReproduction);
|
$('#food-blocks').prop('checked', Hyperparams.foodBlocksReproduction);
|
||||||
|
$('#food-drop-rate').val(Hyperparams.foodDropProb);
|
||||||
|
$('#look-range').val(Hyperparams.lookRange);
|
||||||
|
|
||||||
if (!Hyperparams.useGlobalMutability) {
|
if (!Hyperparams.useGlobalMutability) {
|
||||||
$('.global-mutation-in').css('display', 'none');
|
$('.global-mutation-in').css('display', 'none');
|
||||||
$('#avg-mut').css('display', 'block');
|
$('#avg-mut').css('display', 'block');
|
||||||
@@ -199,7 +246,7 @@ class ControlPanel {
|
|||||||
$('#cell-selections').css('display', 'none');
|
$('#cell-selections').css('display', 'none');
|
||||||
$('#organism-options').css('display', 'none');
|
$('#organism-options').css('display', 'none');
|
||||||
self.editor_controller.setDetailsPanel();
|
self.editor_controller.setDetailsPanel();
|
||||||
switch(this.id){
|
switch(this.id) {
|
||||||
case "food-drop":
|
case "food-drop":
|
||||||
self.setMode(Modes.FoodDrop);
|
self.setMode(Modes.FoodDrop);
|
||||||
break;
|
break;
|
||||||
@@ -219,6 +266,9 @@ class ControlPanel {
|
|||||||
case "drop-org":
|
case "drop-org":
|
||||||
self.setMode(Modes.Clone);
|
self.setMode(Modes.Clone);
|
||||||
self.env_controller.org_to_clone = self.engine.organism_editor.getCopyOfOrg();
|
self.env_controller.org_to_clone = self.engine.organism_editor.getCopyOfOrg();
|
||||||
|
self.env_controller.add_new_species = self.editor_controller.new_species;
|
||||||
|
self.editor_controller.new_species = false;
|
||||||
|
// console.log(self.env_controller.add_new_species)
|
||||||
break;
|
break;
|
||||||
case "drag-view":
|
case "drag-view":
|
||||||
self.setMode(Modes.Drag);
|
self.setMode(Modes.Drag);
|
||||||
@@ -234,6 +284,7 @@ class ControlPanel {
|
|||||||
var env = this.engine.env;
|
var env = this.engine.env;
|
||||||
$('#reset-env').click( function() {
|
$('#reset-env').click( function() {
|
||||||
this.engine.env.reset();
|
this.engine.env.reset();
|
||||||
|
this.stats_panel.reset();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
$('#auto-reset').change(function() {
|
$('#auto-reset').change(function() {
|
||||||
env.auto_reset = this.checked;
|
env.auto_reset = this.checked;
|
||||||
@@ -273,16 +324,29 @@ class ControlPanel {
|
|||||||
this.fps = this.engine.fps;
|
this.fps = this.engine.fps;
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
updateHeadlessIcon(delta_time) {
|
||||||
|
if (this.paused)
|
||||||
|
return;
|
||||||
|
var op = this.headless_opacity + (this.opacity_change_rate*delta_time/1000);
|
||||||
|
if (op <= 0.4){
|
||||||
|
op=0.4;
|
||||||
|
this.opacity_change_rate = -this.opacity_change_rate;
|
||||||
|
}
|
||||||
|
else if (op >= 1){
|
||||||
|
op=1;
|
||||||
|
this.opacity_change_rate = -this.opacity_change_rate;
|
||||||
|
}
|
||||||
|
this.headless_opacity = op;
|
||||||
|
$('#headless-notification').css('opacity',(op*100)+'%');
|
||||||
|
}
|
||||||
|
|
||||||
|
update(delta_time) {
|
||||||
$('#fps-actual').text("Actual FPS: " + Math.floor(this.engine.actual_fps));
|
$('#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);
|
|
||||||
$('#avg-mut').text("Average Mutation Rate: " + Math.round(this.engine.env.averageMutability() * 100) / 100);
|
|
||||||
$('#largest-org').text("Largest Organism: " + this.engine.env.largest_cell_count + " cells");
|
|
||||||
$('#reset-count').text("Auto reset count: " + this.engine.env.reset_count);
|
$('#reset-count').text("Auto reset count: " + this.engine.env.reset_count);
|
||||||
|
this.stats_panel.updateDetails();
|
||||||
|
if (Hyperparams.headless)
|
||||||
|
this.updateHeadlessIcon(delta_time);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class EditorController extends CanvasController{
|
|||||||
this.mode = Modes.None;
|
this.mode = Modes.None;
|
||||||
this.edit_cell_type = null;
|
this.edit_cell_type = null;
|
||||||
this.highlight_org = false;
|
this.highlight_org = false;
|
||||||
|
this.new_species = false;
|
||||||
this.defineCellTypeSelection();
|
this.defineCellTypeSelection();
|
||||||
this.defineEditorDetails();
|
this.defineEditorDetails();
|
||||||
}
|
}
|
||||||
@@ -26,7 +27,7 @@ class EditorController extends CanvasController{
|
|||||||
mouseUp(){}
|
mouseUp(){}
|
||||||
|
|
||||||
getCurLocalCell(){
|
getCurLocalCell(){
|
||||||
return this.env.organism.getLocalCell(this.mouse_c-this.env.organism.c, this.mouse_r-this.env.organism.r);
|
return this.env.organism.anatomy.getLocalCell(this.mouse_c-this.env.organism.c, this.mouse_r-this.env.organism.r);
|
||||||
}
|
}
|
||||||
|
|
||||||
editOrganism() {
|
editOrganism() {
|
||||||
@@ -41,15 +42,17 @@ class EditorController extends CanvasController{
|
|||||||
else
|
else
|
||||||
this.env.addCellToOrg(this.mouse_c, this.mouse_r, this.edit_cell_type);
|
this.env.addCellToOrg(this.mouse_c, this.mouse_r, this.edit_cell_type);
|
||||||
}
|
}
|
||||||
if (this.right_click)
|
else if (this.right_click)
|
||||||
this.env.removeCellFromOrg(this.mouse_c, this.mouse_r);
|
this.env.removeCellFromOrg(this.mouse_c, this.mouse_r);
|
||||||
|
|
||||||
|
this.new_species = true;
|
||||||
this.setBrainPanelVisibility();
|
this.setBrainPanelVisibility();
|
||||||
this.setMoveRangeVisibility();
|
this.setMoveRangeVisibility();
|
||||||
this.updateDetails();
|
this.updateDetails();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDetails() {
|
updateDetails() {
|
||||||
$('.cell-count').text("Cell count: "+this.env.organism.cells.length);
|
$('.cell-count').text("Cell count: "+this.env.organism.anatomy.cells.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
defineCellTypeSelection() {
|
defineCellTypeSelection() {
|
||||||
@@ -111,7 +114,7 @@ class EditorController extends CanvasController{
|
|||||||
this.clearDetailsPanel();
|
this.clearDetailsPanel();
|
||||||
var org = this.env.organism;
|
var org = this.env.organism;
|
||||||
|
|
||||||
$('.cell-count').text("Cell count: "+org.cells.length);
|
$('.cell-count').text("Cell count: "+org.anatomy.cells.length);
|
||||||
$('#move-range').text("Move Range: "+org.move_range);
|
$('#move-range').text("Move Range: "+org.move_range);
|
||||||
$('#mutation-rate').text("Mutation Rate: "+org.mutability);
|
$('#mutation-rate').text("Mutation Rate: "+org.mutability);
|
||||||
if (Hyperparams.useGlobalMutability) {
|
if (Hyperparams.useGlobalMutability) {
|
||||||
@@ -134,7 +137,7 @@ class EditorController extends CanvasController{
|
|||||||
this.clearDetailsPanel();
|
this.clearDetailsPanel();
|
||||||
var org = this.env.organism;
|
var org = this.env.organism;
|
||||||
|
|
||||||
$('.cell-count').text("Cell count: "+org.cells.length);
|
$('.cell-count').text("Cell count: "+org.anatomy.cells.length);
|
||||||
if (this.setMoveRangeVisibility()){
|
if (this.setMoveRangeVisibility()){
|
||||||
$('#move-range-edit').val(org.move_range);
|
$('#move-range-edit').val(org.move_range);
|
||||||
}
|
}
|
||||||
@@ -149,7 +152,7 @@ class EditorController extends CanvasController{
|
|||||||
|
|
||||||
setBrainPanelVisibility() {
|
setBrainPanelVisibility() {
|
||||||
var org = this.env.organism;
|
var org = this.env.organism;
|
||||||
if (org.has_eyes && org.is_mover) {
|
if (org.anatomy.has_eyes && org.anatomy.is_mover) {
|
||||||
$('.brain-details').css('display', 'block');
|
$('.brain-details').css('display', 'block');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -175,7 +178,7 @@ class EditorController extends CanvasController{
|
|||||||
|
|
||||||
setMoveRangeVisibility() {
|
setMoveRangeVisibility() {
|
||||||
var org = this.env.organism;
|
var org = this.env.organism;
|
||||||
if (org.is_mover) {
|
if (org.anatomy.is_mover) {
|
||||||
$('#move-range-cont').css('display', 'block');
|
$('#move-range-cont').css('display', 'block');
|
||||||
$('#move-range').css('display', 'block');
|
$('#move-range').css('display', 'block');
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -3,12 +3,15 @@ const Organism = require('../Organism/Organism');
|
|||||||
const Modes = require("./ControlModes");
|
const Modes = require("./ControlModes");
|
||||||
const CellStates = require("../Organism/Cell/CellStates");
|
const CellStates = require("../Organism/Cell/CellStates");
|
||||||
const Neighbors = require("../Grid/Neighbors");
|
const Neighbors = require("../Grid/Neighbors");
|
||||||
|
const FossilRecord = require("../Stats/FossilRecord");
|
||||||
|
const Hyperparams = require("../Hyperparameters");
|
||||||
|
|
||||||
class EnvironmentController extends CanvasController{
|
class EnvironmentController extends CanvasController{
|
||||||
constructor(env, canvas) {
|
constructor(env, canvas) {
|
||||||
super(env, canvas);
|
super(env, canvas);
|
||||||
this.mode = Modes.Drag;
|
this.mode = Modes.Drag;
|
||||||
this.org_to_clone = null;
|
this.org_to_clone = null;
|
||||||
|
this.add_new_species = false;
|
||||||
this.defineZoomControls();
|
this.defineZoomControls();
|
||||||
this.scale = 1;
|
this.scale = 1;
|
||||||
}
|
}
|
||||||
@@ -76,6 +79,8 @@ class EnvironmentController extends CanvasController{
|
|||||||
}
|
}
|
||||||
|
|
||||||
performModeAction() {
|
performModeAction() {
|
||||||
|
if (Hyperparams.headless)
|
||||||
|
return;
|
||||||
var mode = this.mode;
|
var mode = this.mode;
|
||||||
var right_click = this.right_click;
|
var right_click = this.right_click;
|
||||||
var left_click = this.left_click;
|
var left_click = this.left_click;
|
||||||
@@ -118,8 +123,19 @@ class EnvironmentController extends CanvasController{
|
|||||||
case Modes.Clone:
|
case Modes.Clone:
|
||||||
if (this.org_to_clone != null){
|
if (this.org_to_clone != null){
|
||||||
var new_org = new Organism(this.mouse_c, this.mouse_r, this.env, this.org_to_clone);
|
var new_org = new Organism(this.mouse_c, this.mouse_r, this.env, this.org_to_clone);
|
||||||
|
if (this.add_new_species){
|
||||||
|
FossilRecord.addSpeciesObj(new_org.species);
|
||||||
|
new_org.species.start_tick = this.env.total_ticks;
|
||||||
|
this.add_new_species = false;
|
||||||
|
new_org.species.population = 0;
|
||||||
|
}
|
||||||
|
else if (this.org_to_clone.species.extinct){
|
||||||
|
FossilRecord.resurrect(this.org_to_clone.species);
|
||||||
|
}
|
||||||
|
|
||||||
if (new_org.isClear(this.mouse_c, this.mouse_r)){
|
if (new_org.isClear(this.mouse_c, this.mouse_r)){
|
||||||
this.env.addOrganism(new_org)
|
this.env.addOrganism(new_org);
|
||||||
|
new_org.species.addPop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const ColorScheme = require('./Rendering/ColorScheme');
|
|||||||
|
|
||||||
const render_speed = 60;
|
const render_speed = 60;
|
||||||
|
|
||||||
class Engine{
|
class Engine {
|
||||||
constructor(){
|
constructor(){
|
||||||
this.fps = 60;
|
this.fps = 60;
|
||||||
this.env = new WorldEnvironment(5);
|
this.env = new WorldEnvironment(5);
|
||||||
@@ -23,10 +23,8 @@ class Engine{
|
|||||||
start(fps=60) {
|
start(fps=60) {
|
||||||
if (fps <= 0)
|
if (fps <= 0)
|
||||||
fps = 1;
|
fps = 1;
|
||||||
if (fps > 300)
|
|
||||||
fps = 300;
|
|
||||||
this.fps = fps;
|
this.fps = fps;
|
||||||
this.game_loop = setInterval(function(){this.environmentUpdate();}.bind(this), 1000/fps);
|
this.game_loop = setInterval(function(){this.updateDeltaTime();this.environmentUpdate();}.bind(this), 1000/fps);
|
||||||
this.running = true;
|
this.running = true;
|
||||||
if (this.fps >= render_speed) {
|
if (this.fps >= render_speed) {
|
||||||
if (this.render_loop != null) {
|
if (this.render_loop != null) {
|
||||||
@@ -46,14 +44,17 @@ class Engine{
|
|||||||
|
|
||||||
setRenderLoop() {
|
setRenderLoop() {
|
||||||
if (this.render_loop == null) {
|
if (this.render_loop == null) {
|
||||||
this.render_loop = setInterval(function(){this.necessaryUpdate();}.bind(this), 1000/render_speed);
|
this.render_loop = setInterval(function(){this.updateDeltaTime();this.necessaryUpdate();}.bind(this), 1000/render_speed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateDeltaTime() {
|
||||||
|
this.delta_time = Date.now() - this.last_update;
|
||||||
|
this.last_update = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
environmentUpdate() {
|
environmentUpdate() {
|
||||||
this.delta_time = Date.now() - this.last_update;
|
|
||||||
this.last_update = Date.now();
|
|
||||||
this.env.update(this.delta_time);
|
this.env.update(this.delta_time);
|
||||||
this.actual_fps = 1/this.delta_time*1000;
|
this.actual_fps = 1/this.delta_time*1000;
|
||||||
if(this.render_loop == null){
|
if(this.render_loop == null){
|
||||||
@@ -64,7 +65,7 @@ class Engine{
|
|||||||
|
|
||||||
necessaryUpdate() {
|
necessaryUpdate() {
|
||||||
this.env.render();
|
this.env.render();
|
||||||
this.controlpanel.update();
|
this.controlpanel.update(this.delta_time);
|
||||||
this.organism_editor.update();
|
this.organism_editor.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const GridMap = require('../Grid/GridMap');
|
|||||||
const Renderer = require('../Rendering/Renderer');
|
const Renderer = require('../Rendering/Renderer');
|
||||||
const CellStates = require('../Organism/Cell/CellStates');
|
const CellStates = require('../Organism/Cell/CellStates');
|
||||||
const EditorController = require("../Controllers/EditorController");
|
const EditorController = require("../Controllers/EditorController");
|
||||||
const Directions = require('../Organism/Directions');
|
const Species = require('../Stats/Species');
|
||||||
|
|
||||||
class OrganismEditor extends Environment{
|
class OrganismEditor extends Environment{
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -37,14 +37,15 @@ class OrganismEditor extends Environment{
|
|||||||
var center = this.grid_map.getCenter();
|
var center = this.grid_map.getCenter();
|
||||||
var loc_c = c - center[0];
|
var loc_c = c - center[0];
|
||||||
var loc_r = r - center[1];
|
var loc_r = r - center[1];
|
||||||
var prev_cell = this.organism.getLocalCell(loc_c, loc_r)
|
var prev_cell = this.organism.anatomy.getLocalCell(loc_c, loc_r)
|
||||||
if (prev_cell != null) {
|
if (prev_cell != null) {
|
||||||
var new_cell = this.organism.replaceCell(state, prev_cell.loc_col, prev_cell.loc_row, false);
|
var new_cell = this.organism.anatomy.replaceCell(state, prev_cell.loc_col, prev_cell.loc_row, false);
|
||||||
this.changeCell(c, r, state, new_cell);
|
this.changeCell(c, r, state, new_cell);
|
||||||
}
|
}
|
||||||
else if (this.organism.canAddCellAt(loc_c, loc_r)){
|
else if (this.organism.anatomy.canAddCellAt(loc_c, loc_r)){
|
||||||
this.changeCell(c, r, state, this.organism.addDefaultCell(state, loc_c, loc_r));
|
this.changeCell(c, r, state, this.organism.anatomy.addDefaultCell(state, loc_c, loc_r));
|
||||||
}
|
}
|
||||||
|
this.organism.species = new Species(this.organism.anatomy, null, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeCellFromOrg(c, r) {
|
removeCellFromOrg(c, r) {
|
||||||
@@ -55,20 +56,22 @@ class OrganismEditor extends Environment{
|
|||||||
alert("Cannot remove center cell");
|
alert("Cannot remove center cell");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var prev_cell = this.organism.getLocalCell(loc_c, loc_r)
|
var prev_cell = this.organism.anatomy.getLocalCell(loc_c, loc_r)
|
||||||
if (prev_cell != null) {
|
if (prev_cell != null) {
|
||||||
if (this.organism.removeCell(loc_c, loc_r)) {
|
if (this.organism.anatomy.removeCell(loc_c, loc_r)) {
|
||||||
this.changeCell(c, r, CellStates.empty, null);
|
this.changeCell(c, r, CellStates.empty, null);
|
||||||
|
this.organism.species = new Species(this.organism.anatomy, null, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setOrganismToCopyOf(orig_org){
|
setOrganismToCopyOf(orig_org) {
|
||||||
this.grid_map.fillGrid(CellStates.empty);
|
this.grid_map.fillGrid(CellStates.empty);
|
||||||
var center = this.grid_map.getCenter();
|
var center = this.grid_map.getCenter();
|
||||||
this.organism = new Organism(center[0], center[1], this, orig_org);
|
this.organism = new Organism(center[0], center[1], this, orig_org);
|
||||||
this.organism.updateGrid();
|
this.organism.updateGrid();
|
||||||
this.controller.updateDetails();
|
this.controller.updateDetails();
|
||||||
|
this.controller.new_species = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCopyOfOrg() {
|
getCopyOfOrg() {
|
||||||
@@ -80,8 +83,9 @@ class OrganismEditor extends Environment{
|
|||||||
this.grid_map.fillGrid(CellStates.empty);
|
this.grid_map.fillGrid(CellStates.empty);
|
||||||
var center = this.grid_map.getCenter();
|
var center = this.grid_map.getCenter();
|
||||||
this.organism = new Organism(center[0], center[1], this, null);
|
this.organism = new Organism(center[0], center[1], this, null);
|
||||||
this.organism.addDefaultCell(CellStates.mouth, 0, 0);
|
this.organism.anatomy.addDefaultCell(CellStates.mouth, 0, 0);
|
||||||
this.organism.updateGrid();
|
this.organism.updateGrid();
|
||||||
|
this.organism.species = new Species(this.organism.anatomy, null, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const Organism = require('../Organism/Organism');
|
|||||||
const CellStates = require('../Organism/Cell/CellStates');
|
const CellStates = require('../Organism/Cell/CellStates');
|
||||||
const EnvironmentController = require('../Controllers/EnvironmentController');
|
const EnvironmentController = require('../Controllers/EnvironmentController');
|
||||||
const Hyperparams = require('../Hyperparameters.js');
|
const Hyperparams = require('../Hyperparameters.js');
|
||||||
const Cell = require('../Organism/Cell/GridCell');
|
const FossilRecord = require('../Stats/FossilRecord');
|
||||||
|
|
||||||
class WorldEnvironment extends Environment{
|
class WorldEnvironment extends Environment{
|
||||||
constructor(cell_size) {
|
constructor(cell_size) {
|
||||||
@@ -21,6 +21,9 @@ class WorldEnvironment extends Environment{
|
|||||||
this.auto_reset = true;
|
this.auto_reset = true;
|
||||||
this.largest_cell_count = 0;
|
this.largest_cell_count = 0;
|
||||||
this.reset_count = 0;
|
this.reset_count = 0;
|
||||||
|
this.total_ticks = 0;
|
||||||
|
this.data_update_rate = 100;
|
||||||
|
FossilRecord.setEnv(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
@@ -35,13 +38,25 @@ class WorldEnvironment extends Environment{
|
|||||||
this.generateFood();
|
this.generateFood();
|
||||||
}
|
}
|
||||||
this.removeOrganisms(to_remove);
|
this.removeOrganisms(to_remove);
|
||||||
|
this.total_ticks ++;
|
||||||
|
if (this.total_ticks % this.data_update_rate == 0) {
|
||||||
|
FossilRecord.updateData();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
if (Hyperparams.headless) {
|
||||||
|
this.renderer.cells_to_render.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.renderer.renderCells();
|
this.renderer.renderCells();
|
||||||
this.renderer.renderHighlights();
|
this.renderer.renderHighlights();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderFull() {
|
||||||
|
this.renderer.renderFullGrid(this.grid_map.grid);
|
||||||
|
}
|
||||||
|
|
||||||
removeOrganisms(org_indeces) {
|
removeOrganisms(org_indeces) {
|
||||||
for (var i of org_indeces.reverse()){
|
for (var i of org_indeces.reverse()){
|
||||||
this.total_mutability -= this.organisms[i].mutability;
|
this.total_mutability -= this.organisms[i].mutability;
|
||||||
@@ -56,26 +71,27 @@ class WorldEnvironment extends Environment{
|
|||||||
OriginOfLife() {
|
OriginOfLife() {
|
||||||
var center = this.grid_map.getCenter();
|
var center = this.grid_map.getCenter();
|
||||||
var org = new Organism(center[0], center[1], this);
|
var org = new Organism(center[0], center[1], this);
|
||||||
// org.addDefaultCell(CellStates.eye, 0, 0);
|
org.anatomy.addDefaultCell(CellStates.mouth, 0, 0);
|
||||||
// org.addDefaultCell(CellStates.mouth, 1, 1);
|
org.anatomy.addDefaultCell(CellStates.producer, 1, 1);
|
||||||
// org.addDefaultCell(CellStates.mover, 1, -1);
|
org.anatomy.addDefaultCell(CellStates.producer, -1, -1);
|
||||||
org.addDefaultCell(CellStates.mouth, 0, 0);
|
|
||||||
org.addDefaultCell(CellStates.producer, 1, 1);
|
|
||||||
org.addDefaultCell(CellStates.producer, -1, -1);
|
|
||||||
this.addOrganism(org);
|
this.addOrganism(org);
|
||||||
|
FossilRecord.addSpecies(org, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
addOrganism(organism) {
|
addOrganism(organism) {
|
||||||
organism.updateGrid();
|
organism.updateGrid();
|
||||||
this.total_mutability += organism.mutability;
|
this.total_mutability += organism.mutability;
|
||||||
this.organisms.push(organism);
|
this.organisms.push(organism);
|
||||||
if (organism.cells.length > this.largest_cell_count)
|
if (organism.anatomy.cells.length > this.largest_cell_count)
|
||||||
this.largest_cell_count = organism.cells.length;
|
this.largest_cell_count = organism.anatomy.cells.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
averageMutability() {
|
averageMutability() {
|
||||||
if (this.organisms.length < 1)
|
if (this.organisms.length < 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
if (Hyperparams.useGlobalMutability) {
|
||||||
|
return Hyperparams.globalMutability;
|
||||||
|
}
|
||||||
return this.total_mutability / this.organisms.length;
|
return this.total_mutability / this.organisms.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,11 +130,13 @@ class WorldEnvironment extends Environment{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reset(clear_walls=true) {
|
reset() {
|
||||||
this.organisms = [];
|
this.organisms = [];
|
||||||
this.grid_map.fillGrid(CellStates.empty);
|
this.grid_map.fillGrid(CellStates.empty);
|
||||||
this.renderer.renderFullGrid(this.grid_map.grid);
|
this.renderer.renderFullGrid(this.grid_map.grid);
|
||||||
this.total_mutability = 0;
|
this.total_mutability = 0;
|
||||||
|
this.total_ticks = 0;
|
||||||
|
FossilRecord.clear_record();
|
||||||
this.OriginOfLife();
|
this.OriginOfLife();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +144,6 @@ class WorldEnvironment extends Environment{
|
|||||||
this.renderer.cell_size = cell_size;
|
this.renderer.cell_size = cell_size;
|
||||||
this.renderer.fillShape(rows*cell_size, cols*cell_size);
|
this.renderer.fillShape(rows*cell_size, cols*cell_size);
|
||||||
this.grid_map.resize(cols, rows, cell_size);
|
this.grid_map.resize(cols, rows, cell_size);
|
||||||
this.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resizeFillWindow(cell_size) {
|
resizeFillWindow(cell_size) {
|
||||||
@@ -135,7 +152,6 @@ class WorldEnvironment extends Environment{
|
|||||||
var cols = Math.ceil(this.renderer.width / cell_size);
|
var cols = Math.ceil(this.renderer.width / cell_size);
|
||||||
var rows = Math.ceil(this.renderer.height / cell_size);
|
var rows = Math.ceil(this.renderer.height / cell_size);
|
||||||
this.grid_map.resize(cols, rows, cell_size);
|
this.grid_map.resize(cols, rows, cell_size);
|
||||||
this.reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ const Neighbors = require("./Grid/Neighbors");
|
|||||||
|
|
||||||
const Hyperparams = {
|
const Hyperparams = {
|
||||||
setDefaults: function() {
|
setDefaults: function() {
|
||||||
|
this.headless = false;
|
||||||
|
|
||||||
this.lifespanMultiplier = 100;
|
this.lifespanMultiplier = 100;
|
||||||
this.foodProdProb = 4;
|
this.foodProdProb = 4;
|
||||||
this.foodProdProbScalar = 4;
|
this.foodProdProbScalar = 4;
|
||||||
|
|||||||
96
src/Organism/Anatomy.js
Normal file
96
src/Organism/Anatomy.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
const CellStates = require("./Cell/CellStates");
|
||||||
|
const BodyCellFactory = require("./Cell/BodyCells/BodyCellFactory");
|
||||||
|
|
||||||
|
class Anatomy {
|
||||||
|
constructor(owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
this.cells = [];
|
||||||
|
this.is_producer = false;
|
||||||
|
this.is_mover = false;
|
||||||
|
this.has_eyes = false;
|
||||||
|
this.birth_distance = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
canAddCellAt(c, r) {
|
||||||
|
for (var cell of this.cells) {
|
||||||
|
if (cell.loc_col == c && cell.loc_row == r){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
addDefaultCell(state, c, r) {
|
||||||
|
var new_cell = BodyCellFactory.createDefault(this.owner, state, c, r);
|
||||||
|
this.cells.push(new_cell);
|
||||||
|
return new_cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
addRandomizedCell(state, c, r) {
|
||||||
|
if (state==CellStates.eye && !this.has_eyes) {
|
||||||
|
this.owner.brain.randomizeDecisions();
|
||||||
|
}
|
||||||
|
var new_cell = BodyCellFactory.createRandom(this.owner, state, c, r);
|
||||||
|
this.cells.push(new_cell);
|
||||||
|
return new_cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
addInheritCell(parent_cell) {
|
||||||
|
var new_cell = BodyCellFactory.createInherited(this.owner, parent_cell);
|
||||||
|
this.cells.push(new_cell);
|
||||||
|
return new_cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceCell(state, c, r, randomize=true) {
|
||||||
|
this.removeCell(c, r, true);
|
||||||
|
if (randomize) {
|
||||||
|
return this.addRandomizedCell(state, c, r);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return this.addDefaultCell(state, c, r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeCell(c, r, allow_center_removal=false) {
|
||||||
|
if (c == 0 && r == 0 && !allow_center_removal)
|
||||||
|
return false;
|
||||||
|
for (var i=0; i<this.cells.length; i++) {
|
||||||
|
var cell = this.cells[i];
|
||||||
|
if (cell.loc_col == c && cell.loc_row == r){
|
||||||
|
this.cells.splice(i, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.checkTypeChange(cell.state);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getLocalCell(c, r) {
|
||||||
|
for (var cell of this.cells) {
|
||||||
|
if (cell.loc_col == c && cell.loc_row == r){
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkTypeChange() {
|
||||||
|
this.is_producer = false;
|
||||||
|
this.is_mover = false;
|
||||||
|
this.has_eyes = false;
|
||||||
|
for (var cell of this.cells) {
|
||||||
|
if (cell.state == CellStates.producer)
|
||||||
|
this.is_producer = true;
|
||||||
|
if (cell.state == CellStates.mover)
|
||||||
|
this.is_mover = true;
|
||||||
|
if (cell.state == CellStates.eye)
|
||||||
|
this.has_eyes = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getRandomCell() {
|
||||||
|
return this.cells[Math.floor(Math.random() * this.cells.length)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Anatomy;
|
||||||
@@ -10,8 +10,8 @@ class BodyCell{
|
|||||||
this.loc_row = loc_row;
|
this.loc_row = loc_row;
|
||||||
|
|
||||||
var distance = Math.max(Math.abs(loc_row)*2 + 2, Math.abs(loc_col)*2 + 2);
|
var distance = Math.max(Math.abs(loc_row)*2 + 2, Math.abs(loc_col)*2 + 2);
|
||||||
if (this.org.birth_distance < distance) {
|
if (this.org.anatomy.birth_distance < distance) {
|
||||||
this.org.birth_distance = distance;
|
this.org.anatomy.birth_distance = distance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const Observation = require("../../Perception/Observation")
|
|||||||
class EyeCell extends BodyCell{
|
class EyeCell extends BodyCell{
|
||||||
constructor(org, loc_col, loc_row){
|
constructor(org, loc_col, loc_row){
|
||||||
super(CellStates.eye, org, loc_col, loc_row);
|
super(CellStates.eye, org, loc_col, loc_row);
|
||||||
this.org.has_eyes = true;
|
this.org.anatomy.has_eyes = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
initInherit(parent) {
|
initInherit(parent) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const BodyCell = require("./BodyCell");
|
|||||||
class MoverCell extends BodyCell{
|
class MoverCell extends BodyCell{
|
||||||
constructor(org, loc_col, loc_row){
|
constructor(org, loc_col, loc_row){
|
||||||
super(CellStates.mover, org, loc_col, loc_row);
|
super(CellStates.mover, org, loc_col, loc_row);
|
||||||
this.org.is_mover = true;
|
this.org.anatomy.is_mover = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ const Hyperparams = require("../../../Hyperparameters");
|
|||||||
class ProducerCell extends BodyCell{
|
class ProducerCell extends BodyCell{
|
||||||
constructor(org, loc_col, loc_row){
|
constructor(org, loc_col, loc_row){
|
||||||
super(CellStates.producer, org, loc_col, loc_row);
|
super(CellStates.producer, org, loc_col, loc_row);
|
||||||
this.org.is_producer = true;
|
this.org.anatomy.is_producer = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
performFunction() {
|
performFunction() {
|
||||||
if (this.org.is_mover && !Hyperparams.moversCanProduce)
|
if (this.org.anatomy.is_mover && !Hyperparams.moversCanProduce)
|
||||||
return;
|
return;
|
||||||
var env = this.org.env;
|
var env = this.org.env;
|
||||||
var prob = Hyperparams.foodProdProb;
|
var prob = Hyperparams.foodProdProb;
|
||||||
|
|||||||
@@ -63,12 +63,12 @@ class Eye extends CellState {
|
|||||||
return;
|
return;
|
||||||
var half = size/2;
|
var half = size/2;
|
||||||
var x = -(size)/8
|
var x = -(size)/8
|
||||||
var y = -half
|
var y = -half;
|
||||||
var h = size/2 + size/4;
|
var h = size/2 + size/4;
|
||||||
var w = size/4;
|
var w = size/4;
|
||||||
ctx.translate(cell.x+half, cell.y+half);
|
ctx.translate(cell.x+half, cell.y+half);
|
||||||
ctx.rotate((cell.cell_owner.getAbsoluteDirection() * 90) * Math.PI / 180);
|
ctx.rotate((cell.cell_owner.getAbsoluteDirection() * 90) * Math.PI / 180);
|
||||||
ctx.fillStyle = this.slit_color
|
ctx.fillStyle = this.slit_color;
|
||||||
ctx.fillRect(x, y, w, h);
|
ctx.fillRect(x, y, w, h);
|
||||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
const CellStates = require("../Organism/Cell/CellStates");
|
const CellStates = require("./Cell/CellStates");
|
||||||
const BodyCellFactory = require("./Cell/BodyCells/BodyCellFactory");
|
|
||||||
const Neighbors = require("../Grid/Neighbors");
|
const Neighbors = require("../Grid/Neighbors");
|
||||||
const Hyperparams = require("../Hyperparameters");
|
const Hyperparams = require("../Hyperparameters");
|
||||||
const Directions = require("./Directions");
|
const Directions = require("./Directions");
|
||||||
|
const Anatomy = require("./Anatomy");
|
||||||
const Brain = require("./Perception/Brain");
|
const Brain = require("./Perception/Brain");
|
||||||
|
const FossilRecord = require("../Stats/FossilRecord");
|
||||||
|
|
||||||
class Organism {
|
class Organism {
|
||||||
constructor(col, row, env, parent=null) {
|
constructor(col, row, env, parent=null) {
|
||||||
@@ -13,111 +14,31 @@ class Organism {
|
|||||||
this.lifetime = 0;
|
this.lifetime = 0;
|
||||||
this.food_collected = 0;
|
this.food_collected = 0;
|
||||||
this.living = true;
|
this.living = true;
|
||||||
this.cells = [];
|
this.anatomy = new Anatomy(this)
|
||||||
this.is_producer = false;
|
this.direction = Directions.down; // direction of movement
|
||||||
this.is_mover = false;
|
this.rotation = Directions.up; // direction of rotation
|
||||||
this.has_eyes = false;
|
|
||||||
this.direction = Directions.down;
|
|
||||||
this.rotation = Directions.up;
|
|
||||||
this.can_rotate = Hyperparams.moversCanRotate;
|
this.can_rotate = Hyperparams.moversCanRotate;
|
||||||
this.move_count = 0;
|
this.move_count = 0;
|
||||||
this.move_range = 4;
|
this.move_range = 4;
|
||||||
this.ignore_brain_for = 0;
|
this.ignore_brain_for = 0;
|
||||||
this.mutability = 5;
|
this.mutability = 5;
|
||||||
this.damage = 0;
|
this.damage = 0;
|
||||||
this.birth_distance = 4;
|
|
||||||
this.brain = new Brain(this);
|
this.brain = new Brain(this);
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
this.inherit(parent);
|
this.inherit(parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
canAddCellAt(c, r) {
|
|
||||||
for (var cell of this.cells) {
|
|
||||||
if (cell.loc_col == c && cell.loc_row == r){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
addDefaultCell(state, c, r) {
|
|
||||||
var new_cell = BodyCellFactory.createDefault(this, state, c, r);
|
|
||||||
this.cells.push(new_cell);
|
|
||||||
return new_cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
addRandomizedCell(state, c, r) {
|
|
||||||
if (state==CellStates.eye && !this.has_eyes) {
|
|
||||||
this.brain.randomizeDecisions();
|
|
||||||
}
|
|
||||||
var new_cell = BodyCellFactory.createRandom(this, state, c, r);
|
|
||||||
this.cells.push(new_cell);
|
|
||||||
return new_cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
addInheritCell(parent_cell) {
|
|
||||||
var new_cell = BodyCellFactory.createInherited(this, parent_cell);
|
|
||||||
this.cells.push(new_cell);
|
|
||||||
return new_cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
replaceCell(state, c, r, randomize=true) {
|
|
||||||
this.removeCell(c, r, true);
|
|
||||||
if (randomize) {
|
|
||||||
return this.addRandomizedCell(state, c, r);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return this.addDefaultCell(state, c, r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeCell(c, r, override_defense=false) {
|
|
||||||
if (c == 0 && r == 0 && !override_defense)
|
|
||||||
return false;
|
|
||||||
for (var i=0; i<this.cells.length; i++) {
|
|
||||||
var cell = this.cells[i];
|
|
||||||
if (cell.loc_col == c && cell.loc_row == r){
|
|
||||||
this.cells.splice(i, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.checkTypeChange(cell.state);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
getLocalCell(c, r) {
|
|
||||||
for (var cell of this.cells) {
|
|
||||||
if (cell.loc_col == c && cell.loc_row == r){
|
|
||||||
return cell;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
checkTypeChange() {
|
|
||||||
this.is_producer = false;
|
|
||||||
this.is_mover = false;
|
|
||||||
this.has_eyes = false;
|
|
||||||
for (var cell of this.cells) {
|
|
||||||
if (cell.state == CellStates.producer)
|
|
||||||
this.is_producer = true;
|
|
||||||
if (cell.state == CellStates.mover)
|
|
||||||
this.is_mover = true;
|
|
||||||
if (cell.state == CellStates.eye)
|
|
||||||
this.has_eyes = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inherit(parent) {
|
inherit(parent) {
|
||||||
this.move_range = parent.move_range;
|
this.move_range = parent.move_range;
|
||||||
this.mutability = parent.mutability;
|
this.mutability = parent.mutability;
|
||||||
|
this.species = parent.species;
|
||||||
// this.birth_distance = parent.birth_distance;
|
// this.birth_distance = parent.birth_distance;
|
||||||
for (var c of parent.cells){
|
for (var c of parent.anatomy.cells){
|
||||||
//deep copy parent cells
|
//deep copy parent cells
|
||||||
this.addInheritCell(c);
|
this.anatomy.addInheritCell(c);
|
||||||
}
|
}
|
||||||
if(parent.is_mover) {
|
if(parent.anatomy.is_mover) {
|
||||||
for (var i in parent.brain.decisions) {
|
for (var i in parent.brain.decisions) {
|
||||||
this.brain.decisions[i] = parent.brain.decisions[i];
|
this.brain.decisions[i] = parent.brain.decisions[i];
|
||||||
}
|
}
|
||||||
@@ -126,16 +47,16 @@ class Organism {
|
|||||||
|
|
||||||
// amount of food required before it can reproduce
|
// amount of food required before it can reproduce
|
||||||
foodNeeded() {
|
foodNeeded() {
|
||||||
return this.cells.length;
|
return this.anatomy.cells.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
lifespan() {
|
lifespan() {
|
||||||
// console.log(Hyperparams.lifespanMultiplier)
|
// console.log(Hyperparams.lifespanMultiplier)
|
||||||
return this.cells.length * Hyperparams.lifespanMultiplier;
|
return this.anatomy.cells.length * Hyperparams.lifespanMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
maxHealth() {
|
maxHealth() {
|
||||||
return this.cells.length;
|
return this.anatomy.cells.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
reproduce() {
|
reproduce() {
|
||||||
@@ -159,9 +80,10 @@ class Organism {
|
|||||||
org.mutability = 1;
|
org.mutability = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var mutated = false;
|
||||||
if (Math.random() * 100 <= prob) {
|
if (Math.random() * 100 <= prob) {
|
||||||
if (org.is_mover && Math.random() * 100 <= 10) {
|
if (org.anatomy.is_mover && Math.random() * 100 <= 10) {
|
||||||
if (org.has_eyes) {
|
if (org.anatomy.has_eyes) {
|
||||||
org.brain.mutate();
|
org.brain.mutate();
|
||||||
}
|
}
|
||||||
org.move_range += Math.floor(Math.random() * 4) - 2;
|
org.move_range += Math.floor(Math.random() * 4) - 2;
|
||||||
@@ -170,24 +92,31 @@ class Organism {
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
org.mutate();
|
mutated = org.mutate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var direction = Directions.getRandomScalar();
|
var direction = Directions.getRandomScalar();
|
||||||
var direction_c = direction[0];
|
var direction_c = direction[0];
|
||||||
var direction_r = direction[1];
|
var direction_r = direction[1];
|
||||||
var offset = (Math.floor(Math.random() * 3));
|
var offset = (Math.floor(Math.random() * 3));
|
||||||
var basemovement = this.birth_distance;
|
var basemovement = this.anatomy.birth_distance;
|
||||||
var new_c = this.c + (direction_c*basemovement) + (direction_c*offset);
|
var new_c = this.c + (direction_c*basemovement) + (direction_c*offset);
|
||||||
var new_r = this.r + (direction_r*basemovement) + (direction_r*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)){
|
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.c = new_c;
|
||||||
org.r = new_r;
|
org.r = new_r;
|
||||||
this.env.addOrganism(org);
|
this.env.addOrganism(org);
|
||||||
org.updateGrid();
|
org.updateGrid();
|
||||||
|
if (mutated) {
|
||||||
|
FossilRecord.addSpecies(org, this.species);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
org.species.addPop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.food_collected -= this.foodNeeded();
|
this.food_collected -= this.foodNeeded();
|
||||||
|
|
||||||
@@ -200,31 +129,31 @@ class Organism {
|
|||||||
// add cell
|
// add cell
|
||||||
// console.log("add cell")
|
// console.log("add cell")
|
||||||
|
|
||||||
var branch = this.cells[Math.floor(Math.random() * this.cells.length)];
|
var branch = this.anatomy.getRandomCell();
|
||||||
var state = CellStates.getRandomLivingType();//branch.state;
|
var state = CellStates.getRandomLivingType();//branch.state;
|
||||||
var growth_direction = Neighbors.all[Math.floor(Math.random() * Neighbors.all.length)]
|
var growth_direction = Neighbors.all[Math.floor(Math.random() * Neighbors.all.length)]
|
||||||
var c = branch.loc_col+growth_direction[0];
|
var c = branch.loc_col+growth_direction[0];
|
||||||
var r = branch.loc_row+growth_direction[1];
|
var r = branch.loc_row+growth_direction[1];
|
||||||
if (this.canAddCellAt(c, r)){
|
if (this.anatomy.canAddCellAt(c, r)){
|
||||||
mutated = true;
|
mutated = true;
|
||||||
this.addRandomizedCell(state, c, r);
|
this.anatomy.addRandomizedCell(state, c, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (choice <= Hyperparams.addProb + Hyperparams.changeProb){
|
else if (choice <= Hyperparams.addProb + Hyperparams.changeProb){
|
||||||
// change cell
|
// change cell
|
||||||
var cell = this.cells[Math.floor(Math.random() * this.cells.length)];
|
var cell = this.anatomy.getRandomCell();
|
||||||
var state = CellStates.getRandomLivingType();
|
var state = CellStates.getRandomLivingType();
|
||||||
// console.log("change cell", state)
|
// console.log("change cell", state)
|
||||||
this.replaceCell(state, cell.loc_col, cell.loc_row);
|
this.anatomy.replaceCell(state, cell.loc_col, cell.loc_row);
|
||||||
mutated = true;
|
mutated = true;
|
||||||
}
|
}
|
||||||
else if (choice <= Hyperparams.addProb + Hyperparams.changeProb + Hyperparams.removeProb){
|
else if (choice <= Hyperparams.addProb + Hyperparams.changeProb + Hyperparams.removeProb){
|
||||||
// remove cell
|
// remove cell
|
||||||
// console.log("remove cell")
|
// console.log("remove cell")
|
||||||
|
|
||||||
if(this.cells.length > 1) {
|
if(this.anatomy.cells.length > 1) {
|
||||||
var cell = this.cells[Math.floor(Math.random() * this.cells.length)];
|
var cell = this.anatomy.getRandomCell();
|
||||||
mutated = this.removeCell(cell.loc_col, cell.loc_row);
|
mutated = this.anatomy.removeCell(cell.loc_col, cell.loc_row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mutated;
|
return mutated;
|
||||||
@@ -237,7 +166,7 @@ class Organism {
|
|||||||
var new_c = this.c + direction_c;
|
var new_c = this.c + direction_c;
|
||||||
var new_r = this.r + direction_r;
|
var new_r = this.r + direction_r;
|
||||||
if (this.isClear(new_c, new_r)) {
|
if (this.isClear(new_c, new_r)) {
|
||||||
for (var cell of this.cells) {
|
for (var cell of this.anatomy.cells) {
|
||||||
var real_c = this.c + cell.rotatedCol(this.rotation);
|
var real_c = this.c + cell.rotatedCol(this.rotation);
|
||||||
var real_r = this.r + cell.rotatedRow(this.rotation);
|
var real_r = this.r + cell.rotatedRow(this.rotation);
|
||||||
this.env.changeCell(real_c, real_r, CellStates.empty, null);
|
this.env.changeCell(real_c, real_r, CellStates.empty, null);
|
||||||
@@ -258,7 +187,7 @@ class Organism {
|
|||||||
}
|
}
|
||||||
var new_rotation = Directions.getRandomDirection();
|
var new_rotation = Directions.getRandomDirection();
|
||||||
if(this.isClear(this.c, this.r, new_rotation)){
|
if(this.isClear(this.c, this.r, new_rotation)){
|
||||||
for (var cell of this.cells) {
|
for (var cell of this.anatomy.cells) {
|
||||||
var real_c = this.c + cell.rotatedCol(this.rotation);
|
var real_c = this.c + cell.rotatedCol(this.rotation);
|
||||||
var real_r = this.r + cell.rotatedRow(this.rotation);
|
var real_r = this.r + cell.rotatedRow(this.rotation);
|
||||||
this.env.changeCell(real_c, real_r, CellStates.empty, null);
|
this.env.changeCell(real_c, real_r, CellStates.empty, null);
|
||||||
@@ -277,7 +206,7 @@ class Organism {
|
|||||||
this.move_count = 0;
|
this.move_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// assumes either c1==c2 or r1==r2, returns true if there is a clear path from point a to b
|
// assumes either c1==c2 or r1==r2, returns true if there is a clear path from point 1 to 2
|
||||||
isStraightPath(c1, r1, c2, r2, parent){
|
isStraightPath(c1, r1, c2, r2, parent){
|
||||||
if (c1 == c2) {
|
if (c1 == c2) {
|
||||||
if (r1 > r2){
|
if (r1 > r2){
|
||||||
@@ -314,11 +243,12 @@ class Organism {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isClear(col, row, rotation=this.rotation, ignore_armor=false) {
|
isClear(col, row, rotation=this.rotation, ignore_armor=false) {
|
||||||
for(var loccell of this.cells) {
|
for(var loccell of this.anatomy.cells) {
|
||||||
var cell = this.getRealCell(loccell, col, row, rotation);
|
var cell = this.getRealCell(loccell, col, row, rotation);
|
||||||
if(cell==null) {
|
if (cell==null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// console.log(cell.owner == this)
|
||||||
if (cell.owner==this || cell.state==CellStates.empty || (!Hyperparams.foodBlocksReproduction && cell.state==CellStates.food) || (ignore_armor && loccell.state==CellStates.armor && cell.state==CellStates.food)){
|
if (cell.owner==this || cell.state==CellStates.empty || (!Hyperparams.foodBlocksReproduction && cell.state==CellStates.food) || (ignore_armor && loccell.state==CellStates.armor && cell.state==CellStates.food)){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -335,16 +265,17 @@ class Organism {
|
|||||||
}
|
}
|
||||||
|
|
||||||
die() {
|
die() {
|
||||||
for (var cell of this.cells) {
|
for (var cell of this.anatomy.cells) {
|
||||||
var real_c = this.c + cell.rotatedCol(this.rotation);
|
var real_c = this.c + cell.rotatedCol(this.rotation);
|
||||||
var real_r = this.r + cell.rotatedRow(this.rotation);
|
var real_r = this.r + cell.rotatedRow(this.rotation);
|
||||||
this.env.changeCell(real_c, real_r, CellStates.food, null);
|
this.env.changeCell(real_c, real_r, CellStates.food, null);
|
||||||
}
|
}
|
||||||
|
this.species.decreasePop();
|
||||||
this.living = false;
|
this.living = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateGrid() {
|
updateGrid() {
|
||||||
for (var cell of this.cells) {
|
for (var cell of this.anatomy.cells) {
|
||||||
var real_c = this.c + cell.rotatedCol(this.rotation);
|
var real_c = this.c + cell.rotatedCol(this.rotation);
|
||||||
var real_r = this.r + cell.rotatedRow(this.rotation);
|
var real_r = this.r + cell.rotatedRow(this.rotation);
|
||||||
this.env.changeCell(real_c, real_r, cell.state, cell);
|
this.env.changeCell(real_c, real_r, cell.state, cell);
|
||||||
@@ -360,13 +291,13 @@ class Organism {
|
|||||||
if (this.food_collected >= this.foodNeeded()) {
|
if (this.food_collected >= this.foodNeeded()) {
|
||||||
this.reproduce();
|
this.reproduce();
|
||||||
}
|
}
|
||||||
for (var cell of this.cells) {
|
for (var cell of this.anatomy.cells) {
|
||||||
cell.performFunction();
|
cell.performFunction();
|
||||||
if (!this.living)
|
if (!this.living)
|
||||||
return this.living
|
return this.living
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.is_mover) {
|
if (this.anatomy.is_mover) {
|
||||||
this.move_count++;
|
this.move_count++;
|
||||||
var changed_dir = false;
|
var changed_dir = false;
|
||||||
if (this.ignore_brain_for == 0){
|
if (this.ignore_brain_for == 0){
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderOrganism(org) {
|
renderOrganism(org) {
|
||||||
for(var org_cell of org.cells) {
|
for(var org_cell of org.anatomy.cells) {
|
||||||
var cell = org.getRealCell(org_cell);
|
var cell = org.getRealCell(org_cell);
|
||||||
this.renderCell(cell);
|
this.renderCell(cell);
|
||||||
}
|
}
|
||||||
@@ -75,7 +75,7 @@ class Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
highlightOrganism(org) {
|
highlightOrganism(org) {
|
||||||
for(var org_cell of org.cells) {
|
for(var org_cell of org.anatomy.cells) {
|
||||||
var cell = org.getRealCell(org_cell);
|
var cell = org.getRealCell(org_cell);
|
||||||
this.cells_to_highlight.add(cell);
|
this.cells_to_highlight.add(cell);
|
||||||
}
|
}
|
||||||
|
|||||||
55
src/Stats/Charts/CellsChart.js
Normal file
55
src/Stats/Charts/CellsChart.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
const CellStates = require("../../Organism/Cell/CellStates");
|
||||||
|
const FossilRecord = require("../FossilRecord");
|
||||||
|
const ChartController = require("./ChartController");
|
||||||
|
|
||||||
|
class CellsChart extends ChartController {
|
||||||
|
constructor() {
|
||||||
|
super("Organism Size / Composition",
|
||||||
|
"Avg. Number of Cells per Organism",
|
||||||
|
"Note: to maintain efficiency, species with very small populations are discarded when collecting cell statistics.");
|
||||||
|
}
|
||||||
|
|
||||||
|
setData() {
|
||||||
|
this.clear();
|
||||||
|
//this.mouth, this.producer, this.mover, this.killer, this.armor, this.eye
|
||||||
|
this.data.push({
|
||||||
|
type: "line",
|
||||||
|
markerType: "none",
|
||||||
|
color: 'black',
|
||||||
|
showInLegend: true,
|
||||||
|
name: "pop1",
|
||||||
|
legendText: "Avg. organism size",
|
||||||
|
dataPoints: []
|
||||||
|
}
|
||||||
|
);
|
||||||
|
for (var c of CellStates.living) {
|
||||||
|
this.data.push({
|
||||||
|
type: "line",
|
||||||
|
markerType: "none",
|
||||||
|
color: c.color,
|
||||||
|
showInLegend: true,
|
||||||
|
name: c.name,
|
||||||
|
legendText: "Avg. " + c.name + " cells",
|
||||||
|
dataPoints: []
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.addAllDataPoints();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
addDataPoint(i) {
|
||||||
|
var t = FossilRecord.tick_record[i];
|
||||||
|
var p = FossilRecord.av_cells[i];
|
||||||
|
this.data[0].dataPoints.push({x:t, y:p});
|
||||||
|
var j=1;
|
||||||
|
for (var name in FossilRecord.av_cell_counts[i]) {
|
||||||
|
var count = FossilRecord.av_cell_counts[i][name];
|
||||||
|
this.data[j].dataPoints.push({x:t,y:count})
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CellsChart;
|
||||||
84
src/Stats/Charts/ChartController.js
Normal file
84
src/Stats/Charts/ChartController.js
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
const FossilRecord = require("../FossilRecord");
|
||||||
|
|
||||||
|
class ChartController {
|
||||||
|
constructor(title, y_axis="", note="") {
|
||||||
|
this.data = [];
|
||||||
|
this.chart = new CanvasJS.Chart("chartContainer", {
|
||||||
|
zoomEnabled: true,
|
||||||
|
title:{
|
||||||
|
text: title
|
||||||
|
},
|
||||||
|
axisX:{
|
||||||
|
title: "Ticks",
|
||||||
|
minimum: 0,
|
||||||
|
},
|
||||||
|
axisY:{
|
||||||
|
title: y_axis,
|
||||||
|
minimum: 0,
|
||||||
|
},
|
||||||
|
data: this.data
|
||||||
|
});
|
||||||
|
this.chart.render();
|
||||||
|
$('#chart-note').text(note);
|
||||||
|
}
|
||||||
|
|
||||||
|
setData() {
|
||||||
|
alert("Must override updateData!");
|
||||||
|
}
|
||||||
|
|
||||||
|
setMinimum() {
|
||||||
|
var min = 0;
|
||||||
|
if (this.data[0].dataPoints != [])
|
||||||
|
min = this.data[0].dataPoints[0].x;
|
||||||
|
this.chart.options.axisX.minimum = min;
|
||||||
|
}
|
||||||
|
|
||||||
|
addAllDataPoints(){
|
||||||
|
for (var i in FossilRecord.tick_record) {
|
||||||
|
this.addDataPoint(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
this.setMinimum();
|
||||||
|
this.chart.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateData() {
|
||||||
|
var r_len = FossilRecord.tick_record.length;
|
||||||
|
var newest_t = -1;
|
||||||
|
var oldest_t = 0;
|
||||||
|
if (this.data[0].dataPoints.length>0) {
|
||||||
|
newest_t = this.data[0].dataPoints[this.data[0].dataPoints.length-1].x;
|
||||||
|
newest_t = this.data[0].dataPoints[0].x;
|
||||||
|
}
|
||||||
|
if (newest_t < FossilRecord.tick_record[r_len-1]) {
|
||||||
|
this.addNewest();
|
||||||
|
}
|
||||||
|
if (oldest_t < FossilRecord.tick_record[0]) {
|
||||||
|
this.removeOldest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addNewest() {
|
||||||
|
var i = FossilRecord.tick_record.length-1;
|
||||||
|
this.addDataPoint(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
addDataPoint(i) {
|
||||||
|
alert("Must override addDataPoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
removeOldest() {
|
||||||
|
for (var dps of this.data) {
|
||||||
|
dps.dataPoints.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.data.length = 0;
|
||||||
|
this.chart.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ChartController;
|
||||||
31
src/Stats/Charts/MutationChart.js
Normal file
31
src/Stats/Charts/MutationChart.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
const FossilRecord = require("../FossilRecord");
|
||||||
|
const ChartController = require("./ChartController");
|
||||||
|
|
||||||
|
class MutationChart extends ChartController {
|
||||||
|
constructor() {
|
||||||
|
super("Mutation Rate");
|
||||||
|
}
|
||||||
|
|
||||||
|
setData() {
|
||||||
|
this.clear();
|
||||||
|
this.data.push({
|
||||||
|
type: "line",
|
||||||
|
markerType: "none",
|
||||||
|
color: 'black',
|
||||||
|
showInLegend: true,
|
||||||
|
name: "pop1",
|
||||||
|
legendText: "Average Mutation Rate",
|
||||||
|
dataPoints: []
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.addAllDataPoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
addDataPoint(i) {
|
||||||
|
var t = FossilRecord.tick_record[i];
|
||||||
|
var p = FossilRecord.av_mut_rates[i];
|
||||||
|
this.data[0].dataPoints.push({x:t, y:p});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = MutationChart;
|
||||||
31
src/Stats/Charts/PopulationChart.js
Normal file
31
src/Stats/Charts/PopulationChart.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
const FossilRecord = require("../FossilRecord");
|
||||||
|
const ChartController = require("./ChartController");
|
||||||
|
|
||||||
|
class PopulationChart extends ChartController {
|
||||||
|
constructor() {
|
||||||
|
super("Population");
|
||||||
|
}
|
||||||
|
|
||||||
|
setData() {
|
||||||
|
this.clear();
|
||||||
|
this.data.push({
|
||||||
|
type: "line",
|
||||||
|
markerType: "none",
|
||||||
|
color: 'black',
|
||||||
|
showInLegend: true,
|
||||||
|
name: "pop1",
|
||||||
|
legendText: "Total Population",
|
||||||
|
dataPoints: []
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.addAllDataPoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
addDataPoint(i) {
|
||||||
|
var t = FossilRecord.tick_record[i];
|
||||||
|
var p = FossilRecord.pop_counts[i];
|
||||||
|
this.data[0].dataPoints.push({x:t, y:p});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = PopulationChart;
|
||||||
31
src/Stats/Charts/SpeciesChart.js
Normal file
31
src/Stats/Charts/SpeciesChart.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
const FossilRecord = require("../FossilRecord");
|
||||||
|
const ChartController = require("./ChartController");
|
||||||
|
|
||||||
|
class SpeciesChart extends ChartController {
|
||||||
|
constructor() {
|
||||||
|
super("Species");
|
||||||
|
}
|
||||||
|
|
||||||
|
setData() {
|
||||||
|
this.clear();
|
||||||
|
this.data.push({
|
||||||
|
type: "line",
|
||||||
|
markerType: "none",
|
||||||
|
color: 'black',
|
||||||
|
showInLegend: true,
|
||||||
|
name: "spec",
|
||||||
|
legendText: "Number of Species",
|
||||||
|
dataPoints: []
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.addAllDataPoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
addDataPoint(i) {
|
||||||
|
var t = FossilRecord.tick_record[i];
|
||||||
|
var p = FossilRecord.species_counts[i];
|
||||||
|
this.data[0].dataPoints.push({x:t, y:p});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = SpeciesChart;
|
||||||
133
src/Stats/FossilRecord.js
Normal file
133
src/Stats/FossilRecord.js
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
const CellStates = require("../Organism/Cell/CellStates");
|
||||||
|
const Species = require("./Species");
|
||||||
|
|
||||||
|
const FossilRecord = {
|
||||||
|
init: function(){
|
||||||
|
this.extant_species = [];
|
||||||
|
this.extinct_species = [];
|
||||||
|
|
||||||
|
// if an organism has fewer than this cumulative pop, discard them on extinction
|
||||||
|
this.min_discard = 10;
|
||||||
|
|
||||||
|
this.record_size_limit = 500; // store this many data points
|
||||||
|
},
|
||||||
|
|
||||||
|
setEnv: function(env) {
|
||||||
|
this.env = env;
|
||||||
|
this.setData();
|
||||||
|
},
|
||||||
|
|
||||||
|
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;
|
||||||
|
return new_species;
|
||||||
|
},
|
||||||
|
|
||||||
|
addSpeciesObj: function(species) {
|
||||||
|
// console.log("Adding Species")
|
||||||
|
this.extant_species.push(species);
|
||||||
|
return species;
|
||||||
|
},
|
||||||
|
|
||||||
|
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];
|
||||||
|
if (s == species) {
|
||||||
|
this.extant_species.splice(i, 1);
|
||||||
|
if (species.cumulative_pop < this.min_pop) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
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];
|
||||||
|
if (s == species) {
|
||||||
|
this.extinct_species.splice(i, 1);
|
||||||
|
this.extant_species.push(species);
|
||||||
|
species.extinct = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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()];
|
||||||
|
},
|
||||||
|
|
||||||
|
updateData() {
|
||||||
|
var tick = this.env.total_ticks;
|
||||||
|
this.tick_record.push(tick);
|
||||||
|
this.pop_counts.push(this.env.organisms.length);
|
||||||
|
this.species_counts.push(this.extant_species.length);
|
||||||
|
this.av_mut_rates.push(this.env.averageMutability());
|
||||||
|
this.calcCellCountAverages();
|
||||||
|
|
||||||
|
if (this.tick_record.length > this.record_size_limit) {
|
||||||
|
this.tick_record.shift();
|
||||||
|
this.pop_counts.shift();
|
||||||
|
this.species_counts.shift();
|
||||||
|
this.av_mut_rates.shift();
|
||||||
|
this.av_cells.shift();
|
||||||
|
this.av_cell_counts.shift();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
calcCellCountAverages() {
|
||||||
|
var total_org = 0;
|
||||||
|
var cell_counts = {};
|
||||||
|
for (let c of CellStates.living) {
|
||||||
|
cell_counts[c.name] = 0;
|
||||||
|
}
|
||||||
|
var first=true;
|
||||||
|
for (let s of this.extant_species) {
|
||||||
|
if (s.cumulative_pop < this.min_discard && !first){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (let name in s.cell_counts) {
|
||||||
|
cell_counts[name] += s.cell_counts[name] * s.population;
|
||||||
|
}
|
||||||
|
total_org += s.population;
|
||||||
|
first=false;
|
||||||
|
}
|
||||||
|
if (total_org == 0)
|
||||||
|
return cell_counts;
|
||||||
|
|
||||||
|
var total_cells = 0;
|
||||||
|
for (let c in cell_counts) {
|
||||||
|
total_cells += cell_counts[c];
|
||||||
|
cell_counts[c] /= total_org;
|
||||||
|
}
|
||||||
|
this.av_cells.push(total_cells / total_org);
|
||||||
|
this.av_cell_counts.push(cell_counts);
|
||||||
|
},
|
||||||
|
|
||||||
|
clear_record: function() {
|
||||||
|
this.extant_species = [];
|
||||||
|
this.extinct_species = [];
|
||||||
|
this.setData();
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FossilRecord.init();
|
||||||
|
|
||||||
|
module.exports = FossilRecord;
|
||||||
52
src/Stats/Species.js
Normal file
52
src/Stats/Species.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
const CellStates = require("../Organism/Cell/CellStates");
|
||||||
|
|
||||||
|
class Species {
|
||||||
|
constructor(anatomy, ancestor, start_tick) {
|
||||||
|
this.anatomy = anatomy;
|
||||||
|
this.ancestor = ancestor;
|
||||||
|
this.population = 1;
|
||||||
|
this.cumulative_pop = 1;
|
||||||
|
this.start_tick = start_tick;
|
||||||
|
this.end_tick = -1;
|
||||||
|
this.color = Math.floor(Math.random()*16777215).toString(16);
|
||||||
|
if (ancestor != null) {
|
||||||
|
// needs to be reworked, maybe removed
|
||||||
|
var mutator = Math.floor(Math.random()*16777215)-8000000;
|
||||||
|
this.color = (mutator + parseInt(ancestor.color, 16)).toString(16);
|
||||||
|
}
|
||||||
|
this.name = '_' + Math.random().toString(36).substr(2, 9);
|
||||||
|
this.extinct = false;
|
||||||
|
this.calcAnatomyDetails();
|
||||||
|
}
|
||||||
|
|
||||||
|
calcAnatomyDetails() {
|
||||||
|
var cell_counts = {};
|
||||||
|
for (let c of CellStates.living) {
|
||||||
|
cell_counts[c.name] = 0;
|
||||||
|
}
|
||||||
|
for (let cell of this.anatomy.cells) {
|
||||||
|
cell_counts[cell.state.name]+=1;
|
||||||
|
}
|
||||||
|
this.cell_counts=cell_counts;
|
||||||
|
}
|
||||||
|
|
||||||
|
addPop() {
|
||||||
|
this.population++;
|
||||||
|
this.cumulative_pop++;
|
||||||
|
}
|
||||||
|
|
||||||
|
decreasePop() {
|
||||||
|
this.population--;
|
||||||
|
if (this.population <= 0) {
|
||||||
|
this.extinct = true;
|
||||||
|
const FossilRecord = require("./FossilRecord");
|
||||||
|
FossilRecord.fossilize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lifespan() {
|
||||||
|
return this.end_tick - this.start_tick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Species;
|
||||||
66
src/Stats/StatsPanel.js
Normal file
66
src/Stats/StatsPanel.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
const PopulationChart = require("./Charts/PopulationChart");
|
||||||
|
const SpeciesChart = require("./Charts/SpeciesChart");
|
||||||
|
const MutationChart = require("./Charts/MutationChart");
|
||||||
|
const CellsChart = require("./Charts/CellsChart");
|
||||||
|
const FossilRecord = require("./FossilRecord");
|
||||||
|
|
||||||
|
|
||||||
|
const ChartSelections = [PopulationChart, SpeciesChart, CellsChart, MutationChart];
|
||||||
|
|
||||||
|
class StatsPanel {
|
||||||
|
constructor(env) {
|
||||||
|
this.defineControls();
|
||||||
|
this.chart_selection = 0;
|
||||||
|
this.setChart();
|
||||||
|
this.env = env;
|
||||||
|
this.last_reset_count=env.reset_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
setChart(selection=this.chart_selection) {
|
||||||
|
this.chart_controller = new ChartSelections[selection]();
|
||||||
|
this.chart_controller.setData();
|
||||||
|
this.chart_controller.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
startAutoRender() {
|
||||||
|
this.setChart();
|
||||||
|
this.render_loop = setInterval(function(){this.updateChart();}.bind(this), 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopAutoRender() {
|
||||||
|
clearInterval(this.render_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
defineControls() {
|
||||||
|
$('#chart-option').change ( function() {
|
||||||
|
this.chart_selection = $("#chart-option")[0].selectedIndex;
|
||||||
|
this.setChart();
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateChart() {
|
||||||
|
if (this.last_reset_count < this.env.reset_count){
|
||||||
|
this.reset()
|
||||||
|
}
|
||||||
|
this.last_reset_count = this.env.reset_count;
|
||||||
|
this.chart_controller.updateData();
|
||||||
|
this.chart_controller.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDetails() {
|
||||||
|
var org_count = this.env.organisms.length;
|
||||||
|
$('#org-count').text("Total Population: " + org_count);
|
||||||
|
$('#species-count').text("Number of Species: " + FossilRecord.extant_species.length);
|
||||||
|
$('#largest-org').text("Largest Organism Ever: " + this.env.largest_cell_count + " cells");
|
||||||
|
$('#avg-mut').text("Average Mutation Rate: " + Math.round(this.env.averageMutability() * 100) / 100);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.setChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = StatsPanel;
|
||||||
Reference in New Issue
Block a user