セルオートマトンでシミュレーションをしよう(2)

3. グラフィックス版1次元CA

さて、簡単なグラフィック・ライブラリーを使って前と同じセルオートマトンの グラフィックス版を作ってみよう。 以下ではグラフィック・ライブラリーEGGXを使う。「EGGXチュートリアル」 の内容は理解しているものとして解説する。

前のプログラムの主要部分はそのまま残して、グラフィックス用の準備部分と 表示部分を書き加えてみたのが次のプログラムである

#include<stdio.h>
#include<stdlib.h>
#include<eggxlib.h>
#define N 70
#define EDGE 10
main(){
 int i,j,step,seed;
 int s[N], snew[N];
 int left,center,right;
 int rule[8];
 int win;

/*  preparation for graphics */
 win=eggx_gopen(N*EDGE,EDGE);
 eggx_layer(win,0,1);
 eggx_gsetbgcolor(win,"white");
 eggx_newcolor(win,"blue");

/* read data from keyboard */
 scanf("%d%d%d%d%d%d%d%d",&rule[0],&rule[1],&rule[2],&rule[3],&rule[4],&rule[5],&rule[6],&rule[7]);
 scanf("%d%d",&step, &seed);
 srandom(seed);

/* set initial random state */
 for(i=0; i< N; ++i)s[i]= random()%2;

/*start*/
 for(j=0; j< step; ++j){

/* clear screen */
  eggx_gclr(win);

  for(i=0; i< N; ++i){
   if(i==0){
     left = s[N-1];
     center = s[0];
     right = s[1];}
   else if(i==N-1){
     left = s[N-2];
     center = s[N-1];
     right = s[0];}
   else {
     left = s[i-1];
     center = s[i];
     right = s[i+1];}

   if(left == 0 && center == 0 && right ==0)snew[i] = rule[0];
   else if(left == 0 && center == 0 && right == 1)snew[i] = rule[1];
   else if(left == 0 && center == 1 && right == 0)snew[i] = rule[2];
   else if(left == 0 && center == 1 && right == 1)snew[i] = rule[3];
   else if(left == 1 && center == 0 && right == 0)snew[i] = rule[4];
   else if(left == 1 && center == 0 && right == 1)snew[i] = rule[5];
   else if(left == 1 && center == 1 && right == 0)snew[i] = rule[6];
   else if(left == 1 && center == 1 && right == 1)snew[i] = rule[7];
 }

  for(i=0; i< N; ++i)s[i]=snew[i];

/* display one line */
  for(i=0;i<N;++i) if(s[i]==1) eggx_fillrect(win,i*EDGE,0,EDGE,EDGE);
   eggx_copylayer(win,1,0);
   eggx_msleep(100);
/* end of one line */
  }
 eggx_msleep(5000);
}

3B. グラフィックス版1次元CA(2)

 上のプログラム、各時刻のセルの状態を表示するという方針はいいのだが、前のグラフィックなしバージョンのほうが変化の様子がよくわかってよかったような気もする。 理由は、以前の時刻の状態も画面上に残っているからである。 せっかく画面にも余裕があるのだから、グラフィック版でも違う時刻の状態を縦に並べて表示してみよう。
#include<stdio.h>
#include<stdlib.h>
#include<eggxlib.h>
#define N 200
#define M 100
#define EDGE 4
main(){
 int i,j,k,step,seed;
 int s[N], snew[N];
 int left,center,right;
 int rule[8];
 int win;


/*  preparation for graphics */
 win=eggx_gopen(N*EDGE,M*EDGE);
 eggx_layer(win,0,1);
 eggx_gsetbgcolor(win,"white");
 eggx_newcolor(win,"blue");

/* read data from keyboard */
 scanf("%d%d%d%d%d%d%d%d",&rule[0],&rule[1],&rule[2],&rule[3],&rule[4],&rule[5],&rule[6],&rule[7]);
 scanf("%d%d",&step, &seed);
 srandom(seed);

/* set initial random state */
 for(i=0; i<N; ++i)s[i]= random()%2;

/* start */
 for(j=0; j<step; ++j){

/* clear screen */
  eggx_gclr(win);
/*  end of clear screen */

  for(k=0; k<M; ++k){
  for(i=0; i< N; ++i){
   if(i==0){
     left = s[N-1];
     center = s[0];
     right = s[1];}
   else if(i==N-1){
     left = s[N-2];
     center = s[N-1];
     right = s[0];}
   else {
     left = s[i-1];
     center = s[i];
     right = s[i+1];}

   if(left == 0 && center == 0 && right ==0)snew[i] = rule[0];
   else if(left == 0 && center == 0 && right == 1)snew[i] = rule[1];
   else if(left == 0 && center == 1 && right == 0)snew[i] = rule[2];
   else if(left == 0 && center == 1 && right == 1)snew[i] = rule[3];
   else if(left == 1 && center == 0 && right == 0)snew[i] = rule[4];
   else if(left == 1 && center == 0 && right == 1)snew[i] = rule[5];
   else if(left == 1 && center == 1 && right == 0)snew[i] = rule[6];
   else if(left == 1 && center == 1 && right == 1)snew[i] = rule[7];
 }

  for(i=0; i<N; ++i)s[i]=snew[i];

/* display one line */
  for(i=0;i<N;++i) if(s[i]==1) eggx_fillrect(win,i*EDGE,k*EDGE,EDGE,EDGE);
   eggx_copylayer(win,1,0);
   eggx_msleep(20);
/* end of one line */
  }
  eggx_msleep(500);
 }
 eggx_msleep(10000);
}

プログラムの変更点概要は以下のとおり

  1. システムサイズ(横幅)はNセル、縦方向に下からMステップ分を表示したところで500ミリ秒休んで画面をクリアし、続きを一番下から再開する。これをstep回繰り返す。つまり、このプログラムでは、実際の計算ステップ数はstep×M である。
  2. 画面上のひとマスのサイズははEDGEドット
  3. double bufferingを用いてlayer 1に1行描くごとにその結果をlayer 0に転送している。 1行描くたびに20ミリ秒の休みをいれているが、最低でもこの程度は休みをいれたほうが表示がなめらかになる
  4. 表示は値が1のセルについてだけ青で四角を描いている。全体を最初に背景色で塗りつぶしているので、0のマスについては何もしなくてよい
  5. stept回繰り返したのち、10秒停止して終了

3C. グラフィックス版1次元CA(3)

 グラフィック無し版の最後に掲載した「もうちょっとスマートなプログラム」のグラフィック版も掲載しておく。興味がなければ、飛ばして最後の「発展」にいってください
#include<stdio.h>
#include<stdlib.h>
#include<eggxlib.h>
#define N 200
#define M 100
#define EDGE 4
main(){
 int i,j,k,step,seed;
 int s[N], snew[N];
 int left,center,right;
 int rule[8];
 int win;

/*  preparation for graphics */
 win=gopen(N*EDGE,M*EDGE);
 layer(win,0,1);
 gsetbgcolor(win,"white");
 newcolor(win,"blue");

/* read data from keyboard */
 scanf("%d%d%d%d%d%d%d%d",&rule[0],&rule[1],&rule[2],&rule[3],&rule[4],&rule[5],&rule[6],&rule[7]);
 scanf("%d%d",&step, &seed);
 srandom(seed);

/* set initial random state */
 for(i=0;i<N;++i)s[i]= random()%2;

/* start */
 for(j=0;j< step;++j){

/* clear screen */
  gclr(win);

  for(k=0; k< step; ++k){
  for(j=0;j< M;++j){
   snew[0] = rule[s[N-1]*4+s[0]*2+s[1]];
   for(i=1;i< N-1;++i){
    snew[i] = rule[s[i-1]*4+s[i]*2+s[i+1]];}
   snew[N-1] = rule[s[N-2]*4+s[N-1]*2+s[0]];

  for(i=0;i< N;++i)s[i]=snew[i];

/* display one line */
  for(i=0;i< N;++i) if(s[i]==1) fillrect(win,i*EDGE,k*EDGE,EDGE,EDGE);
   copylayer(win,1,0);
   msleep(20);
/* end of one line */
  }
  msleep(500);
 }
 msleep(10000);
}

発展

2状態5近傍totalistic rule(次の時刻での状態は近傍内にある1の数だけで決まり、その配置によらない。たとえば、5近傍の状態が10000,01000,00100,00010,00001の5通りすべてについて、真ん中のセルは次の時刻で同じ状態になる)のセルオートマトンのグラフィック版を作る。特にrule 20、つまり
近傍内の1の数012345
次の時刻での真ん中の状態001010
の場合をやってみると面白い(初期状態はランダム配置とするのがよい)


次へ