/* Cellular automata, polar coordinates. Michael Soukup, 2014 */ function nearestMod0(f, n) { var mod = f % n; if (mod < n/2) { // floor return f-mod } else { // ceil return f+n-mod } } function Cartesian(x, y) { this.x = x; this.y = y; } Cartesian.prototype.r = function() { return Math.sqrt(Math.pow(this.x,2) + Math.pow(this.y, 2)); } Cartesian.prototype.theta = function() { return Math.atan2(this.y, this.x); } function Polar(r, theta) { this.r = r; this.theta = theta; } Polar.prototype.x = function() { return this.r*Math.cos(this.theta); } Polar.prototype.y = function() { return this.r*Math.sin(this.theta); } function Vector(x, y) { this.x = x; this.y = y; } Vector.prototype.add = function(v) { this.x += v.x; this.y += v.y; } Vector.prototype.sub = function(v) { this.x -= v.x; this.y -= v.y; } function Frame(o, v) { this.o = o; this.v = v; } Frame.prototype.center = function() { return new Cartesian(this.o.x + this.v.x/2, this.o.y + this.v.y/2); } var CELL_COL = { 0: '#eee', 1: '#222', 2: '#484', 3: '#a44', 4: '#8a8a8a' }, CELL_STROKE = { 0: '#bbb', 1: '#222', 2: '#484', 3: '#a44', 4: '#8a8a8a' }, CELL_LINEWIDTH = { 0: 0.1, 1: 0.8, 2: 0.5, 3: 0.5, 4: 0.2 }, CELL_T = { 'empty': 0, 'wall': 1, 'player': 2, 'enemy': 3, 'conway': 4 }, CELL_CONFIGS = { 'player0': [2,2,2,2,2,2], 'enemy0': [3,3,3,3,3,3], 'organism0': [4,0,4,0,0,0,4,0,0,4,4,4], 'box': [4,4,0,0,0,0], 'diamond': [4,0,0,4,0,0] }; var Game = (function(canvas_id) { var canvas = document.getElementById(canvas_id), ctx = canvas.getContext('2d'), view = new Frame(new Cartesian(0,0), new Vector(canvas.width, canvas.height)), n_poly, r_poly, alpha_poly; function Organism(cell_seq, x, y) { this.cells = cell_seq; // Must be of length % n == 0. When big enough, cell sequence becomes a new cell. Cells expand recursively this.o = new Cartesian(x, y); // Continuous. Fit to r_sim before simulation this.layers = cell_seq.length/world.n_cell;// find radius = f(length sequence) //this.r = this.layers*world.r_cell; } function loadWorld(w) { n_poly = w.n_poly; r_poly = w.r_poly; alpha_poly = Math.PI / n_poly; } function drawPolygon(N, R, phi, x, y, fill) { var vertice = new Polar(R, phi - Math.PI / N), dtheta = 2*Math.PI / N; ctx.fillStyle = fill; ctx.beginPath(); ctx.moveTo(x+vertice.x(), y+vertice.y()); for (var i=0; i N*l) { l++; incr = 0; c_0l = i; dtheta = (2*Math.PI) / (l*N); } pol.theta = (i - c_0l)*dtheta + phi; pol.r = Math.sqrt(3*Math.pow(R*l, 2) + Math.pow((l/2 - (i - c_0l) % l)*2*R, 2)); drawPolygon(N, R, phi, x+pol.x(), y+pol.y(), CELL_COL[seq[i]]); } } function getNeighbourhood(i, l, N) { var X = []; if (i == 0) { // origo is special for (var _i=0; _i= 2 && nd[4] > 0) { // Inside of player return 4 } else if (nd[4] == 2) { // rebirth rule return 4 } else if (nd[1] > 2) { // wall rule return 4 } else { return 0 } } function cell_1_update(X) { return 1 } function cell_2_update(X) { // player cell var nd = neighbourDist(X); if (nd[4] > 2 || nd[2] > 4) { return 0 } else { return 2 } } function cell_3_update(X) { // enemy cell var nd = neighbourDist(X); if (nd[4] > 3 || nd[3] > 4) { return 0 } else { return 3 } } function cell_4_update(X) { // conway live cells var nd = neighbourDist(X); if (nd[2] > 2 && nd[4] >= 2) { // Inside corner of player return 2 } else if (nd[4] == 2){ return 4 } else if (nd[1] > 2) { // wall rule return 4 } else { return 0 } } function cellNext(c, X) { // Size of neighbourhood N is nxn where n is (r*2 + 1), r in {1,2,3...}: // 3x3, 5x5, 7x7, ... switch (c) { case 0: return cell_0_update(X); case 1: return cell_1_update(X); case 2: return cell_2_update(X); case 3: return cell_3_update(X); case 4: return cell_4_update(X); } } function simulate(org, dt) { var org_buf = [], l = 0, incr = 0, X, N = 6; X = neighbours(org, 0, l, N); org_buf.push(cellNext(org[0], X)) for (var i=1; i N*l) { l++; } X = neighbours(org, i, l, N); org_buf.push(cellNext(org[i], X)); } return org_buf; } function drawOrganism(organism, depth) { // recursive return 0 } function drawGrid() { var o = view.center(), o_polar = new Polar(o.r(),-o.theta()); o_polar.r = nearestMod0(o_polar.r, world.r_cell); // set r to fit nearest o_polar.theta = nearestMod0(o_polar.theta, world.dtheta) + world.theta_cell;// set theta to nearest // Set relative to canvas o.x = o_polar.x() - view.o.x; o.y = o_polar.y() - view.o.y; o_polar.r = 0; o_polar.theta = world.theta_cell; var i = 0, r_max = Math.sqrt(Math.pow(o.x, 2), Math.pow(o.y, 2)); // Upper left corner console.log(o); console.log(o_polar.r); console.log(r_max); console.log(o_polar.x()); var dtheta; while (o_polar.r < r_max) { o_polar.r = Math.ceil(i/world.n_cell)*2*world.r_cell; dtheta = 2*Math.PI/(Math.ceil(i/world.n_cell)*world.n_cell); o_polar.theta = Math.ceil(i/world.n_cell)*world.n_cell*i + world.theta_cell; drawPolygon(o_polar.x()+o.x, o_polar.y()+o.y, world.r_cell, world.n_cell, 0, '#bbb'); i++; } } function update() { // move and update physics; // fit origo to tiles // check for collision, merge and expand radius // simulate // update camera // load simulation frame from world config? } return { loadWorld: loadWorld, testDraw: function() { //drawGrid(); //drawPolygon(N, R, phi, x, y, fill) drawPolygon(6, 10, 0, 40, 80, '#aaa'); drawPolygon(6, 15, (2*Math.PI/6)/10, 100, 80, '#aaa', '#ddd', 0.4); drawPolygon(3, 15, 0, 180, 80, '#aaa', '#ddd', 0.4); drawPolygon(3, 15, 2*Math.PI/12, 240, 80, '#aaa', '#ddd', 0.4); drawPolygon(8, 15, 0, 300, 80, '#aaa', '#ddd', 0.4); //drawPolySeq(seq, N, R, phi, x, y) var n=6, px=10, phi=Math.PI/n; drawPolySeq([2,2,3,3,3,2,4,2,2,1,2,2,2,3,2,2,2,2,2,2,3,4,1,1,1,1,2,2,2,2,2,3,3,3,2,2,2,4], n, px, phi, 101.5, 201.5); drawPolySeq([2,2,3,3,3,2,4,2,2,1,2,2,2,3,2,2,2,2,2,2,3,4,1,1,1,1,2,2,2,2,2,3,3,3,2,2,2,4], n, px/2, phi, 301.5, 201.5); n=4; px=5; phi=Math.PI/n; drawPolySeq([2,2,2,3,3,2,2,2,2,3,3,3,3,2,2,4,4,2,2,4,2,3,2,2,3,3,2], n, px, phi, 501.5, 201.5); }, testLogic: function() { var n=6,l=2,i=7;; console.log('c_02 neighbourhood: (i=7)'); X = getNeighbourhood(i, l, n); console.log(X); i=1;l=1;n=6 console.log('c_01 neighbourhood: (i=1)'); X = getNeighbourhood(i, l, n); console.log(X); i=22;l=3;n=6 console.log('crown 1 l=3 neighbourhood (i=22):'); X = getNeighbourhood(i, l, n); console.log(X); i=34;l=3;n=6 console.log('crown 5 l=3 neighbourhood (i=34):'); X = getNeighbourhood(i, l, n); console.log(X); i=8;l=2;n=6 console.log('middle 1 l=2 neighbourhood (i=8):'); X = getNeighbourhood(i, l, n); console.log(X); i=60;l=4;n=6 console.log('middle step layer 6 l=4 neighbourhood (i=60):'); X = getNeighbourhood(i, l, n); console.log(X); i=4;l=1;n=6 console.log('crown 3 l=1 (i=4):'); X = getNeighbourhood(i, l, n); console.log(X); i=3;l=1;n=6 console.log('crown 3 l=1 (i=3):'); X = getNeighbourhood(i, l, n); console.log(X); i=13;l=2;n=6 console.log('crown 3 l=2 (i=13):'); X = getNeighbourhood(i, l, n); console.log(X); i=0;l=0;n=6 console.log('center cell (i=0):'); X = getNeighbourhood(i, l, n); console.log(X); }, testSimulation: function() { var n=6, px=10, phi=Math.PI/n, dt=1000, player=[4,4,4,0,0,0,0,4,0,0,0,4,0,0,4,4,4,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2], enemy1=[4,0,0,0,4,4,0,0,4,0,0,4,4,4,4,0,0,0,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3]; enemy2=[4,4,4,0,4,4,0,0,4,0,0,4,4,4,4,0,0,0,4,4,0,0,0,0,4,4,4,0,0,4,4,0,0,4,4,0,0,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3]; wallor=[1,0,1,0,1,0,4,4,4,0,1,0,1,0,1,0,0,4,4,4,4,4,0,0,0,4,4,4,0,0,4,4,0,0,4,4,4,0] drawPolySeq(player, n, px, phi, canvas.width*0.25, canvas.height*0.25); drawPolySeq(enemy1, n, px, phi, canvas.width*0.75, canvas.height*0.75); drawPolySeq(enemy2, n, px, phi, canvas.width*0.8, canvas.height*0.2); drawPolySeq(wallor, n, px, phi, canvas.width*0.2, canvas.height*0.8); setInterval(function (){ player = simulate(player, dt); enemy1 = simulate(enemy1, dt); enemy2 = simulate(enemy2, dt); wallor = simulate(wallor, dt); ctx.clearRect(0, 0, canvas.width, canvas.height); drawPolySeq(player, n, px, phi, canvas.width*0.25, canvas.height*0.25); drawPolySeq(enemy1, n, px, phi, canvas.width*0.75, canvas.height*0.75); drawPolySeq(enemy2, n, px, phi, canvas.width*0.8, canvas.height*0.2); drawPolySeq(wallor, n, px, phi, canvas.width*0.2, canvas.height*0.8); }, dt); } } }); // conway // static rule // CELL MAP /** 0: dead or empty tile 1: solid wall 2: skin cell (player) 3: skin cell (enemy) 4: living cell 4: **/ var WORLD0 = { "n_poly": 6, // Must be 3, 6, .. "r_poly": 15, "alpha_poly": 0, "dtheta": 2*Math.PI / 6, "r_sim": 2000, "o_f_init": new Cartesian(0, 0), "level": 0, "config": [] // sequence of cell configs }; var game = Game('board'); //game.loadWorld(WORLD0); //game.testDraw(); //testLogic(); game.testSimulation();