Cellular Automaton
CellularAutomaton.as
by nox, 2010.7.9
http://handasse.blogspot.com/
1-9 : ルール設定 - セルの周りの生存セル数(0-8)に対応
: '.' 死亡, '&' 変化, '+' 誕生, '=' 維持
SPACE : 一時停止
ENTER : リセット
V : メッセージ表示/非表示
Z : 初期生存セルの割合を減少
X : 初期生存セルの割合を増加
/**
* Copyright nox ( http://wonderfl.net/user/nox )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/6yen
*/
// CellularAutomaton.as
// by nox, 2010.7.9
// http://handasse.blogspot.com/
/*
1-9 : ルール設定 - セルの周りの生存セル数(0-8)に対応
: '.' 死亡, '&' 変化, '+' 誕生, '=' 維持
SPACE : 一時停止
ENTER : リセット
V : メッセージ表示/非表示
Z : 初期生存セルの割合を減少
X : 初期生存セルの割合を増加
*/
package {
import flash.display.*;
import flash.events.*;
import flash.text.*;
import flash.utils.*;
import flash.geom.Point;
[SWF (width="465", height="465", backgroundColor="0x000000", frameRate="60")]
public class CellularAutomaton extends Sprite
{
private const R:int = 465, C:int = 465, C5:int = ((C-1) >> 5) + 1;
private const RCinv:Number = 1.0 / (R * C);
private const remainBits:int = ((~C + 1) & 0x1f) * R;
// '.': death, '&': change, '+': birth, '=': survival
private const ruleChar:Array = ['.', '&', '+', '=']
private const log4inv:Number = 1.0 / Math.log(4.0);
private const updateTime:uint = 500;
private var bmpData:BitmapData = new BitmapData(R, C);
private var cells:Array = new Array(R);
private var modR:Array = new Array(R * 3);
private var modC:Array = new Array(C * 3);
// 0: death, 1: change, 2: birth, 3: survival
private var rules:Array = [0, 0, 3, 2, 0, 0, 0, 0, 0]; // Conway's Game of Life
private var rate:Number = 0.3;
private var initRate:Number = rate;
private var cnt:Array = new Array(R);
private var ruleBits:Array = new Array(4);
private var entropy:Number = 1.0;
private var patterns:Array = [0, 0, 0, 0];
private var txtData:TextField = new TextField();
private var txtRules:TextField = new TextField();
private var fps:Number = 0.0;
private var drawCount:uint = 0;
private var prevTimer:uint = 0;
private function bitcount(bits:uint):uint {
bits = (bits & 0x55555555) + (bits >> 1 & 0x55555555);
bits = (bits & 0x33333333) + (bits >> 2 & 0x33333333);
bits = (bits & 0x0f0f0f0f) + (bits >> 4 & 0x0f0f0f0f);
bits = (bits & 0x00ff00ff) + (bits >> 8 & 0x00ff00ff);
return (bits & 0x0000ffff) + (bits >>16 & 0x0000ffff);
}
private function init():void {
var i:int, j:int;
for (i = 0; i < R; i++) for (j = 0; j < C5; j++) cells[i][j] = 0;
for (i = 0; i < R; i++) for (j = 0; j < C; j++)
if (Math.random() < initRate) cells[i][j>>5] |= 1 << (j&0x1f);
}
private function next():void {
var i:int, j:int, x:int, y:int;
for (i = 0; i < R; i++) for (j = 0; j < C; j++) cnt[i][j] = 0;
for (x = 0; x < R; x++) for (y = 0; y < C; y++)
if (cells[x][y>>5] & 1 << (y&0x1f))
for (i = -1; i <= 1; i++) for (j = -1; j <= 1; j++)
if (i != 0 || j != 0) cnt[modR[x+i+R]][modC[y+j+C]]++;
for (i = 0; i < 4; i++) patterns[i] = 0;
for (x = 0; x < R; x++) {
for (i = 0; i < 4; i++) for (j = 0; j < C5; j++) ruleBits[i][j] = 0;
for (y = 0; y < C; y++)
ruleBits[rules[cnt[x][y]]][y>>5] |= 1 << (y&0x1f);
for (i = 0; i < C5; i++) {
var cellBits:uint = cells[x][i];
cells[x][i] = cells[x][i] & ~ruleBits[0][i] ^ ruleBits[1][i] | ruleBits[2][i];
patterns[0] += bitcount(~cellBits & ~cells[x][i]);
patterns[1] += bitcount(cellBits & ~cells[x][i]);
patterns[2] += bitcount(~cellBits & cells[x][i]);
patterns[3] += bitcount(cellBits & cells[x][i]);
}
}
patterns[0] -= remainBits;
entropy = 0.0;
for (i = 0; i < 4; i++) {
if (patterns[i] == 0) continue;
var p:Number = patterns[i] * RCinv;
entropy -= p * Math.log(p) * log4inv;
}
}
private function countAlive():int {
var cnt:int = 0;
for (var x:int = 0; x < R; x++)
for (var c:int = 0; c < C5; c++)
cnt += bitcount(cells[x][c]);
return cnt;
}
private function showData(cntAlive:uint):void {
drawCount += 1;
if (getTimer() - prevTimer >= updateTime) {
fps = (drawCount * 1000 / (getTimer() - prevTimer) * 10 >> 0) / 10;
prevTimer = getTimer();
drawCount = 0;
}
txtData.htmlText = "<span class='c'>FPS = " + fps + " / " + stage.frameRate
+ "\nN = " + cntAlive
+ "\nH = " + (entropy * 100000 >> 0) / 100000 + "</span>";
}
private function showRules():void {
var rule:String = "";
for (var i:int = 0; i < 9; i++) rule += ruleChar[rules[i]];
txtRules.htmlText = "<span class='c'>" + "[" + rule + "] "
+ initRate + " / " + (rate * 100000 >> 0) / 100000 + "</span>";
}
private function onEnterFrame(event:Event):void {
var cntAlive:uint = countAlive();
showData(cntAlive);
showRules();
rate = cntAlive * RCinv;
var color:uint = (rate * 0xff) << 16 | ((1.0 - rate) * 0xff) >> 0;
var bgcolor:uint = (entropy * 0xff) << 8;
bmpData.lock();
for (var i:int = 0; i < R; i++) {
for (var j:int = 0; j < C; j++) {
var bit:uint = cells[i][j>>5] >> (j&0x1f) & 1;
bmpData.setPixel(i, j, bit * color + (bit ^ 1) * bgcolor);
}
}
bmpData.unlock();
next();
}
private function onKeyDown(event:KeyboardEvent):void{
if (49 <= event.keyCode && event.keyCode <= 57) {
// '1'-'9' keys : set rules for number of neighbors (0-8)
var idx:uint = event.keyCode - 49;
rules[idx] = (rules[idx] + 1) & 0x3;
} else {
switch (event.keyCode) {
case 32: // Space : pause
if (stage.hasEventListener(Event.ENTER_FRAME))
stage.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
else
stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
break;
case 86: // 'V' : show / hide messages
if (stage.contains(txtData)) stage.removeChild(txtData);
else stage.addChild(txtData);
if (stage.contains(txtRules)) stage.removeChild(txtRules);
else stage.addChild(txtRules);
break;
case 88: // 'X' : decrease initial rate of population
initRate = (initRate * 10) >> 0;
initRate = initRate + 1;
initRate = (initRate > 10) ? 0.0 : initRate / 10;
break;
case 90: // 'Z' : increase initial rate of population
initRate = (initRate * 10) >> 0;
initRate = initRate - 1;
initRate = (initRate < 0) ? 1.0 : initRate / 10;
break;
case 13: // Enter : reset
init();
break;
}
}
showRules();
};
public function CellularAutomaton() {
stage.addChild(new Bitmap(bmpData));
stage.addChild(txtData);
stage.addChild(txtRules);
stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
var i:int;
for (i = 0; i < R * 3; i++) modR[i] = i % R;
for (i = 0; i < C * 3; i++) modC[i] = i % C;
for (i = 0; i < R; i++) {
cells[i] = new Array(C5);
cnt[i] = new Array(C);
}
for (i = 0; i < 4; i++) ruleBits[i] = new Array(C5);
var styleSheet:StyleSheet = new StyleSheet();
styleSheet.setStyle(".c", {fontFamily:"_typewriter", fontSize:"16px", color:"#ffffff"});
txtData.styleSheet = txtRules.styleSheet = styleSheet;
txtData.autoSize = txtRules.autoSize = TextFieldAutoSize.LEFT;
txtData.y = 0;
txtRules.y = stage.height - 24;
prevTimer = getTimer();
init();
}
}
}