Randomized Creature Generation
Adds a random organism generator that is accessible through the editor control panel. This generator gives the user to generate an entire world of random organisms for selection to act upon.
This commit is contained in:
3
dist/css/style.css
vendored
3
dist/css/style.css
vendored
@@ -169,6 +169,9 @@ button:hover{
|
|||||||
.edit-mode-btn#drag-view {
|
.edit-mode-btn#drag-view {
|
||||||
background-color: #81d2c7;
|
background-color: #81d2c7;
|
||||||
}
|
}
|
||||||
|
.randomize-button {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
#clear-walls {
|
#clear-walls {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|||||||
19
dist/index.html
vendored
19
dist/index.html
vendored
@@ -100,6 +100,7 @@
|
|||||||
<div class='editor-buttons'>
|
<div class='editor-buttons'>
|
||||||
<button class="edit-mode-btn" id="select" title="Select organism from world"><i class="fa fa-hand-pointer-o"></i></button>
|
<button class="edit-mode-btn" id="select" title="Select organism from world"><i class="fa fa-hand-pointer-o"></i></button>
|
||||||
<button class="edit-mode-btn" id="edit" title="Edit organism"><i class="fa fa-pencil"></i></button>
|
<button class="edit-mode-btn" id="edit" title="Edit organism"><i class="fa fa-pencil"></i></button>
|
||||||
|
<button class="edit-mode-btn" id='randomize' title="Randomize"><i class="fa fa-random"></i></button>
|
||||||
<button class="edit-mode-btn" id="drop-org" title="Drop organism in world"><i class="fa fa-plus"></i></button>
|
<button class="edit-mode-btn" id="drop-org" title="Drop organism in world"><i class="fa fa-plus"></i></button>
|
||||||
</div>
|
</div>
|
||||||
<div id='editor-env'>
|
<div id='editor-env'>
|
||||||
@@ -169,6 +170,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id='randomize-organism-details' style="display:none;">
|
||||||
|
<h3>Generate Random Organism</h3>
|
||||||
|
|
||||||
|
<label for='random-width' title='The maximum width of the generated organism'>Max Width:</label>
|
||||||
|
<input type='range' id='random-width' min="1" max="7" value="2" step="1">
|
||||||
|
<span id='random-width-display'>5</span>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<label for='cell-spawn-chance' title='The chance a cell will spawn in the innermost layer (becomes less likely near the edges)'>Cell Spawn Chance:</label>
|
||||||
|
<input type='range' id='cell-spawn-chance' min="0.01" max="1" value="0.75" step='0.001'>
|
||||||
|
<span id='spawn-chance-display'>75%</span>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<button class='randomize-button' id='generate-random' title="Generate a random organism in the editor window.">Generate</button>
|
||||||
|
<br />
|
||||||
|
<button class='randomize-button' id='create-random-world' title="Generate hundreds of random organisms in the world.">Create Random World</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id='hyperparameters' class='tab'>
|
<div id='hyperparameters' class='tab'>
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ const Modes = {
|
|||||||
Select: 4,
|
Select: 4,
|
||||||
Edit: 5,
|
Edit: 5,
|
||||||
Clone: 6,
|
Clone: 6,
|
||||||
Drag: 7
|
Drag: 7,
|
||||||
|
Randomize: 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Modes;
|
module.exports = Modes;
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
const Hyperparams = require("../Hyperparameters");
|
const Hyperparams = require("../Hyperparameters");
|
||||||
const Modes = require("./ControlModes");
|
const Modes = require("./ControlModes");
|
||||||
const StatsPanel = require("../Stats/StatsPanel");
|
const StatsPanel = require("../Stats/StatsPanel");
|
||||||
|
const RandomOrganismGenerator = require("../Organism/RandomOrganismGenerator")
|
||||||
|
|
||||||
class ControlPanel {
|
class ControlPanel {
|
||||||
constructor(engine) {
|
constructor(engine) {
|
||||||
@@ -21,7 +22,7 @@ class ControlPanel {
|
|||||||
this.stats_panel = new StatsPanel(this.engine.env);
|
this.stats_panel = new StatsPanel(this.engine.env);
|
||||||
this.headless_opacity = 1;
|
this.headless_opacity = 1;
|
||||||
this.opacity_change_rate = -0.8;
|
this.opacity_change_rate = -0.8;
|
||||||
this.paused=false;
|
//this.paused=false;
|
||||||
}
|
}
|
||||||
|
|
||||||
defineMinMaxControls(){
|
defineMinMaxControls(){
|
||||||
@@ -84,17 +85,12 @@ class ControlPanel {
|
|||||||
}
|
}
|
||||||
$('#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");
|
// toggle pause
|
||||||
$('.pause-button').find("i").toggleClass("fa fa-play");
|
this.setPaused(this.engine.running);
|
||||||
this.paused = !this.paused;
|
|
||||||
if (this.engine.running) {
|
|
||||||
this.engine.stop();
|
|
||||||
}
|
|
||||||
else if (!this.engine.running){
|
|
||||||
this.engine.start(this.fps);
|
|
||||||
}
|
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
$('.headless').click(function() {
|
$('.headless').click(function() {
|
||||||
$('.headless').find("i").toggleClass("fa fa-eye");
|
$('.headless').find("i").toggleClass("fa fa-eye");
|
||||||
$('.headless').find("i").toggleClass("fa fa-eye-slash");
|
$('.headless').find("i").toggleClass("fa fa-eye-slash");
|
||||||
@@ -263,6 +259,9 @@ class ControlPanel {
|
|||||||
self.setMode(Modes.Edit);
|
self.setMode(Modes.Edit);
|
||||||
self.editor_controller.setEditorPanel();
|
self.editor_controller.setEditorPanel();
|
||||||
break;
|
break;
|
||||||
|
case "randomize":
|
||||||
|
self.setMode(Modes.Randomize);
|
||||||
|
self.editor_controller.setRandomizePanel();
|
||||||
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();
|
||||||
@@ -298,6 +297,24 @@ class ControlPanel {
|
|||||||
this.engine.organism_editor.clear();
|
this.engine.organism_editor.clear();
|
||||||
this.editor_controller.setEditorPanel();
|
this.editor_controller.setEditorPanel();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
document.getElementById("random-width").addEventListener('input', function() {
|
||||||
|
var width = 2 * this.value + 1;
|
||||||
|
$('#random-width-display').text(width);
|
||||||
|
RandomOrganismGenerator.organismLayers = this.value;
|
||||||
|
});
|
||||||
|
document.getElementById("cell-spawn-chance").addEventListener("input", function() {
|
||||||
|
var value = parseFloat(this.value);
|
||||||
|
$('#spawn-chance-display').text((value * 100).toFixed(1) + "%");
|
||||||
|
RandomOrganismGenerator.cellSpawnChance = value;
|
||||||
|
});
|
||||||
|
$('#generate-random').click( function() {
|
||||||
|
this.engine.organism_editor.createRandom();
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
$('#create-random-world').click( function() {
|
||||||
|
this.setPaused(true);
|
||||||
|
this.engine.organism_editor.createRandomWorld(this.engine.env);
|
||||||
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
defineChallenges() {
|
defineChallenges() {
|
||||||
@@ -307,6 +324,20 @@ class ControlPanel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPaused(paused) {
|
||||||
|
|
||||||
|
if (paused) {
|
||||||
|
$('.pause-button').find("i").removeClass("fa-pause");
|
||||||
|
$('.pause-button').find("i").addClass("fa-play");
|
||||||
|
this.engine.stop();
|
||||||
|
}
|
||||||
|
else if (!paused) {
|
||||||
|
$('.pause-button').find("i").addClass("fa-pause");
|
||||||
|
$('.pause-button').find("i").removeClass("fa-play");
|
||||||
|
this.engine.start(this.fps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setMode(mode) {
|
setMode(mode) {
|
||||||
this.env_controller.mode = mode;
|
this.env_controller.mode = mode;
|
||||||
this.editor_controller.mode = mode;
|
this.editor_controller.mode = mode;
|
||||||
@@ -325,7 +356,7 @@ class ControlPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateHeadlessIcon(delta_time) {
|
updateHeadlessIcon(delta_time) {
|
||||||
if (this.paused)
|
if (this.engine.running)
|
||||||
return;
|
return;
|
||||||
var op = this.headless_opacity + (this.opacity_change_rate*delta_time/1000);
|
var op = this.headless_opacity + (this.opacity_change_rate*delta_time/1000);
|
||||||
if (op <= 0.4){
|
if (op <= 0.4){
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ class EditorController extends CanvasController{
|
|||||||
clearDetailsPanel() {
|
clearDetailsPanel() {
|
||||||
$('#organism-details').css('display', 'none');
|
$('#organism-details').css('display', 'none');
|
||||||
$('#edit-organism-details').css('display', 'none');
|
$('#edit-organism-details').css('display', 'none');
|
||||||
|
$('#randomize-organism-details').css('display', 'none');
|
||||||
}
|
}
|
||||||
|
|
||||||
setDetailsPanel() {
|
setDetailsPanel() {
|
||||||
@@ -193,6 +194,11 @@ class EditorController extends CanvasController{
|
|||||||
var reaction = this.env.organism.brain.decisions[name];
|
var reaction = this.env.organism.brain.decisions[name];
|
||||||
$('#reaction-edit').val(reaction);
|
$('#reaction-edit').val(reaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setRandomizePanel() {
|
||||||
|
this.clearDetailsPanel();
|
||||||
|
$('#randomize-organism-details').css('display', 'block');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = EditorController;
|
module.exports = EditorController;
|
||||||
|
|||||||
@@ -122,21 +122,7 @@ 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);
|
this.dropOrganism(this.org_to_clone, this.mouse_c, this.mouse_r);
|
||||||
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)){
|
|
||||||
this.env.addOrganism(new_org);
|
|
||||||
new_org.species.addPop();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Modes.Drag:
|
case Modes.Drag:
|
||||||
@@ -151,6 +137,26 @@ class EnvironmentController extends CanvasController{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dropOrganism(organism, col, row) {
|
||||||
|
|
||||||
|
// close the organism and drop it in the world
|
||||||
|
var new_org = new Organism(col, row, this.env, organism);
|
||||||
|
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)){
|
||||||
|
this.env.addOrganism(new_org);
|
||||||
|
new_org.species.addPop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dropCellType(col, row, state, killBlocking=false) {
|
dropCellType(col, row, state, killBlocking=false) {
|
||||||
for (var loc of Neighbors.allSelf){
|
for (var loc of Neighbors.allSelf){
|
||||||
var c=col + loc[0];
|
var c=col + loc[0];
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ 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 Species = require('../Stats/Species');
|
const Species = require('../Stats/Species');
|
||||||
|
const RandomOrganismGenerator = require('../Organism/RandomOrganismGenerator')
|
||||||
|
|
||||||
class OrganismEditor extends Environment{
|
class OrganismEditor extends Environment{
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -87,6 +88,38 @@ class OrganismEditor extends Environment{
|
|||||||
this.organism.updateGrid();
|
this.organism.updateGrid();
|
||||||
this.organism.species = new Species(this.organism.anatomy, null, 0);
|
this.organism.species = new Species(this.organism.anatomy, null, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createRandom() {
|
||||||
|
|
||||||
|
this.grid_map.fillGrid(CellStates.empty);
|
||||||
|
|
||||||
|
this.organism = RandomOrganismGenerator.generate(this);
|
||||||
|
this.organism.updateGrid();
|
||||||
|
this.organism.species = new Species(this.organism.anatomy, null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
createRandomWorld(worldEnvironment) {
|
||||||
|
|
||||||
|
worldEnvironment.clear();
|
||||||
|
|
||||||
|
var numOrganismCols = Math.floor(worldEnvironment.grid_map.cols / this.grid_map.cols);
|
||||||
|
var numOrganismRows = Math.floor(worldEnvironment.grid_map.rows / this.grid_map.rows);
|
||||||
|
var center = this.grid_map.getCenter();
|
||||||
|
|
||||||
|
for (var x = 0; x < numOrganismCols; x++) {
|
||||||
|
for (var y = 0; y < numOrganismRows; y++) {
|
||||||
|
|
||||||
|
var newOrganism = RandomOrganismGenerator.generate(this);
|
||||||
|
//newOrganism.updateGrid();
|
||||||
|
newOrganism.species = new Species(newOrganism.anatomy, null, 0);
|
||||||
|
|
||||||
|
var col = x * this.grid_map.cols + center[0];
|
||||||
|
var row = y * this.grid_map.rows + center[1];
|
||||||
|
worldEnvironment.controller.add_new_species = true;
|
||||||
|
worldEnvironment.controller.dropOrganism(newOrganism, col, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = OrganismEditor;
|
module.exports = OrganismEditor;
|
||||||
@@ -131,13 +131,17 @@ class WorldEnvironment extends Environment{
|
|||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
|
this.clear();
|
||||||
|
this.OriginOfLife();
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
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;
|
this.total_ticks = 0;
|
||||||
FossilRecord.clear_record();
|
FossilRecord.clear_record();
|
||||||
this.OriginOfLife();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resizeGridColRow(cell_size, cols, rows) {
|
resizeGridColRow(cell_size, cols, rows) {
|
||||||
|
|||||||
@@ -91,6 +91,22 @@ class Anatomy {
|
|||||||
getRandomCell() {
|
getRandomCell() {
|
||||||
return this.cells[Math.floor(Math.random() * this.cells.length)];
|
return this.cells[Math.floor(Math.random() * this.cells.length)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNeighborsOfCell(col, row) {
|
||||||
|
|
||||||
|
var neighbors = [];
|
||||||
|
|
||||||
|
for (var x = -1; x <= 1; x++) {
|
||||||
|
for (var y = -1; y <= 1; y++) {
|
||||||
|
|
||||||
|
var neighbor = this.getLocalCell(col + x, row + y);
|
||||||
|
if (neighbor)
|
||||||
|
neighbors.push(neighbor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return neighbors;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Anatomy;
|
module.exports = Anatomy;
|
||||||
@@ -95,6 +95,7 @@ const CellStates = {
|
|||||||
return this.living[Math.floor(Math.random() * this.living.length)];
|
return this.living[Math.floor(Math.random() * this.living.length)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CellStates.defineLists();
|
CellStates.defineLists();
|
||||||
|
|
||||||
module.exports = CellStates;
|
module.exports = CellStates;
|
||||||
|
|||||||
@@ -79,4 +79,6 @@ class Brain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Brain.Decision = Decision;
|
||||||
|
|
||||||
module.exports = Brain;
|
module.exports = Brain;
|
||||||
77
src/Organism/RandomOrganismGenerator.js
Normal file
77
src/Organism/RandomOrganismGenerator.js
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
const CellStates = require("./Cell/CellStates");
|
||||||
|
const Organism = require("./Organism");
|
||||||
|
const Brain = require("./Perception/Brain")
|
||||||
|
|
||||||
|
class RandomOrganismGenerator {
|
||||||
|
|
||||||
|
static generate(env) {
|
||||||
|
|
||||||
|
var center = env.grid_map.getCenter();
|
||||||
|
var organism = new Organism(center[0], center[1], env, null);
|
||||||
|
organism.anatomy.addDefaultCell(CellStates.mouth, 0, 0);
|
||||||
|
|
||||||
|
var outermostLayer = RandomOrganismGenerator.organismLayers;
|
||||||
|
var x, y;
|
||||||
|
|
||||||
|
// iterate from center to edge of organism
|
||||||
|
// layer 0 is the central cell of the organism
|
||||||
|
for (var layer = 1; layer <= outermostLayer; layer++) {
|
||||||
|
|
||||||
|
var someCellSpawned = false;
|
||||||
|
var spawnChance = RandomOrganismGenerator.cellSpawnChance * 1 - ((layer - 1) / outermostLayer);
|
||||||
|
|
||||||
|
// top
|
||||||
|
y = -layer;
|
||||||
|
for (x = -layer; x <= layer; x++)
|
||||||
|
someCellSpawned = RandomOrganismGenerator.trySpawnCell(organism, x, y, spawnChance);
|
||||||
|
|
||||||
|
// bottom
|
||||||
|
y = layer;
|
||||||
|
for (x = -layer; x <= layer; x++)
|
||||||
|
someCellSpawned = RandomOrganismGenerator.trySpawnCell(organism, x, y, spawnChance);
|
||||||
|
|
||||||
|
// left
|
||||||
|
x = -layer;
|
||||||
|
for (y = -layer + 1; y <= layer - 1; y++)
|
||||||
|
someCellSpawned = RandomOrganismGenerator.trySpawnCell(organism, x, y, spawnChance);
|
||||||
|
|
||||||
|
// right
|
||||||
|
x = layer;
|
||||||
|
for (y = -layer + 1; y < layer - 1; y++)
|
||||||
|
someCellSpawned = RandomOrganismGenerator.trySpawnCell(organism, x, y, spawnChance);
|
||||||
|
|
||||||
|
if (!someCellSpawned)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// randomize the organism's brain
|
||||||
|
var decisions = organism.brain.decisions;
|
||||||
|
decisions[CellStates.empty.name] = Brain.Decision.getRandom();
|
||||||
|
decisions[CellStates.food.name] = Brain.Decision.getRandom();
|
||||||
|
decisions[CellStates.wall.name] = Brain.Decision.getRandom();
|
||||||
|
decisions[CellStates.mouth.name] = Brain.Decision.getRandom();
|
||||||
|
decisions[CellStates.producer.name] = Brain.Decision.getRandom();
|
||||||
|
decisions[CellStates.mover.name] = Brain.Decision.getRandom();
|
||||||
|
decisions[CellStates.killer.name] = Brain.Decision.getRandom();
|
||||||
|
decisions[CellStates.armor.name] = Brain.Decision.getRandom();
|
||||||
|
decisions[CellStates.eye.name] = Brain.Decision.getRandom();
|
||||||
|
|
||||||
|
return organism;
|
||||||
|
}
|
||||||
|
|
||||||
|
static trySpawnCell(organism, x, y, spawnChance) {
|
||||||
|
|
||||||
|
var neighbors = organism.anatomy.getNeighborsOfCell(x, y);
|
||||||
|
if (neighbors.length && Math.random() < spawnChance) {
|
||||||
|
organism.anatomy.addRandomizedCell(CellStates.getRandomLivingType(), x, y);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RandomOrganismGenerator.organismLayers = 2;
|
||||||
|
RandomOrganismGenerator.cellSpawnChance = 0.75;
|
||||||
|
|
||||||
|
module.exports = RandomOrganismGenerator;
|
||||||
Reference in New Issue
Block a user