ライフゲームを作る
Systemtapのプログラム例として、できるだけ役に立たなさそうなものを選んでみました。
今回取り上げるのはライフゲームです。非常に単純なルールなので、わかりやすく、かつ(実用上)役に立ちません。
ライフゲームの基本的なルールは、Wikipediaによると
碁盤のような格子があり、一つの格子はセルと呼ばれる。各セルは8つのセルと接している。各セルには「生」と「死」の2つの状態があり、あるセルの次のステップ(世代)の状態は周囲の8つのセルの今の世代における状態により決定される。
誕生
死んでいるセルの周囲に3つの生きているセルがあれば次の世代では生きる(誕生する)。
維持
生きているセルの周囲に2つか3つの生きているセルがあれば次の世代でも生き残る。
死亡
上以外の場合には次の世代では死ぬ。
素晴らしく単純です。
実行コード
では、実際にSystemtapで書いてみたコードを示します。
#!/usr/bin/stap # lifegame global X_MAX=15, Y_MAX=15 global map, tmp global t function showupdate() { print("\033[1;1H\033[J") # clear screen delete map foreach([x,y] in tmp) { printf("\033[%d;%dH", y+1, x+1) # move cursor print("o") map[x,y] = 1 } delete tmp } function lookaround(x,y) { return map[x-1,y-1] + map[x,y-1] + map[x+1,y-1] + map[x-1,y ] + map[x+1,y ] + map[x-1,y+1] + map[x,y+1] + map[x+1,y+1] } probe timer.ms(10) { t ++ if (t == Y_MAX) { showupdate() t = -1 next # skip to the next timer. } # updating just 1 line y = t for (x=0; x < X_MAX; x++) { c = lookaround(x,y) if (c == 3) tmp[x,y] = 1 else if (c == 2 && [x,y] in map) tmp[x,y] = map[x,y] } } probe begin { tmp[6,5] = 1; tmp[5,6] = 1; tmp[4,7] = 1; tmp[5,7] = 1 tmp[6,7] = 1 showupdate() t = -1 }
mapとtmpが二次元配列になっていて、この上でセルの状態を更新していきます。
これらは配列なのでglobalにしています。あと、Y_MAXとX_MAXでマップの大きさを示しています*1。
showupdate()関数
この関数で、mapの内容を更新する(tmpの方が新しい)とともに、画面に書き出しています。
lookaround()関数
この関数は、指定されたセルの周囲8セルのいくつが生きているかを計算して返します。
probe timer.ms(10)
このプローブは、10ミリ秒毎に呼び出されます。10ミリ秒ごとにY軸方向に1ラインずつ処理を行って、すべてのラインを処理したら、showupdate()関数を呼ぶようになっています。
- [x,y] in map というのは、map[x,y]が存在するかどうかを確認する式になります。存在すれば真、しなければ偽が返ります。
- 1ラインずつ処理しているのは、一回のプローブハンドラ内で実行できる処理の数が限られているからです*3。この上限は、無限ループや実質的に非常に時間のかかる処理を検出するために設けられています。
probe begin
このプローブは、スクリプトの実効が開始される直前に呼び出されます。一般的には変数などの初期化処理をここで行います。