生と死の2次元パターンの変化
The Game of Life
The Game of Life というパターンの変化のシミュレーションプログラムをVisual C++ 2013/CLRで作成した。パターンの変化の規則は以下のように設定されている(A. Bellos, 2015, Alex through the looking glass: How life reflects numbers, and numbers reflect life. Bloomsbury Publishing PLC, p. 262)。
格子状に区切られた2次元平面において、各格子点が1つ1つの生命体を表しているものとする。
各格子点が表す生命はliveであるかdeadであるかの2つの状態であり、次の時点における状態は、以下の規則で決まるものとする。
自身がdeadであるとき、
周りの8個の格子点のうち、ちょうど3個がliveであるとき、その格子点はdeadからliveに変わる。
自身がliveであるとき、
周りの8個のうち、liveであるものが、2個であるか3個であれば、次の時点においてもliveであり続ける(survival)。
周りの8個のうち、liveであるものが1個以下であれば、寂しさで次の時点においてdeadになり(loneliness)、4個以上であれば過密で次の時点においてdeadになる(overcrowding)。
この規則をプログラミングしたものをGameOfLife.zipにまとめた。ダウンロード解凍すれば、Visual Studio 2013で開くことができる。実行形式のプログラムGameOfLife.exeも用意した。
実行形式のプログラムは、Visual Studio 2013がインストールされているPCであれば、ダウンロードして直接実行することができる。
Windows 8.1において、プログラムファイル(*.exe)をダウンロード実行したときに表示されるWindowsのメッセージについては、ウェブサイト
http://y-okamoto-psy1949.la.coocan.jp/VCpp/Win8EXEFile/
で説明している。
Visual Studioでプロジェクトを開いたときにエラーメッセージが表示されることがあるが、これらは無視することができる。
フォームデザイナー関連のエラーについては、ウェブサイト
http://y-okamoto-psy1949.la.coocan.jp/VCpp/FormError/
で、下端のバーに表示されるエラーについてはウェブサイト
http://y-okamoto-psy1949.la.coocan.jp/booksetc/introvcpp/vs2012FormApp/
で説明している。
プログラムを実行すると、図1のフォームが表示される。
図1
フォーム中央のpictureBox上を、マウスの右ボタンでクリックする、あるいはクリックしたままなぞると、図2のように黒で小正方形が描かれていく。
図2
各小正方形が、その位置の生命体を表す(100×100の配列)が、黒がlive、白がdeadを表している。右ボタンでクリック、あるいはクリックしたままなぞると、黒(live)が白(dead)に変わる。
パターンの設定後、Goボタンをクリックすると、上の規則に基づいてシミュレーションが始まる(図3)。
図3
Stopボタンのクリックでシミュレーションは一時停止し、Goボタンの表示されている図1、図2のようなフォームになる。このとき、マウスによるクリックでパターンの再設定を行うことができる。設定後、Goボタンのクリックで、シミュレーションが再開する。
Closeボタンのクリックで、プログラムの終了となる。
プログラムは以下のようになっている。
================= MyForm.h ========================
#pragma
once
#include
"common.h";
namespace
GameOfLife {
・
・
・
#pragma
endregion
private:
System::Void buttonClose_Click(System::Object^ sender, System::EventArgs^ e) {
this->Close();
}
int pw, ph;
Graphics ^ g;
Brush ^ bshWt;
Brush ^ bshBk;
int ms_st;
int cell_ck0,
cell_ck1;
int cond_set;
private:
System::Void MyForm_Load(System::Object^ sender, System::EventArgs^ e) {
pw
= 500;
ph = 500;
pictureBox1->Width
= pw;
pictureBox1->Height
= ph;
pictureBox1->Image
= gcnew Bitmap(pw, ph);
g
= Graphics::FromImage(pictureBox1->Image);
bshWt = gcnew SolidBrush(Color::White);
g->FillRectangle(bshWt, 0, 0, pw, ph);
bshBk = gcnew SolidBrush(Color::Black);
ms_st = 0;
for (int st = 0; st
< 2; st++)
for
(int i = 0; i <= 101; i++)
for
(int j = 0; j <= 101; j++)
cell_state[st][i][j] = dead;
cell_ck0
= 0;
cell_ck1
= 1;
cond_set = 0;
buttonStop->Hide();
}
private:
System::Void pictureBox1_MouseDown(System::Object^ sender, System::Windows::Forms::MouseEventArgs^
e) {
int ix, iy;
if (cond_set == 0){
ix
= (e->Location.X / 5);
iy = (e->Location.Y / 5);
if
((ix >= 0) && (ix < 100) && (iy
>= 0) && (iy < 100)){
if
(e->Button == System::Windows::Forms::MouseButtons::Left){
pictureBox1->Image
= pictureBox1->Image;
g->FillRectangle(bshBk, ix * 5, iy * 5, 5, 5);
ms_st = 1;
cell_state[cell_ck0][ix + 1][iy +
1] = live;
}
else
if (e->Button == System::Windows::Forms::MouseButtons::Right){
pictureBox1->Image
= pictureBox1->Image;
g->FillRectangle(bshWt, ix * 5, iy * 5, 5, 5);
ms_st = -1;
cell_state[cell_ck0][ix + 1][iy +
1] = dead;
}
}
}
}
private:
System::Void pictureBox1_MouseMove(System::Object^ sender, System::Windows::Forms::MouseEventArgs^
e) {
if (cond_set == 0){
if
(ms_st == 1){
int ix, iy;
ix
= (e->Location.X / 5);
iy = (e->Location.Y / 5);
if
((ix >= 0) && (ix < 100) && (iy
>= 0) && (iy < 100)){
pictureBox1->Image
= pictureBox1->Image;
g->FillRectangle(bshBk, ix * 5, iy * 5, 5, 5);
cell_state[cell_ck0][ix + 1][iy +
1] = live;
}
}
else
if (ms_st == -1){
int ix, iy;
ix
= (e->Location.X / 5);
iy = (e->Location.Y / 5);
if
((ix >= 0) && (ix < 100) && (iy
>= 0) && (iy < 100)){
pictureBox1->Image
= pictureBox1->Image;
g->FillRectangle(bshWt, ix * 5, iy * 5, 5, 5);
cell_state[cell_ck0][ix + 1][iy +
1] = dead;
}
}
}
}
private:
System::Void pictureBox1_MouseUp(System::Object^ sender, System::Windows::Forms::MouseEventArgs^
e) {
if (cond_set == 0){
ms_st = 0;
}
}
private:
System::Void buttonGo_Click(System::Object^ sender, System::EventArgs^ e) {
buttonStop->Show();
cond_set = 1;
timer1->Enabled
= true;
buttonGo->Hide();
}
private:
System::Void buttonStop_Click(System::Object^ sender, System::EventArgs^ e) {
buttonGo->Show();
cond_set = 0;
timer1->Enabled
= false;
buttonStop->Hide();
}
private:
System::Void timer1_Tick(System::Object^
sender, System::EventArgs^ e) {
timer1->Enabled
= false;
for
(int i = 1; i <= 100; i++)
for
(int j = 1; j <= 100; j++){
int count = 0;
for
(int i1 = -1; i1 <= 1; i1++)
for
(int j1 = -1; j1 <= 1; j1++){
if
((i1 != 0) || (j1 != 0)){
if
(cell_state[cell_ck0][i +
i1][j + j1] == live)
count++;
}
}
switch
(cell_state[cell_ck0][i][j]){
case
dead: if (count == 3){
cell_state[cell_ck1][i][j] =
live;
}
else{
cell_state[cell_ck1][i][j] = dead;
}
break;
case
live: if ((2 <= count) && (count <= 3)){
cell_state[cell_ck1][i][j] =
live;
}
else{
cell_state[cell_ck1][i][j] = dead;
}
break;
default: break;
}
}
pictureBox1->Image
= pictureBox1->Image;
for
(int i = 0; i < 100; i++)
for
(int j = 0; j < 100; j++)
if
(cell_state[cell_ck1][i +
1][j + 1] == live){
g->FillRectangle(bshBk, i * 5, j * 5, 5, 5);
}
else{
g->FillRectangle(bshWt, i * 5, j * 5, 5, 5);
}
if
(cell_ck1 == 0){
cell_ck1
= 1;
cell_ck0
= 0;
}
else{
cell_ck1
= 0;
cell_ck0
= 1;
}
timer1->Enabled
= true;
}
};
}
=============================================
各個体の生死の状態を表す配列cell_state(2次元配列の配列、すなわち3次元配列)は、フォームの宣言されているヘッダーファイルとは別のファイルcommon.cppおよびcommon.hに宣言・定義されている。ref class型のメンバとして、あるいはフォームの宣言されている位置の前に、このような配列を宣言することができないためである。
=========== common.h ============
#ifndef COMMON_CK
#define
COMMON_CK
enum Cell_st {live, dead, other};
extern
Cell_st cell_state[2][102][102];
#endif
============================
========== common.cpp ===========
#include "common.h"
Cell_st cell_state[2][102][102];
==========================
シミュレーションの速さは、timerコンポーネントで制御している。Graphicsハンドルgによる描画は、少し間があくと描画コードの実行がディスプレイに反映されなくなることがある。このようなときは、描画コードの実行の前に、PictureBoxのImageの再設定を行っておくと、描画コードの実行がディスプレイ上に反映される。上のプログラムでは、Imageの再設定を
pictureBox1->Image = pictureBox1->Image;
と行っている。
なお、Visual C++/CLRプログラミングについては、
岡本安晴「大学生のための心理学VC++プログラミング入門」勁草書房
で説明している。