/** * ECA: Elementary Cellular Automaton * by Kazuki Maeda * Last-Modified: Jun 29, 2015 */ var WIDTH = 600, HEIGHT = 600; var CELLSIZE = 10; var cellNumber = WIDTH/CELLSIZE; var backgroundcolor = '#242424'; var gridcolor = '#888888'; var oncolor = '#7FFF00'; var presentColor = '#FFD700'; var state; var presentLine; var ruleArray; var dragFlag; var setMode; var thread; var cell; var t; var savestate; window.onload = function(){ document.getElementById('periodic').addEventListener('click', setPeriodic); document.getElementById('startstop').addEventListener('click', startstop); document.getElementById('clear').addEventListener('click', init); document.getElementById('random').addEventListener('click', random); document.getElementById('save').addEventListener('click', save); document.getElementById('load').addEventListener('click', load); // make selectbox var rules = new Array(); for(var i = 0; i < 256; ++i){ rules[i] = document.createElement('option'); rules[i].value = ''+i; var tmpstr = ''; var tmp = i; for(var j = 0; j < 8; ++j){ tmpstr = '' + (tmp % 2) + tmpstr; tmp >>= 1; } rules[i].innerHTML = (i < 10 ? '0' : '') + (i < 100 ? '0' : '') + i + ' (' + tmpstr + ')'; } var rule = document.getElementById('rule'); for(var i = 0; i < 256; ++i) rule.appendChild(rules[i]); rule.value = '184'; // default rule // make canvas document.getElementById('canvas').removeChild(document.getElementById('canvas').firstChild); rootSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); rootSVG.setAttribute('width', WIDTH); rootSVG.setAttribute('height', HEIGHT); rootSVG.setAttribute('stroke', gridcolor); rootSVG.setAttribute('stroke-width', '1.0'); document.getElementById('canvas').appendChild(rootSVG); rootSVG.addEventListener('mouseup', upCell); cell = new Array(); for(var i = 0; i < cellNumber; ++i){ cell[i] = new Array(); for(var j = 0; j < cellNumber; ++j){ cell[i][j] = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); cell[i][j].setAttribute('id', 'cell'+i+','+j); cell[i][j].setAttribute('x', ''+(j*CELLSIZE)); cell[i][j].setAttribute('y', ''+(i*CELLSIZE)); cell[i][j].setAttribute('width', ''+CELLSIZE); cell[i][j].setAttribute('height', ''+CELLSIZE); if(i == 0){ cell[i][j].addEventListener('mousedown', downCell); cell[i][j].addEventListener('mousemove', moveCell); } cell[i][j].addEventListener('dragstart', preventDrag); rootSVG.appendChild(cell[i][j]); } } presentLine = new Array(); // 0: N, 1: E, 2: S, 3: W for(var i = 0; i < 4; ++i){ presentLine[i] = document.createElementNS('http://www.w3.org/2000/svg', 'line'); presentLine[i].setAttribute('stroke', presentColor); presentLine[i].setAttribute('stroke-width', '3.0'); rootSVG.appendChild(presentLine[i]); } setPeriodic(null); savestate = new Array(); for(var i = 0; i < cellNumber; ++i) savestate[i] = 0; init(); } function init(){ document.getElementById('rule').disabled = false; document.getElementById('periodic').disabled = false; document.getElementById('clear').disabled = false; document.getElementById('random').disabled = false; document.getElementById('fps').disabled = false; document.getElementById('save').disabled = false; document.getElementById('load').disabled = false; state = new Array(); state[0] = new Array(); for(var i = 0; i < cellNumber; ++i) state[0][i] = 0; dragFlag = false; t = 0; setPresentLine(t); for(var i = 0; i < cellNumber; ++i) for(var j = 0; j < cellNumber; ++j){ if(i == 0){ cell[i][j].setAttribute('class', 'draggable'); } cell[i][j].setAttribute('fill', backgroundcolor); } } function setPresentLine(t){ if(t >= cellNumber) t = cellNumber-1; presentLine[0].setAttribute('x1', '0'); presentLine[0].setAttribute('x2', ''+WIDTH); presentLine[0].setAttribute('y1', ''+CELLSIZE*t); presentLine[0].setAttribute('y2', ''+CELLSIZE*t); presentLine[1].setAttribute('x1', ''+WIDTH); presentLine[1].setAttribute('x2', ''+WIDTH); presentLine[1].setAttribute('y1', ''+CELLSIZE*t); presentLine[1].setAttribute('y2', ''+CELLSIZE*(t+1)); presentLine[2].setAttribute('x1', ''+WIDTH); presentLine[2].setAttribute('x2', '0'); presentLine[2].setAttribute('y1', ''+CELLSIZE*(t+1)); presentLine[2].setAttribute('y2', ''+CELLSIZE*(t+1)); presentLine[3].setAttribute('x1', '0'); presentLine[3].setAttribute('x2', '0'); presentLine[3].setAttribute('y1', ''+CELLSIZE*(t+1)); presentLine[3].setAttribute('y2', ''+CELLSIZE*t); } function setCell(n){ if(setMode){ cell[0][n].setAttribute('fill', oncolor); state[0][n] = 1; } else { cell[0][n].setAttribute('fill', backgroundcolor); state[0][n] = 0; } } function downCell(e){ if(t == 0){ var str = e.target.id; var n = parseInt(str.substr(str.indexOf(',')+1, str.length)); setMode = state[0][n] ? 0 : 1; dragFlag = true; setCell(n); } } function upCell(e){ dragFlag = false; } function moveCell(e){ if(dragFlag){ var str = e.target.id; var n = parseInt(str.substr(str.indexOf(',')+1, str.length)); setCell(n); } } function startstop(e){ dragFlag = false; if(document.getElementById('startstop').value == 'Start') { document.getElementById('rule').disabled = true; document.getElementById('periodic').disabled = true; document.getElementById('clear').disabled = true; document.getElementById('random').disabled = true; document.getElementById('fps').disabled = true; document.getElementById('save').disabled = true; document.getElementById('load').disabled = true; if(t == 0){ for(var i = 0; i < cellNumber; ++i) cell[0][i].setAttribute('class', ''); ruleArray = new Array(); var tmp = parseInt(document.getElementById('rule').value); for(var i = 0; i < 8; ++i){ ruleArray[i] = tmp % 2; tmp >>= 1; } } next(); var timeInterval = 1000/parseInt(document.getElementById('fps').value); thread = setInterval('next();', timeInterval); document.getElementById('startstop').value = 'Stop'; } else { document.getElementById('clear').disabled = false; document.getElementById('fps').disabled = false; document.getElementById('save').disabled = false; document.getElementById('load').disabled = false; document.getElementById('random').disabled = false; clearInterval(thread); document.getElementById('startstop').value = 'Start'; } } function next(){ ++t; if(t < cellNumber) setPresentLine(t); else { for(var i = 0; i < cellNumber-1; ++i) for(var j = 0; j < cellNumber; ++j) cell[i][j].setAttribute('fill', state[t-cellNumber+1+i][j] ? oncolor : backgroundcolor); } state[t] = new Array(); var p = document.getElementById('periodic').checked; for(var i = 0; i < cellNumber; ++i){ var tmp = 0; tmp += (!p && i == 0) ? 0 : state[t-1][tr(i-1)]*4; tmp += state[t-1][i]*2; tmp += (!p && i == cellNumber-1) ? 0 : state[t-1][tr(i+1)]; state[t][i] = ruleArray[tmp]; if(t < cellNumber) cell[t][i].setAttribute('fill', state[t][i] ? oncolor : backgroundcolor); else cell[cellNumber-1][i].setAttribute('fill', state[t][i] ? oncolor : backgroundcolor); } } function tr(n){ if(n == -1) return cellNumber-1; if(n == cellNumber) return 0; return n; } function random(e){ init(); for(var i = 0; i < cellNumber; ++i){ state[0][i] = Math.floor(Math.random()*2); cell[0][i].setAttribute('fill', state[0][i] ? oncolor : backgroundcolor); } } function setPeriodic(e){ if(document.getElementById('periodic').checked){ presentLine[1].setAttribute('stroke-width', '0.0'); presentLine[3].setAttribute('stroke-width', '0.0'); } else { presentLine[1].setAttribute('stroke-width', '3.0'); presentLine[3].setAttribute('stroke-width', '3.0'); } } function preventDrag(e){ e.preventDefault(); } function save(e){ for(var i = 0; i < cellNumber; ++i) savestate[i] = state[0][i]; } function load(e){ init(); for(var i = 0; i < cellNumber; ++i){ state[0][i] = savestate[i]; cell[0][i].setAttribute('fill', state[0][i] ? oncolor : backgroundcolor); } }