C#
数学
セルオートマトン
NGraphics

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