ライフゲームを作ってみる(後編)

こんにちは、きうちです。

前回はライフゲームを作ってみました。
(この記事の最後にソース公開のリンクがありますので良かったらご覧下さい。)

今回は、そこに以下のように手を加えたいと思います。

  • 開始・停止をボタン押下でできるようにする
  • セルをクリックするとそのセルをONにしたりOFFにしたりできるようにする
  • プリセットの種類を増やし、ボタン押下で切り替えられるようにする

★ ★ ★

まずは「開始・停止をボタン押下でできるようにする」をやってみます。

ピクチャーボックスの右横にボタンを7つ並べます。

文言を上4つについてそれぞれ開始、停止、クリア、プリセット1 – 基本とします。のこり3つはあとで使用します。

各々、名前はbtnStart, btnStop, btnClear, btnPreset01 とします。

開始ボタンを押したときに初めて開始させたいので、timNextGenerationのEnabledはfalseにします。
また、少し世代が進むのを速くしたいので、Intervalを100にします。

ボタン押下時処理を次のようにします。

        private void btnStart_Click(object sender, EventArgs e)
        {
            timNextGeneration.Start();
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            timNextGeneration.Stop();
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            ClearField(field);
            pictureBox1.Refresh();
        }

        private void btnPreset01_Click(object sender, EventArgs e)
        {
            ClearField(field);
            PresetField(field);
            pictureBox1.Refresh();
        }

実行するとこんな感じ。画面右部にボタンが並びました!
開始ボタンを押すと世代が進行し、停止ボタンを押すと止まります。クリアボタンで画面がクリアされます。プリセット1ボタンで固定物体と振動子が並びます。

★ ★ ★

続いて、「セルをクリックするとそのセルをONにしたりOFFにしたりできるようにする」をやってみます。

ピクチャーボックスにMouseClickイベントを追加します。処理は以下のようにします。

        private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
        {
            int i = (int)(e.Y / 8.0);
            int j = (int)(e.X / 8.0);
            field[i, j] = 1 - field[i, j];
            pictureBox1.Invalidate();
        }

これで、クリックしたセルがON/OFFされます。

例えばこれで、こんな風にお花の絵を描いてあげて…

…お花に見えないかもしれないですが、お花です!^^;

そして、開始させてみます。なかなかいい動きしてますね。

 
★ ★ ★

さらに「プリセットの種類を増やし、ボタン押下で切り替えられるようにする」をやってみます。

「移動物体」をプリセットとして追加します。

プリセット1ボタンの下のボタンを「プリセット2 – 移動物体」とし、イベント・処理を次のようにします。

        private void btnPreset02_Click(object sender, EventArgs e)
        {
            PresetField2(field);
            pictureBox1.Refresh();
        }

        private void PresetField2(int[,] f)
        {
            ClearField(f);

            int x = 10;
            int y = 1;
            long[] data = { 0b001, 0b101, 0b011 };
            SetField(f, x, y, data);

            x = 10;
            y = 12;
            data = new long[] { 0b10010, 0b00001, 0b10001, 0b01111 };
            SetField(f, x, y, data);

            x = 10;
            y = 20;
            data = new long[] { 0b000110, 0b111011, 0b111110, 0b011100 };
            SetField(f, x, y, data);

            x = 10;
            y = 30;
            data = new long[] { 0b0000110, 0b1111011, 0b1111110, 0b0111100 };
            SetField(f, x, y, data);
        }

        private void SetField(int[,] f, int x, int y, long[] data)
        {
            for (int i = 0; i < data.Length; i++)
            {
                int j = 0;
                long a = data[i];
                while (a > 0)
                {
                    f[y + i, x - j] = (int)(a & 1);
                    a >>= 1;
                    j++;
                }
            }
        }

プリセットをするのに「基本」のときのように1セルずつ設定するのは大変なので、2進数でデータを表現する方法を使ってみました。
各ビット、1ならセルがON、0ならOFFということになります。
SetFieldメソッドで1ビットずつ取り出してfieldに格納しています。
X座標を x + j ではなく x – j としていますが、これはデータとして定義した数値を下位ビットから順に処理しているためです。

実行してプリセット2ボタンを押すと、こう!

開始すると、こんな風に!

なお、一番上の一番小さい、右下に向かって飛び続けている塊は「グライダー」と呼ばれるパターンです。

★ ★ ★

グライダーを打ち続けるパターン、「グライダー銃」をやってみようと思います。

データを用意するのがなかなか大変ですねー。

        private void btnPreset03_Click(object sender, EventArgs e)
        {
            PresetField3(field);
            pictureBox1.Invalidate();
        }

        private void PresetField3(int[,] f)
        {
            ClearField(f);

            int x = 40;
            int y = 1;
            long[] data = {
                0B0000000000000000000000000100000000000,
                0B0000000000000000000000010100000000000,
                0B0000000000000110000001100000000000011,
                0B0000000000001000100001100000000000011,
                0B0110000000010000010001100000000000000,
                0B0110000000010001011000010100000000000,
                0B0000000000010000010000000100000000000,
                0B0000000000001000100000000000000000000,
                0B0000000000000110000000000000000000000,
            };
            SetField(f, x, y, data);
        }

実行するとこんな感じ。

 
★ ★ ★

最後に、こんなのはいかがでしょうか!?

        private void btnPreset04_Click(object sender, EventArgs e)
        {
            PresetField4(field);
            pictureBox1.Refresh();
        }

        private void PresetField4(int[,] f)
        {
            ClearField(f);

            int x = 35;
            int y = 20;
            long[] data = {
                0B0011100111101111010001,
                0B0100000100001000001010,
                0B0101110111001110000100,
                0B0100010100001000001010,
                0B0011100111101111010001,
            };
            SetField(f, x, y, data);
        }

…いったいどんな動きになるんでしょうか…

それは是非、コーディングして確かめてみてください!

…なんちゃって…

ちゃんと、動画ご用意しました。

予想もできない動きをしますね!

★ ★ ★

というわけで、2回にわたり「ライフゲームを作ってみる」をお届けしましたが、いかがでしたでしょうか。

これからもまた、何かプログラミングネタを投稿していこうと思います。

ではまた!

追伸

GitHubに前回と今回のライフゲームのソースを載せましたので、良かったらご覧ください!
ここをクリック