Edited at

C#: 1次元セルオートマトン - ルール0からルール255まで全て試す

Wikipedia「セル・オートマトン」の記事を参考にして、1次元セルオートマトンのルール0からルール255までの全256パターンを描くプログラムを作成してみました。

このなかでは、「ルール30」と「ルール90」が有名ですね。


ルール30のセルオートマトンとは

ある時刻 t+1の中央のセル状態は、時刻 t の 左、中央、右の3つのセルの状態で決定されます。

そのルールを以下に示します。

時刻 t    時刻 t+1

111 → 0
110 → 0
101 → 0
100 → 1
011 → 1
010 → 1
001 → 1
000 → 0

時刻 t の値を並べると 0001110 となり、10進数で 30 となることから、ルール30と呼ばれます。

このルールで作成される模様が、ある種の貝殻の表面にある模様と大変よく似ているらしいです。自然が作り出す模様が数学的なルールに基づいていると考えると、とても興味深いですね。

Rule30.png


ルール90のセルオートマトンとは

ルール30で示した規則をルール90のものに置き換えると、典型的なフラクタル図形であるシェルピンスキーのギャスケットが現れます。

そのルール90を以下に示します。

時刻 t    時刻 t+1

111 → 0
110 → 1
101 → 0
100 → 1
011 → 1
010 → 0
001 → 1
000 → 0

時刻 t の値を並べると 01011010 となり、10進数で 90 となることから、ルール90と呼ばれています。

通常のフラクタル図形を描く方法とは全く別の方法(1次元セルオートマトン)で描けるというのは不思議です。

Rule90.png

つまり、このルールを変えることでいろんな図形を描くことができるわけです。ということで、ルール0からルール255までの全256パターンを描くプログラムを作成してみました。ルール番号を与えると、対応するルールを作成するようにしていますが、詳しくは後述のコードをみてください。


NGraphicパッケージ

以前このようなプログラムを作成した時は、Silverlightで作成したのですが、もう死んでしまったテクノロージーなので、今回は、コンソールアプリケーションとして作成し、PNGファイルとしてセルオートマトンを描画し出力することにします。

PNGファイルの作成には、NGraphicパッケージを使っています。.NET Core版のNGraphicパッケージで動かそうとしたのですが、どうもコンソールアプリでは無理みたいなので、.NET Framework版のNGraphicパッケージを利用しています。


NGraphicパッケージを隠蔽するEasyCanvasクラスを定義

まずは、EasyCanvasクラスを定義します。

このクラスは、NGraphicパッケージの機能をこの中に隠蔽するためのものです。

SVGにも書き出せればいいなと思ったのでこうしています。まあ、そうなったら、インターフェース定義して云々かんぬんやらないといけないのですが、まあそれは本当に必要になった時にやろうと思います。

必要なのは、点を打つ機能とファイルに出力する機能だけなので、SetPixelメソッドとWriteメソッドを定義しています。

using NGraphics;

namespace Cellautomaton {
class EasyCanvas {
private IImageCanvas canvas;

static EasyCanvas() {
// .NET Coreのパッケージの場合は、このコードが必要?
//Platforms.SetPlatform<SystemDrawingPlatform>();
}
public EasyCanvas(int width, int height) {
canvas = Platforms.Current.CreateImageCanvas(new Size(width, height), scale: 2);
}

public void SetPixel(int x, int y, SolidBrush brush) {
canvas.FillRectangle(x, y, 1, 1, brush);
}

public void Write(string path) {
canvas.GetImage().SaveAsPng(path);
}
}
}


1次元セルオートマトンを実現するCellularAutomatonクラスを定義

このEasyCanvasを使って、1次元セルオートマトンを実現するクラスCellularAutomatonを定義します。

なお、Ruleの数値はコンストラクタで与えられるようにしています。

using System;

using System.Collections.Generic;
using System.Text;
using System.Linq;

namespace Cellautomaton {
class CellularAutomaton {

private bool[] _cells;

private Dictionary<string, bool> _rule;

public CellularAutomaton(int rule, int width) {
_cells = new bool[width];
_cells[width / 2] = true;
_rule = MakeRule(rule);
}

// ルール番号からルールを作成
private Dictionary<string, bool> MakeRule(int rule) {
var list = new[] {
"111", "110", "101", "100", "011", "010", "001", "000"
};
return list.Reverse().Select(k => {
var b = (rule & 0x01) == 0x01;
rule = rule >> 1;
return new { Key = k, Value = b };
}).ToDictionary(p => p.Key, p => p.Value);
}

// すべての世代を列挙する。永遠に列挙するので注意。
public IEnumerable<bool[]> AllGenerations() {
yield return _cells;
while (true) {
List<bool> area = new List<bool>();
foreach (var la in GetAdjoints()) {
area.Add(_rule[LocalAreaToString(la)]);
}
_cells = area.ToArray();
yield return _cells;
}
}

// 3つのセルを文字列に変換
string LocalAreaToString(bool[] area) {
string s = "";
foreach (var a in area) {
s += a == true ? "1" : "0";
}
return s;
}

// 隣接した3つのセルを配列として順に取り出す。
IEnumerable<bool[]> GetAdjoints() {
bool[] area = new bool[3];
for (int i = 0; i < _cells.Length; i++) {
area[0] = (i == 0) ? false : _cells[i - 1];
area[1] = _cells[i];
area[2] = (i == _cells.Length - 1) ? false : _cells[i + 1];
yield return area;
}
}
}
}


Programクラスを定義

そして最後は、Programクラスです。

CellularAutomatonクラスを使ってルール0からルール255までのセルオートマトンを作成しています。

using NGraphics;

using System;
using System.Collections.Generic;
using System.Linq;

namespace Cellautomaton {
class Program {
static void Main(string[] args) {
for (int i = 0; i < 256; i++) {
int rule = i;
int generations = 180;
int width = generations * 2;
var ca = new CellularAutomaton(rule, width);
var lines = ca.AllGenerations().Take(generations);
MakeImage(rule, lines, width);
}
}

static void MakeImage(int rule, IEnumerable<bool[]> lines, int width) {
var canvas = new EasyCanvas(width, width / 2 + 10);
int y = 10;
foreach (var line in lines) {
for (int x = 0; x < width; x++) {
if (line[x])
canvas.SetPixel(x, y, Brushes.DarkGray);
}
y++;
}
canvas.Write($"Rule{rule}.png");
}
}
}

このコードを実行すれば、Rule0.pngからRule255.pngまでの256個の画像ファイルが作成できます。

ソースコードはGitHubでも公開しています。


結果

ルール30とルール90以外で特徴的な図形をいくつかピックアップして掲載します。




ルール110

Rule110.png




ルール118

Rule118.png




ルール131

Rule131.png




ルール150

Rule150.png




ルール161

Rule161.png




ルール241

Rule241.png