昨晩、なんと7時に寝てしまい、今朝は1時に起きる始末。寝床の中でつらつら考えていると、古くから欧州、中東、アジア、中南米等場所を選ばず発生した侵略、覇権、示威、暴力等の人間の愚かさが思い起こされるも、宇宙の広大さを鑑みればそれは卑小さにしか過ぎず、まるでその様は「昔作ったBCCSkeltonのLifeGameのようだ」と感慨を抱きました。
ん?
それはネタとして使えるかも?と考え始めました。
末尾にあるのは、定年退職した直後にC++とBCCSkeltonの復習で書いた「ライフゲーム」プログラムで、
昔からあるライフゲームのアルゴリズムをクラス(注)でまとめようとしたものです。現在見直すと羞恥に堪えませんがお許しを。
注:このクラスは「世界」を表現していて、「生命体」を表現していないことに注意。
このライフゲームは、「世界(縦、横二次元の領域)」にランダムで1/3程度の「生命体(Cells)」を発生させ、集落(による共生)ができないと次世代生命が生まれなくなったり、死滅してしまいますが、一定数いれば繁殖し、過密状態になるとまた死滅するというものです。
このライフゲームが単純である理由は、
(1)種族が無い単一生命体(性別のない単一生殖)
(2)複数いると共助、共生する(というお気楽さ)
なのでしょうか?最初に作った時は世代交代で集落が変化してゆく様(それなりに)面白く見えますが、何度か見ると飽きてきますね。
では、どうすればより「人間に近いライフゲーム」が作れるのか?これは上記単純である理由を逆に降ればよいでしょう。
(1)クラスを「生命体ベースで作る。」
(2)生命体は「寿命」、「性別」、「種族」の属性を持ち、メソッドに「有効 vs. 敵対」判断と「(有効なら)共生」「(敵対なら)攻撃」を加えてやればよいのではないか?
(3)世界(閉鎖領域)をクラスで表現し、複数の種族(同一IDの生命体)を発生させ、単独別離の状態では従来のライフゲーム類似のルールで発展、衰退するが、発展の過程で多種族に遭遇すると攻撃(より複雑化するなら「避難」も)して自種族を存続させるようにすればよいのではないか?
と、ここまで考えて
「それって、うちらの世界、まんまじゃん!」
ということに気が付きました。
まぁ、気長にアイデアを熟成させましょうか?_
【LifeClass.h】
/*
LifeClass.h
*/
// 定数定義
#define MAX_WIDTH 50 // 世界の最大幅
#define MAX_HEIGHT 50 // 世界の最大高さ
#define INTERVAL 100 // Generation(世代)表示の長さ
#define ALIVE 1
#define DEAD 0
#define TRUE 1
#define FALSE 0
// Lifeクラス定義
class Life {
private:
int Width;
int Height;
int* Cells;
int* NewGen;
public:
// サイズ無指定コンストラクター
Life() {
// 乱数の初期化
srand((unsigned int)time(NULL));
// 世界と次世代
Cells = 0;
NewGen = 0;
// 世界の規模
Width = rand() % (MAX_WIDTH - 2) + 3; // Min 3が必要
Height = rand() % (MAX_HEIGHT - 2) + 3; // Min 3が必要
// 生命体(Cells)の初期化
if(!Cells) Cells = new int[Width * Height];
if(!NewGen) NewGen = new int[Width * Height];
// 世界の初期化(誕生する生命体数は1/3)
for(int y = 0; y < Height; y++) {
for(int x = 0; x < Width; x++) {
if(rand() % 3 == 0) // Cellを増やす場合、2(50%) → 3(66%)にする
Cells[x + Width * y] = ALIVE;
else
Cells[x + Width * y] = DEAD;
}
}
}
// サイズ指定コンストラクター
Life(int w, int h) {
// 乱数の初期化
srand((unsigned int)time(NULL));
// 世界と次世代
Cells = 0;
NewGen = 0;
// 世界の規模
w = (w < 3 ? 3 : w); // 幅はMin 3
h = (h < 3 ? 3 : h); // 高さもMin 3
Width = (w > MAX_WIDTH ? MAX_WIDTH : w); // 幅はMAX_WIDTH限度
Height = (h > MAX_HEIGHT ? MAX_HEIGHT : h); // 高さはMAX_HEIGHT限度
// 生命体(Cells)の初期化
if(!Cells) Cells = new int[Width * Height];
if(!NewGen) NewGen = new int[Width * Height];
// 世界の初期化(誕生する生命体数は1/3)
for(int y = 0; y < Height; y++) {
for(int x = 0; x < Width; x++) {
if(rand() % 3 == 0) // Cellを増やす場合、2(50%) → 3(66%)にする
Cells[x + Width * y] = ALIVE;
else
Cells[x + Width * y] = DEAD;
}
}
}
// デストラクター
~Life() {
delete [] Cells;
delete [] NewGen;
}
// 次世代(NewGen)の世界の評価
void Eval() {
// 上下左右の状況確認
for(int y = 0; y < Height; y++) { // 高さ
for(int x = 0; x < Width; x++) { // 幅
int count = 0; // 周囲の生存(ALIVE)生命体(Cell)数
if(x == 0) { // 左は無い
if(y == 0) { // 上は無い
if(Cells[(x + 1) + Width * y] == ALIVE) // 右
count++;
if(Cells[(x + 1) + Width * (y + 1)] == ALIVE) // 右下
count++;
if(Cells[x + Width * (y + 1)] == ALIVE) // 下
count++;
}
else {
if(y == Height -1) { // 下は無い
if(Cells[x + Width * (y - 1)] == ALIVE) // 上
count++;
if(Cells[(x + 1) + Width * (y - 1)] == ALIVE) // 右上
count++;
if(Cells[(x + 1) + Width * y] == ALIVE) // 右
count++;
}
else { // 上下あり
if(Cells[x + Width * (y - 1)] == ALIVE) // 上
count++;
if(Cells[(x + 1) + Width * (y - 1)] == ALIVE) // 右上
count++;
if(Cells[(x + 1) + Width * y] == ALIVE) // 右
count++;
if(Cells[(x + 1) + Width * (y + 1)] == ALIVE) // 右下
count++;
if(Cells[x + Width * (y + 1)] == ALIVE) // 下
count++;
}
}
}
else {
if(x == Width - 1) { // 右は無い
if(y == 0) { // 上は無い
if(Cells[x + Width * (y + 1)] == ALIVE) // 下
count++;
if(Cells[(x - 1) + Width * (y + 1)] == ALIVE) // 左下
count++;
if(Cells[(x - 1) + Width * y] == ALIVE) // 左
count++;
}
else {
if(y == Height -1) { // 下は無い
if(Cells[(x - 1) + Width * y] == ALIVE) // 左
count++;
if(Cells[(x - 1) + Width * (y - 1)] == ALIVE) // 左上
count++;
if(Cells[x + Width * (y - 1)] == ALIVE) // 上
count++;
}
else { // 上下あり
if(Cells[x + Width * (y + 1)] == ALIVE) // 下
count++;
if(Cells[(x - 1) + Width * (y + 1)] == ALIVE) // 左下
count++;
if(Cells[(x - 1) + Width * y] == ALIVE) // 左
count++;
if(Cells[(x - 1) + Width * (y - 1)] == ALIVE) // 左上
count++;
if(Cells[x + Width * (y - 1)] == ALIVE) // 上
count++;
}
}
}
else { // 左右あり
if(y == 0) { // 上は無い
if(Cells[(x + 1) + Width * y] == ALIVE) // 右
count++;
if(Cells[(x + 1) + Width * (y + 1)] == ALIVE) // 右下
count++;
if(Cells[x + Width * (y + 1)] == ALIVE) // 下
count++;
if(Cells[(x - 1) + Width * (y + 1)] == ALIVE) // 左下
count++;
if(Cells[(x - 1) + Width * y] == ALIVE) // 左
count++;
}
else {
if(y == Height - 1) { // 下は無い
if(Cells[(x - 1) + Width * y] == ALIVE) // 左
count++;
if(Cells[(x - 1) + Width * (y - 1)] == ALIVE) // 左上
count++;
if(Cells[x + Width * (y - 1)] == ALIVE) // 上
count++;
if(Cells[(x + 1) + Width * (y - 1)] == ALIVE) // 右上
count++;
if(Cells[(x + 1) + Width * y] == ALIVE) // 右
count++;
}
else { // 上下あり
if(Cells[(x - 1) + Width * y] == ALIVE) // 左
count++;
if(Cells[(x - 1) + Width * (y - 1)] == ALIVE) // 左上
count++;
if(Cells[x + Width * (y - 1)] == ALIVE) // 上
count++;
if(Cells[(x + 1) + Width * (y - 1)] == ALIVE) // 右上
count++;
if(Cells[(x + 1) + Width * y] == ALIVE) // 右
count++;
if(Cells[(x + 1) + Width * (y + 1)] == ALIVE) // 右下
count++;
if(Cells[x + Width * (y + 1)] == ALIVE) // 下
count++;
if(Cells[(x - 1) + Width * (y + 1)] == ALIVE) // 左下
count++;
}
}
}
}
switch(count) {
case 0: // 周囲の生命体数が1以下だと死滅
case 1: NewGen[x + Width * y] = DEAD;
break;
case 2: // 周囲の生命体数が2 - 3の場合は現状維持
NewGen[x + Width * y] = Cells[x + Width * y];
break;
case 3: // 周囲の生命体数が3の場合は、生命体が誕生する
NewGen[x + Width * y] = ALIVE;
break;
case 4: // 周囲の生命体数が4以上だと過密で死滅
case 5:
case 6:
case 7:
case 8: NewGen[x + Width * y] = DEAD;
break;
}
}
}
}
void Transition() {
for(int y = 0; y < Height; y++) { // 高さ
for(int x = 0; x < Width; x++) { // 幅
Cells[x + Width * y] = NewGen[x + Width * y];
}
}
}
int GetW() {return Width;}
int GetH() {return Height;}
int* GetCells() {return Cells;}
};
