furutatsuのメモ帳

ぼくがソフトウェアを設定したことや組んだプログラムを見返すためのブログです。

ProcessingでLangtonのループを作ってみた

追加

ルールの引用元を記載した。(2012年4月18日)

目的

Processingというグラフィックに特化したプログラム言語で、Langtonのループを動かしてみた。

Langtonのループって

Christopher Langtonが考案したセルオートマトンによる自己複製のモデルである。
一つのセルに0-7の状態があり、この(中央にある)セルの状態とこの上下左右のセルの状態からルールを適用することによってこの中央のセルの値が変わる。
これを繰り返すことでループを増殖していく。

仕様

  • 任意のキーでポーズ
  • ルールが適用できなかったら初期状態にして動かす
  • 状態の色
    • 0: ■
    • 1: □
    • 2:
    • 3:
    • 4:
    • 5:
    • 6:
    • 7:
  • ルールはruletablerepositoryLangtons-Loops.tableを使いました。

コードの構成

  • void initCellSpace(): セルを初期化する。
  • boolean nextState(): 次のセルを更新する。ここでルールの適用を行う。ルールが適用できたらtrue、そうでなければ(ルールを全部探索したら)falseを返す。

ソースコード

int rectSize = 5;
int[][] space;
int[][] nextSpace;
ArrayList rules;
boolean running = true;
int count = 0;

// Init state
int[][] langLoop = {
  {0,2,2,2,2,2,2,2,2,0,0,0,0,0,0},
  {2,1,7,0,1,4,0,1,4,2,0,0,0,0,0},
  {2,0,2,2,2,2,2,2,0,2,0,0,0,0,0},
  {2,7,2,0,0,0,0,2,1,2,0,0,0,0,0},
  {2,1,2,0,0,0,0,2,1,2,0,0,0,0,0},
  {2,0,2,0,0,0,0,2,1,2,0,0,0,0,0},
  {2,7,2,0,0,0,0,2,1,2,0,0,0,0,0},
  {2,1,2,2,2,2,2,2,1,2,2,2,2,2,0},
  {2,0,7,1,0,7,1,0,7,1,1,1,1,1,2},
  {0,2,2,2,2,2,2,2,2,2,2,2,2,2,0}
};

void setup() {
  size(800, 800);
  colorMode(RGB, 100);
  int sizeX = width/rectSize;  // Number of rect for x and y axis
  int sizeY = height/rectSize;
  println("X = " + sizeX);
  println("Y = " + sizeY);
  space = new int[sizeY][sizeX];
  nextSpace = new int[sizeY][sizeX];
  rules = new ArrayList();
  
  // init space
  initCellSpace();
  
  // load rules from a file "Langtons-Loops.table"
  // and add loaded rule to list
  BufferedReader br = createReader("Langtons-Loops.table");
  String l;
  try {
    while((l = br.readLine()) != null) {
      // like 00000, load numeric format
      if(Character.isDigit(l.charAt(0))) rules.add(l);
    }
  } catch (IOException e) {
    e.printStackTrace();
  }
  println("Loaded " + rules.size() + " rules.");
  frameRate(120);
}

void draw() {
  if(running) {
    if(nextState()) {
      //println(count);    
      for(int i=0; i<space.length; i++) {
        for(int j=0; j<space[i].length; j++) {
          fill(stateToColor(space[i][j]));
          rect(j*rectSize, i*rectSize, rectSize, rectSize);
        }
      }  
      count++;
    } else {
      initCellSpace();
      count = 0;
    }
  }
}

// stop or run
void keyPressed() {
  if (running) {
    println("Stop!");
  } else {
    println("Play!");
  }
  running = !running;
}

void initCellSpace() {
  for(int i=0; i<space.length; i++) {
    for(int j=0; j<space[i].length; j++) {
      space[i][j] = 0;
    }
  }
  
  // put init state on 'space' at center
  int offsetX = space.length/2;
  int offsetY = space[0].length/2;
  for(int i=0; i<langLoop.length; i++) {
    for(int j=0; j<langLoop[i].length; j++) {
      space[offsetY-langLoop.length/2+i][offsetX-langLoop.length/2+j] = langLoop[i][j];
    }
  }
}

color stateToColor(int state) {
  color cl = 0;
  switch(state) {
    case  0: cl = color(0,0,0); break;
    case  1: cl = color(100,100,100); break;
    case  2: cl = color(50,50,50); break;
    case  3: cl = color(0,100,0); break;
    case  4: cl = color(0,50,50); break;
    case  5: cl = color(0,0,100); break;
    case  6: cl = color(50,0,50); break;
    case  7: cl = color(100,100,100); break;
  }
  return cl;
}

// n_states:8(0-7)
// neighborhood:vonNeumann (Center, North, East, South, West) 
// symmetries:rotate4
boolean nextState() {
  for(int i=0; i<space.length; i++) {
    for(int j=0; j<space[i].length; j++) {
      // 読み込み
      int vN[] = new int[5];
      int next = space[i][j];
      
      vN[0] = space[i][j];   // Center
      vN[1] = (i == 0)                 ? space[space.length-1][j] : space[i-1][j]; // North 1 -> 2 -> 3 -> 4
      vN[2] = (j == space[i].length-1) ? space[i][0]              : space[i][j+1]; // East  2 -> 3 -> 4 -> 1
      vN[3] = (i == space.length-1)    ? space[0][j]              : space[i+1][j]; // South 3 -> 4 -> 1 -> 2
      vN[4] = (j == 0)                 ? space[i][space.length-1] : space[i][j-1]; // Weat  4 -> 1 -> 2 -> 3
      
      // 比較・回転
      String rule;
      int k;
      for(k=0; k<rules.size(); k++) {
        rule = (String)rules.get(k);
        
        // XXX: 片っ端から比較または回転する
        if (vN[0] == rule.charAt(0)-'0' && vN[1] == rule.charAt(1)-'0' &&
            vN[2] == rule.charAt(2)-'0' && vN[3] == rule.charAt(3)-'0' && vN[4] == rule.charAt(4)-'0') {
          next = rule.charAt(5)-'0';
          break;
        } else if (vN[0] == rule.charAt(0)-'0' && vN[1] == rule.charAt(2)-'0' &&
            vN[2] == rule.charAt(3)-'0' && vN[3] == rule.charAt(4)-'0' && vN[4] == rule.charAt(1)-'0') {
          next = rule.charAt(5)-'0';
          break;
        } else if (vN[0] == rule.charAt(0)-'0' && vN[1] == rule.charAt(3)-'0' &&
            vN[2] == rule.charAt(4)-'0' && vN[3] == rule.charAt(1)-'0' && vN[4] == rule.charAt(2)-'0') {
          next = rule.charAt(5)-'0';
          break;
        } else if (vN[0] == rule.charAt(0)-'0' && vN[1] == rule.charAt(4)-'0' &&
            vN[2] == rule.charAt(1)-'0' && vN[3] == rule.charAt(2)-'0' && vN[4] == rule.charAt(3)-'0') {
          next = rule.charAt(5)-'0';
          break;
        }
      }
      if(k == rules.size()) {
        return false;
      } else {
        nextSpace[i][j] = next;
      }
    }
  }
  
  // 全更新
  for(int i=0; i<space.length; i++) {
    for(int j=0; j<space[i].length; j++) {
      space[i][j] = nextSpace[i][j];
    }
  }
  return true;
}

実行結果

f:id:nyannyanboo:20120417230241p:plain