プログラミング講座 第7回

第7回の目標:練習問題をこなしてプログラミングに慣れる。

1.セルオートマトン(1次元)

セル(四角)が1列に並ぶ世界を考える。セルには生き物がいる場合(1)といない場合(0)がある。セルAとAの両隣(の計3つのセル)にどのように生き物がいるかで、次の世代のセルAに生き物がいるかどうかが決まる。

この次の世代を決めるルールを次のように表す。

ある世代のAの左隣とAとAの右隣次の世代のA
111
110
101
100
011
010
001
000

101個のセルを考え、初期値として、中央のセルは1、その他のセルは0とする。このセル達に対して、先のルールを適用して次の世代、次の世代を計算していく。

横1行が一つの世代で、その1行下に次の世代、さらにその1行下に次の世代である。60世代を図示すると以下になる。

例題1 次の世代を決めるルールを変更して、再描画してみよ。(再計算ボタンは上にある)

例:HTML

  ~省略~

  <head>
    <script src="cellAutomaton.js"></script>
  </head>

  ~省略~

  <canvas id="ID1246" width="505" height="300">
  </canvas>

  ~省略~
  

例:javascript(cellAutomaton.js)

//ある世代と次の世代の対応
let rule = [["111", 0],
            ["110", 1],
            ["101", 0],
            ["100", 1],
            ["011", 1],
            ["010", 0],
            ["001", 1],
            ["000", 0]];

function applyRule(left_A, A, right_A) {
  let i;
  let oldGeneration = String(left_A) + String(A) + String(right_A);

  for (i = 0; i < rule.length; i++) {
    if (rule[i][0] == oldGeneration) {
      return rule[i][1];
    }
  }
}

function nextGeneration(world) {
  let newWorld = [];

  for (i = 0; i <= 100; i++) {
    // 世界の左端
    if (i == 0) {
      newWorld[i] = applyRule(0         , world[i], world[i+1]);
    // 世界の右端
    } else if (i == 100) {
      newWorld[i] = applyRule(world[i-1], world[i], 0         );
    // 世界の端でないところ
    } else {
      newWorld[i] = applyRule(world[i-1], world[i], world[i+1]);
    }
  }
  return newWorld;
}


function draw1generation (world, context, offset) {
  let i;
  let rect_size = 5;

  context.clearRect(0,offset,505,rect_size);
  context.fillStyle = "rgb(0, 0, 0)";

  for (i=0; i <= 100; i++) {
    if (world[i] == 1) {
      context.fillRect(i*rect_size, offset, rect_size, rect_size);
    }
  }
}

function cellAutomaton() {
  let world=[];
  let canvas, i;

  // オートマトンの世界の初期化
  for (i = 0; i <= 100; i++) {
    world[i] = 0;
  }
  world[50] = 1; // 中央に1匹生物を置く

  // 描画準備
  canvas = document.getElementById('ID1246');
  context = canvas.getContext('2d');

  for (i = 0; i < 60; i++) {
    draw1generation(world, context, i*5);
    world = nextGeneration(world);
  }
}

window.onload = function () {
  cellAutomaton();
}
  

javascriptの2行目のruleが次の世代を決める。上記の例では、テキストボックスの入力値を読み取って、ルールを変更するような処理は省略されている。

applyRule関数はruleを用いて、次の世代のあるセルに生き物がいるかいないかを判定する。

nextGeneration関数はapplyRule関数を用いて、101セルある世界の次の世代を計算する。

draw1generation関数は1世代分を横1列に描画する。

cellAutomaton関数では、オートマトンの世界を宣言して初期化し、nextGeneration関数で計算しながら60世代分を描画している。

例題2 ライフゲームを実装せよ。ライフゲーム:2次元のセルオートマトンである。セルが縦横に並ぶ世界があり、セルには生き物がいる場合といない場合がある。あるセルAの周囲8マスの生き物の数により、次の世代でセルAに生き物がいるかどうかが決まる。詳しいルールは下にある。世代交替させて生き物たちが繁殖していくのを眺めるものである。

元になるhtmlとcssとjavascriptのセットをダウンロード

下のように動く。このサンプルではランダムに2割りくらいのセルに生き物を配置してライフゲームを開始している。

第6回へ 目次へ 第8回へ