Arduinoで「Rule 30・90・110」のセルオートマトンをLEDに実装しました。ビット演算1行で0〜255すべてのルールに対応できる汎用コードと、Rule 30がなぜ貝殻の模様に一致するのか・Rule 110がチューリング完全な理由も解説します。動作デモ動画あり。

Rule 30 の予測不能なカオス、Rule 90 の完璧なフラクタル、Rule 110 のチューリング完全な振る舞い。これらはすべて「左・自分・右の3セルを見て次の状態を決める」というただそれだけのルールから生まれます。

💡 この記事でわかること
  • 1次元セルオートマトンの仕組みとRule番号の決まり方
  • Rule 30・90・110それぞれの特徴と実際の見た目
  • ビット演算1行で256ルールすべてに対応する汎用実装コード
  • Rule 30がなぜ貝殻の模様に似るのか(自然界との関係)
  • Rule 110がチューリング完全である理由
  • ArduinoのアナログピンをデジタルOUTPUTとして使う方法

🎥 動作デモ(Rule 30 → 90 → 110 自動切り替え)

Rule 30 → Rule 90 → Rule 110 の3つのルールが64世代ずつ自動切り替えし、終了時には全LEDが点滅するクライマックス演出が入ります。

Arduino UNO R4 MinimaにLEDシールドを装着

Arduino UNO R4 MinimaにLEDシールドを装着


🔍 1次元セルオートマトンとRule番号の仕組み【初心者向け解説】

セルオートマトン(Cellular Automaton) とは、「マス目(セル)が隣のマス目の状態を見て次の自分の状態を決める」という単純な仕組みで動くシミュレーションです。

最も有名な 「ライフゲーム」 は2次元(格子状)のセルオートマトンです。今回は LED が一列に並んでいるため、1次元 バージョンを実装します。

Rule番号の仕組み

1次元セルオートマトンでは、各セルは 左隣・自分・右隣 の3つの状態(ON/OFF)を見て次の世代の状態を決めます。

[左][中][右]  → 3つを見る(例: 1, 0, 1 → 3ビットパターン「101」)
      ↓
    [ 次 ]    → ルール表に従って決まる(例: 0)

3セルがそれぞれ2状態(0か1)なので組み合わせは 2³ = 8通り。この8通りそれぞれに「次の状態(0か1)」を定義すると 2⁸ = 256種類 のルールが作れます。そのルールを8ビットの2進数で表したときの10進数値が「Rule番号」です。

左中右の組み合わせ:  111  110  101  100  011  010  001  000
次の状態(Rule 30):  0    0    0    1    1    1    1    0
                  ↑ 2進数 00011110 → 10進数 30

🌿 Rule 30と貝殻の模様が一致する理由|自然界のセルオートマトン

「光って綺麗」というだけではありません。セルオートマトンは自然界の模倣や計算科学で実際に役立っています。

イモガイの貝殻の模様(Rule 30)

イモガイ(Conus textile)の殻の模様(イメージ)

イモガイ(Conus textile)の殻の模様(イメージ)

イモガイ(Conus textile)の貝殻模様は、Rule 30のパターンと酷似しています。貝殻は成長の過程で「貝殻の端にある細胞が、隣の細胞の色素の有無を見て自分が色素を出すか決める」という単純なメカニズムで模様を作っていると考えられています。

自然界は複雑な計算をせず、単純なルールだけで美しいデザインを生成しているのです。

雪の結晶

雪の結晶も同様に、水分子が「隣の分子の配置を見て結合する」という単純な物理法則から複雑な幾何学模様を作り出しています。

乱数生成への応用(Rule 30)

Rule 30が生成する予測不可能なランダムパターンは、乱数生成アルゴリズムにも応用されています。数学者スティーブン・ウルフラムは「Mathematica」の乱数生成にRule 30を採用していました。


📐 Rule 30・90・110の仕組みと特徴

今回のプログラムでは3つのルールを順番に実行し、自動切り替えしながら無限ループさせています。

Rule 30 — カオス・無秩序の世界

左中右 111 110 101 100 011 010 001 000
次状態 0 0 0 1 1 1 1 0

2進数 0001111010進数 30

特徴:

  • 中央1点から始まり、カオス的で予測不可能なパターンが広がる
  • 左右非対称——自然の荒々しさを感じさせる
  • Rule 30の中央列は乱数として使われるほどランダム性が高い

Rule 90 — フラクタル・幾何学美の世界

左中右 111 110 101 100 011 010 001 000
次状態 0 1 0 1 1 0 1 0

2進数 0101101010進数 90

特徴:

  • 「シェルピンスキーのギャスケット」というフラクタル図形が現れる
  • 完全な左右対称——秩序と美しさが際立つ
  • Next = Left XOR Right という1行の計算式で表現できる
Next = Left XOR Right  // これだけでRule 90が実装できる

Rule 110 — チューリング完全な世界

左中右 111 110 101 100 011 010 001 000
次状態 0 1 1 0 1 1 1 0

2進数 0110111010進数 110

特徴:

  • 周期的なパターンが現れたかと思えば突然崩れ、移動する
  • 2004年にマシュー・クックによってチューリング完全であることが証明された
  • 理論上、このルールだけでどんなプログラムでも実行できる

ウルフラムはセルオートマトンを4つのクラスに分類しており、Rule 110は最も複雑な「クラス4(秩序とカオスの境界)」に属します。

📌 チューリング完全とは

チューリング完全とは「理論上あらゆる計算が実行できる」という性質のこと。Rule 110はこの性質を持つ最もシンプルなシステムの一つです。LEDの点滅パターンが、コンピュータと同等の計算能力を秘めているのです。


💻 Arduinoでの実装コード全文|ビット演算1行で全256ルール対応する方法

ハードウェア構成

Arduino UNO の全ピンに LED がついているシールドを使用しています。

  • D0〜D13(14個): セルオートマトンの「現在の世代」を表示。各LEDが1セルに対応
  • A0〜A5(6個): 現在の世代数を2進数で表示(0〜63世代をカウント)
// アナログピンをデジタル出力として使う
pinMode(A0, OUTPUT);  // pinMode に OUTPUT を指定するだけでOK

// 世代数を2進数で表示
void displayGeneration(int gen) {
  digitalWrite(A0, (gen >> 0) & 1);
  digitalWrite(A1, (gen >> 1) & 1);
  digitalWrite(A2, (gen >> 2) & 1);
  digitalWrite(A3, (gen >> 3) & 1);
  digitalWrite(A4, (gen >> 4) & 1);
  digitalWrite(A5, (gen >> 5) & 1);
}
✅ アナログピンのデジタル出力

Arduino の A0〜A5 は pinMode(A0, OUTPUT) とするだけでデジタル出力として使用できます。analogWrite は不要です。

ビット演算1行で全ルール対応する仕組み

if文を256個書く代わりに、ビット演算1行で任意のルールを計算できます。

int pattern = (left << 2) | (center << 1) | right;  // 3セルを0〜7の整数に
int nextState = (rule >> pattern) & 1;               // ルール番号の該当ビットを取り出す

rule の値を変えるだけで0〜255すべてのルールに対応できます。

エッジの処理(ラップアラウンド)

左端と右端を循環させることで、LEDの列が「輪」のように繋がった世界として動作します。

int leftIdx  = (i - 1 + numCells) % numCells;  // 左端のとき右端を参照
int rightIdx = (i + 1) % numCells;             // 右端のとき左端を参照

完全なコード

/*
  Demo: 3-Rule Cellular Automaton (30 -> 90 -> 110)
  Board: Arduino Uno R4
  D0-D13: Automaton Display
  A0-A5 : Generation Counter
*/

const int cellPins[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
const int numCells = sizeof(cellPins) / sizeof(cellPins[0]);
const int countPins[] = { A0, A1, A2, A3, A4, A5 };
const int numCountBits = sizeof(countPins) / sizeof(countPins[0]);

byte currentCells[14];
byte nextCells[14];

int generation = 0;
const int maxGenerations = 64;

int rules[] = { 30, 90, 110 };
int currentRuleIndex = 0;

void setup() {
  for (int i = 0; i < numCells; i++) pinMode(cellPins[i], OUTPUT);
  for (int i = 0; i < numCountBits; i++) pinMode(countPins[i], OUTPUT);
  resetWorld();
}

void loop() {
  for (int i = 0; i < numCells; i++) digitalWrite(cellPins[i], currentCells[i]);
  for (int i = 0; i < numCountBits; i++) digitalWrite(countPins[i], (generation >> i) & 1);
  delay(120);
  calculateNextGeneration(rules[currentRuleIndex]);
  generation++;
  if (generation >= maxGenerations) {
    finishedEffect();
    currentRuleIndex = (currentRuleIndex + 1) % 3;
    resetWorld();
  }
}

void calculateNextGeneration(int ruleNumber) {
  for (int i = 0; i < numCells; i++) {
    int leftIdx  = (i - 1 + numCells) % numCells;
    int rightIdx = (i + 1) % numCells;
    byte left   = currentCells[leftIdx];
    byte center = currentCells[i];
    byte right  = currentCells[rightIdx];
    int pattern = (left << 2) | (center << 1) | right;
    nextCells[i] = (ruleNumber >> pattern) & 1;
  }
  for (int i = 0; i < numCells; i++) currentCells[i] = nextCells[i];
}

void resetWorld() {
  generation = 0;
  for (int i = 0; i < numCells; i++) currentCells[i] = 0;
  currentCells[numCells / 2] = 1;
}

void finishedEffect() {
  for (int k = 0; k < 10; k++) {
    for (int i = 0; i < numCells; i++) digitalWrite(cellPins[i], HIGH);
    for (int i = 0; i < numCountBits; i++) digitalWrite(countPins[i], HIGH);
    delay(250);
    for (int i = 0; i < numCells; i++) digitalWrite(cellPins[i], LOW);
    for (int i = 0; i < numCountBits; i++) digitalWrite(countPins[i], LOW);
    delay(250);
  }
  delay(500);
}

🛒 使用したハードウェア

この記事のデモで使用した Arduino UNO用LEDシールド(デジタル・アナログ全ピンLED搭載)はBOOTHで販売しています。

この記事のデモで使ったLEDシールドを見る(¥2,000)

全ピン(D0-D13・A0-A5)LED搭載・はんだ付け不要・Arduino UNO R3/R4対応

本来はデバッグ用として設計したシールドですが、全ピン(D0-D13, A0-A5)にLEDがついているという特性を活かして「アルゴリズム可視化装置」として使用しています。

使用機材:

  • Arduino UNO R4 Minima(R3でも動作します)
  • Arduino UNO用LEDシールド
Arduino UNO R4 Minima

Arduino UNO R4 Minima

Arduino UNO R4 Minima starter kit

Arduino UNO R4 Minima starter kit


まとめ

セルオートマトンが動作する様子

セルオートマトンが動作する様子

  • Rule 30:カオス的で予測不可能。乱数生成にも応用される。中央1点から貝殻のような模様が広がる
  • Rule 90Left XOR Right の1行で実装できる完璧なフラクタル(シェルピンスキーのギャスケット)
  • Rule 110:チューリング完全。理論上あらゆる計算が実行できる最もシンプルなシステムの一つ
  • 実装:ビット演算 (rule >> pattern) & 1 の1行で、ルール番号を変えるだけで256種類すべてに対応できる

単純なルールから複雑なパターンが生まれるのは、自然界の仕組みそのものです。

See you …


よくある質問(FAQ)

Q. ArduinoでセルオートマトンをRule番号だけで切り替えるにはどうすればいいですか?

(rule >> pattern) & 1 というビット演算1行で実装できます。rule にRule番号(0〜255)を渡すだけで、すべてのルールに対応します。上記コードの calculateNextGeneration(int ruleNumber) 関数を参照してください。

Q. ArduinoでRule 30・90・110を実装するとどんなパターンが出る?

Rule 30はカオス的でランダムなパターン(乱数生成に応用)、Rule 90は完全対称のフラクタル(シェルピンスキーのギャスケット)、Rule 110はチューリング完全(理論上あらゆる計算が可能)という性質を持ちます。

Q. ArduinoのA0〜A5ピンをデジタルOUTPUTとして使えますか?

使えます。pinMode(A0, OUTPUT) とするだけで、通常のデジタルピンと同様に digitalWrite で制御できます。

Q. このコードはArduino UNO R3でも動作しますか?

動きます。analogWrite を使っていないため、UNO R3・R4 Minimaどちらでも同じコードで動作します。

Q. セルオートマトンの初期状態を変えるとどうなりますか?

resetWorld() 内の currentCells[numCells / 2] = 1 の部分を変更することで初期状態を変えられます。複数点を初期ON状態にすると、各ルールのパターンが大きく変化します。


関連記事


ソースコード・回路図

回路図と今回のArduinoスケッチはGitHubで公開しています。

GitHubでコードを見る