diff --git a/dist/css/style.css b/dist/css/style.css index 3b15873..a0624dc 100644 --- a/dist/css/style.css +++ b/dist/css/style.css @@ -75,6 +75,7 @@ button { display: inline-block; font-size: 16px; min-width: 30px; + margin: 2px; } button:hover{ background-color: #81d2c7; diff --git a/dist/index.html b/dist/index.html index 99fba39..5585f26 100644 --- a/dist/index.html +++ b/dist/index.html @@ -35,6 +35,8 @@
+ +

diff --git a/src/Controllers/ControlPanel.js b/src/Controllers/ControlPanel.js index 5396a9e..fcbe8b3 100644 --- a/src/Controllers/ControlPanel.js +++ b/src/Controllers/ControlPanel.js @@ -327,6 +327,9 @@ class ControlPanel { env.auto_reset = false; $('#auto-reset').prop('checked', false);; }); + $('#random-walls').click( function() { + this.env_controller.randomizeWalls(); + }.bind(this)); $('#auto-reset').change(function() { env.auto_reset = this.checked; }); diff --git a/src/Controllers/EnvironmentController.js b/src/Controllers/EnvironmentController.js index 8722d61..5e8b963 100644 --- a/src/Controllers/EnvironmentController.js +++ b/src/Controllers/EnvironmentController.js @@ -5,6 +5,7 @@ const CellStates = require("../Organism/Cell/CellStates"); const Neighbors = require("../Grid/Neighbors"); const FossilRecord = require("../Stats/FossilRecord"); const Hyperparams = require("../Hyperparameters"); +const Perlin = require("../Utils/Perlin"); class EnvironmentController extends CanvasController{ constructor(env, canvas) { @@ -51,8 +52,34 @@ class EnvironmentController extends CanvasController{ this.scale = 1; } + /* + Iterate over grid from 0,0 to env.num_cols,env.num_rows and create random walls using perlin noise to create a more organic shape. + */ + randomizeWalls(thickness=1) { + this.env.clearWalls(); + const noise_threshold = -0.017; + let avg_noise = 0; + let resolution = 20; + Perlin.seed(); + + for (let r = 0; r < this.env.num_rows; r++) { + for (let c = 0; c < this.env.num_cols; c++) { + let xval = c/this.env.num_cols*(resolution/this.env.renderer.cell_size*(this.env.num_cols/this.env.num_rows)); + let yval = r/this.env.num_rows*(resolution/this.env.renderer.cell_size*(this.env.num_rows/this.env.num_cols)); + let noise = Perlin.get(xval, yval); + avg_noise += noise/(this.env.num_rows*this.env.num_cols); + if (noise > noise_threshold && noise < noise_threshold + thickness/resolution) { + let cell = this.env.grid_map.cellAt(c, r); + if (cell != null) { + if(cell.owner != null) cell.owner.die(); + this.env.changeCell(c, r, CellStates.wall, null); + } + } + } + } + } + updateMouseLocation(offsetX, offsetY){ - super.updateMouseLocation(offsetX, offsetY); } diff --git a/src/Environments/WorldEnvironment.js b/src/Environments/WorldEnvironment.js index 1da6c73..fdf48c3 100644 --- a/src/Environments/WorldEnvironment.js +++ b/src/Environments/WorldEnvironment.js @@ -12,9 +12,9 @@ class WorldEnvironment extends Environment{ super(); this.renderer = new Renderer('env-canvas', 'env', cell_size); this.controller = new EnvironmentController(this, this.renderer.canvas); - var grid_rows = Math.ceil(this.renderer.height / cell_size); - var grid_cols = Math.ceil(this.renderer.width / cell_size); - this.grid_map = new GridMap(grid_cols, grid_rows, cell_size); + this.num_rows = Math.ceil(this.renderer.height / cell_size); + this.num_cols = Math.ceil(this.renderer.width / cell_size); + this.grid_map = new GridMap(this.num_cols, this.num_rows, cell_size); this.organisms = []; this.walls = []; this.total_mutability = 0; @@ -104,7 +104,8 @@ class WorldEnvironment extends Environment{ clearWalls() { for(var wall of this.walls){ - if (this.grid_map.cellAt(wall.col, wall.row).state == CellStates.wall) + let wcell = this.grid_map.cellAt(wall.col, wall.row); + if (wcell && wcell.state == CellStates.wall) this.changeCell(wall.col, wall.row, CellStates.empty, null); } } @@ -154,9 +155,9 @@ class WorldEnvironment extends Environment{ resizeFillWindow(cell_size) { this.renderer.cell_size = cell_size; this.renderer.fillWindow('env'); - var cols = Math.ceil(this.renderer.width / cell_size); - var rows = Math.ceil(this.renderer.height / cell_size); - this.grid_map.resize(cols, rows, cell_size); + this.num_cols = Math.ceil(this.renderer.width / cell_size); + this.num_rows = Math.ceil(this.renderer.height / cell_size); + this.grid_map.resize(this.num_cols, this.num_rows, cell_size); } } diff --git a/src/Utils/Perlin.js b/src/Utils/Perlin.js new file mode 100644 index 0000000..ab9b5ca --- /dev/null +++ b/src/Utils/Perlin.js @@ -0,0 +1,46 @@ +let perlin = { + rand_vect: function(){ + let theta = Math.random() * 2 * Math.PI; + return {x: Math.cos(theta), y: Math.sin(theta)}; + }, + dot_prod_grid: function(x, y, vx, vy){ + let g_vect; + let d_vect = {x: x - vx, y: y - vy}; + if (this.gradients[[vx,vy]]){ + g_vect = this.gradients[[vx,vy]]; + } else { + g_vect = this.rand_vect(); + this.gradients[[vx, vy]] = g_vect; + } + return d_vect.x * g_vect.x + d_vect.y * g_vect.y; + }, + smootherstep: function(x){ + return 6*x**5 - 15*x**4 + 10*x**3; + }, + interp: function(x, a, b){ + return a + this.smootherstep(x) * (b-a); + }, + seed: function(){ + this.gradients = {}; + this.memory = {}; + }, + get: function(x, y) { + if (this.memory.hasOwnProperty([x,y])) + return this.memory[[x,y]]; + let xf = Math.floor(x); + let yf = Math.floor(y); + //interpolate + let tl = this.dot_prod_grid(x, y, xf, yf); + let tr = this.dot_prod_grid(x, y, xf+1, yf); + let bl = this.dot_prod_grid(x, y, xf, yf+1); + let br = this.dot_prod_grid(x, y, xf+1, yf+1); + let xt = this.interp(x-xf, tl, tr); + let xb = this.interp(x-xf, bl, br); + let v = this.interp(y-yf, xt, xb); + this.memory[[x,y]] = v; + return v; + } +} +perlin.seed(); + +module.exports = perlin; \ No newline at end of file