2021年5月14日
プログラミング
A-Frameで実装するセル・オートマトン(ライフゲーム)
はじめに
この記事では、簡単にWebVRシーンを作成することのできるA-Frameというライブラリを使ってセル・オートマトンを実装していきます。
セル・オートマトンとは
セル・オートマトンとはシンプルなルールによる計算モデルです。
「セル」とは、表計算のマス目や「細胞」を意味する言葉です。
ここでは、オセロのマス目と石のようなものをイメージするとよいでしょう。
それぞれのセルは、連続な状態を持ちます。そして、まさにオセロのように、
各セルの状態は周りのセルの状態によって変化していきます。
セルは、周りのセルの状態により、状態を更新していきます。
引用: 『作って動かすALife』
セル・オートマトンは、セルの次の状態を決定するためのルール(分岐)を決めておくことで、やがて様々な生命現象や自然界のパターンを想起させる、複雑な結果を見せてくれます。
上記の引用文のようにALife(Artificial Life: 人工生命)の文脈で例に出される、自律的なアルゴリズムのひとつです。
セル・オートマトンのモデルは一次元から多次元までに用いることができ、またルールも多数存在しますが、この記事では代表的なルールである「ライフゲーム」を用いて二次元と三次元で実装してみます。
完成イメージ
二次元セル・オートマトン
(変化がなくなっていたら中心の再生アイコンを押してみてください)
三次元セル・オートマトン
こんな人におすすめ
- Javascriptが書けてクリエイティブコーディングに興味がある人
- クリエイティブコーディングの新たな表現を発見したい人
※今回の記事はJavaScriptの実装が中心となりますので、A-Frameを知らない方・触ったことのない方でも問題ありません。
また別のライブラリや言語に落とし込む参考にもなるかなと思います。
A-Frameの基本的な書き方を知りたい方は、まずこちらの記事をご覧ください。
二次元セル・オートマトン
それではまずは二次元セル・オートマトンを実装してみましょう。
基本的なセルの状態やルールから整理していきます。
セルの状態
各セルの状態は2進数の「0」か「1」で表します。
「0」は死亡、「1」は生存を意味します。
今回はそれぞれ「0」を白、「1」を黒(or グレー)として実装していきます。
ルール
二次元セル・オートマトンは、隣り合う4つのセルの状態に影響をうけるノイマン近傍と
斜めも含めた8つのセルの状態に影響をうけるムーア近傍がありますが、
本記事ではノイマン近傍を採用していきます。
「人口過剰」
中心のセル:生存(1)→死亡(2)
生存するセルが3つより多くの生存セルに囲まれている場合、人口過剰となり中心のセルは死亡する
「均衡状態」
中心のセル:生存(1)→生存(1)
生存するセルが2〜3つの生存セルに囲まれている場合、中心のセルはそのまま生存となる。
「人口過疎」
中心のセル:生存(1)→死亡(0)
生存するセルの周りを囲む生存セルが2つより少ないとき、中心のセルは死亡する。
「再生」
中心のセル:死亡(0)→生存(1)
死亡しているセルがちょうど3つの生存するセルに囲まれているとき、中心のセルは生存状態になる。
動かしてみる
それではこのルールを早速実装していきます。
今回A-Frameのa-boxを一つのセルと見立てて実装していきます。
まずはhtml。
html側にca-containerというa-entityを用意しました。この中にセルを格納していこうと思います。
js側では以下の処理を準備をしていきます。
- 配列の用意
- セルの状態を保存しておく二次元配列を用意
- セル(box)オブジェクトを入れておく配列を用意
- boxをca-containerに格納
- 最初のセルの状態をランダムに決定
- セルの次の状態を判定
- 描写
配列の用意
まずは各変数の定義と配列の用意をします。
let width;
let height;
let cellSize;
let columns;
let rows;
let boardState;
let nextState;
let elArray;
let onColor = "black";
let offColor = "white";
let caContainer = null;
let aScene = null;
const initDefinition = () => {
width = 20;
height = 20;
cellSize = 1;
columns = Math.floor(width / cellSize);
rows = Math.floor(height / cellSize);
// 現状のセルの状態を保存するための二次元配列
currentState = new Array(columns);
for (let i = 0; i < columns; i++) {
currentState[i] = new Array(rows);
}
// 次のセルの状態を保存するための二次元配列
nextState = new Array(columns);
for (i = 0; i < columns; i++) {
nextState[i] = new Array(rows);
}
// boxオブジェクトを保存するための二次元配列
elArray = new Array(columns);
for (i = 0; i < columns; i++) {
elArray[i] = new Array(rows);
}
};
boxをca-containerに格納
最初にセルであるboxオブジェクトを生成し、html側に入れます。
また、先ほど作ったelArray
にboxオブジェクトを格納します。
const initAddGrid = () => {
for (let x = 0; x < columns; x++) {
for (let y = 0; y < rows; y++) {
const newEl = document.createElement("a-box");
newEl.setAttribute("color", offColor);
newEl.setAttribute("scale", `${cellSize} ${cellSize} ${cellSize}`);
newEl.setAttribute("position", `${y} 0 ${x}`);
newEl.setAttribute("opacity", "0.5");
caContainer.appendChild(newEl);
elArray[x][y] = newEl;
}
}
};
最初のセルの状態をランダムに決定
今回初期のセルの状態はランダムで配置します。
const initRandomSet = () => {
for (let i = 0; i < columns; i++) {
for (let j = 0; j < rows; j++) {
if (i == 0 || j == 0 || i == columns - 1 || j == rows - 1) {
currentState[i][j] = 0;
} else {
currentState[i][j] = Math.round(Math.random(2));
}
}
}
};
最初の状態をランダムに決定するとき、セルの外側一列を最初に排除(状態:死亡)としています。
外側(つまり隣り合うセルが2つ or 3つ)のセルは、一度生存状態にしてしまうと
これらは生存と死亡を1フレームごとに繰り返すようになります。
▲gif: p5.jsで試した例
あまり綺麗な状態にはならないため、内側で多様に動くセルを確認するためにも外側のセルは排除して進めます。
セルの次の状態を判定
最初の状態をランダムに決定したら、次の状態を判定していきましょう。
ここで最初にご紹介した「人口過剰」「均衡状態」などを判定していき、セルの状態を配列に保存していきます。
function generate() {
for (let x = 1; x < columns - 1; x++) {
for (let y = 1; y < rows - 1; y++) {
let neighbors = 0;
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
neighbors += currentState[x + i][y + j];
}
}
neighbors -= currentState[x][y];
// 人口過疎
if (currentState[x][y] == 1 && neighbors < 2) nextState[x][y] = 0;
// 人口過剰
else if (currentState[x][y] == 1 && neighbors > 3)
nextState[x][y] = 0;
// 再生
else if (currentState[x][y] == 0 && neighbors == 3)
nextState[x][y] = 1;
// 均衡状態
else nextState[x][y] = currentState[x][y];
}
}
let temp = currentState;
currentState = nextState;
nextState = temp;
}
描写
最後にこの判定をフレームごとに繰り返して、死亡であれば白、生存であれば黒とboxのカラーを変更していく処理を追加します。
const drawCa = () => {
generate();
for (let i = 0; i < columns; i++) {
for (let j = 0; j < rows; j++) {
if (currentState[i][j] == 1) elArray[i][j].setAttribute("color", onColor);
else elArray[i][j].setAttribute("color", offColor);
}
}
};
完成!
動かすとこのような感じになります。
「1」の時だけアニメーションをつけたり、高さを変えたり…などアレンジしても楽しそうですね。
コードの全文はNEORTより確認できます
三次元セル・オートマトン
次に三次元セル・オートマトンに挑戦します。
と言ってもあまりやることは変えず、
columns, rowsの二次元で管理していたboxの位置をaislesも加えた三次元で保存することなどを追加しています。
box数が増え重くなってしまったので1フレームあたりの時間を長く取っています、、
また、せっかくなので全体をグルグル回転させるアニメーションもca-container自体につけてみて、
ついでに死亡から再生を果たしたセルに関しては薄いグレーをつけるなど色数も増やしてみました。
コードの全文はNEORTより確認できます
まとめ
二次元、三次元セル・オートマトンをA-Frameで実装してみました。
セル・オートマトンを始めとするALifeの分野はまだまだ面白いプログラムがたくさんあるので、また実装していきたいなと思います。
さて、記事のメインコンテンツはここまでですが以下に「付録」「参考」と題して
「付録」ではセル・オートマトンを多角的に楽しむ試みの紹介、
「参考」では今回記事を書くに当たって参考にさせていただいたコードや書籍を紹介しております。
どちらもより深くセル・オートマトンを楽しむためにぴったりの資料ですので
ぜひ併せてお楽しみください。
付録
セル・オートマトンを飲み会で孤立しないためのシミュレーションプログラムとして用いる記事です。(?)
とりあえこちらを読めばセル・オートマトンを触ってみたくなります。
プログラマであれば、
それはコミュニケーション上の課題ではなく、プログラミングで解決するべき課題である、
と認識すべきかと思います。
やくしまるえつこ「わたしは人類」
「わたしは人類」ではMVにセル・オートマトンの演出を使用しています。
そもそもこの楽曲自体、微生物のDNA情報を楽曲制作に用いるなど音楽を生命シミュレーションとみなすような挑戦的な取り組みがされており
セル・オートマトンも楽曲のコンセプトに沿うかたちで引用されています。
参考
この記事を書くにあたって以下のコードを参考に改変させていただきました。
コード参考
p5.js official example以下の書籍は、セル・オートマトンやALife、その実装方法についての理解を深めるのに最適な本ですのでぜひ参考にしてください。
『作って動かすALife』『ジェネラティブ・アート』
プログラミングのお仕事に関するご相談
Bageleeの運営会社、palanではプログラミングに関するお仕事のご相談を無料で承っております。
zoomなどのオンラインミーティング、お電話、貴社への訪問、いずれも可能です。
ぜひお気軽にご相談ください。
この記事は
参考になりましたか?
1
0
関連記事
簡単に自分で作れるWebAR
「palanAR」はオンラインで簡単に作れるWebAR作成ツールです。WebARとはアプリを使用せずに、Webサイト上でARを体験できる新しい技術です。
palanARへpalanでは一緒に働く仲間を募集しています
正社員や業務委託、アルバイトやインターンなど雇用形態にこだわらず、
ベテランの方から業界未経験の方まで様々なかたのお力をお借りしたいと考えております。
運営メンバー
Eishi Saito 総務
SIerやスタートアップ、フリーランスを経て2016年11月にpalan(旧eishis)を設立。 マーケター・ディレクター・エンジニアなど何でも屋。 COBOLからReactまで色んなことやります。
sasakki デザイナー
アメリカの大学を卒業後、日本、シンガポールでデザイナーとして活動。
やまかわたかし デザイナー
フロントエンドデザイナー。デザインからHTML / CSS、JSの実装を担当しています。最近はReactやReact Nativeをよく触っています。
Sayaka Osanai デザイナー
Sketchだいすきプロダクトデザイナー。シンプルだけどちょっとかわいいデザインが得意。 好きな食べものは生ハムとお寿司とカレーです。
はらた エンジニア
サーバーサイドエンジニア Ruby on Railsを使った開発を行なっています
こぼり ともろう エンジニア
サーバーサイドエンジニア。SIerを経て2019年7月に入社。日々学習しながらRuby on Railsを使った開発を行っています。
ささい エンジニア
フロントエンドエンジニア WebGLとReactが強みと言えるように頑張ってます。
Damien
WebAR/VRの企画・開発をやっています。森に住んでいます。
ゲスト bagelee
かっきー
まりな
suzuki
miyagi
ogawa
雑食デザイナー。UI/UXデザインやコーディング、時々フロントエンドやってます。最近はARも。
いわもと
デザイナーをしています。 好きな食べ物はラーメンです。
taishi kobari
フロントエンドの開発を主に担当してます。Blitz.js好きです。
kubota shogo
サーバーサイドエンジニア。Ruby on Railsを使った開発を行いつつ月500kmほど走っています!
nishi tomoya
aihara
グラフィックデザイナーから、フロントエンドエンジニアになりました。最近はWebAR/VRの開発や、Blender、Unityを触っています。モノづくりとワンコが好きです。
nagao
SIerを経てアプリのエンジニアに。xR業界に興味があり、unityを使って開発をしたりしています。
Kainuma
サーバーサイドエンジニア Ruby on Railsを使った開発を行なっています
sugimoto
asama
ando
iwasawa ayane
yoko oshimo
異業界からやってきたデザイナー。palanARのUIをメインに担当してます。これからたくさん吸収していきます!