ライフゲーム

提供:kuhalaboWiki
移動: 案内, 検索

目次

openFrameworksのサンプル

Cellクラスを作る

Cell.h
#pragma once
#include "ofMain.h"

class Cell {
public:
	int currState; //現在の状態
	int nextState; //次回の状態
	int activeNeighbors; //近傍の生命の数
	ofColor color; //セルの色
	ofColor gcolor; //グリッドの色

	Cell();
	void update();
	void draw(float x, float y, float w, float h);
	void clear();
};
Cell.cpp
#include "Cell.h"

//初期状態の設定
Cell::Cell(){
	currState = 0;
	nextState = 0;	
	color = ofColor::black; // cell color
	gcolor = ofColor(150, 150, 150); // grid color
}
//セルの状態変異測
void Cell::update(){
	if (currState) {
		switch(activeNeighbors){
		case 0:
		case 1:
			nextState = 0;
			break;
		case 2:
		case 3:
			nextState = 1;
			color = ofColor::black; // live cell color
			break;
		case 4:
		case 5:
		case 6:
		case 7:
		case 8:
			nextState = 0;
			break;
		}
	}
	else{
		switch(activeNeighbors){
		case 3:
			nextState = 1;
			color = ofColor::green; // born cell color
			break;
		default:
			nextState = 0;
			break;
		}
	}
}
//セルを描画
void Cell::draw(float x, float y, float w, float h){
	ofSetColor(gcolor);
	ofNoFill();
	ofRect(x, y, w, h);
	if (currState) {
		ofSetColor(color);
		ofFill();				
		ofRect(x, y, w, h);
		ofNoFill();
	}
}

void Cell::clear() {
	currState = 0;
	nextState = 0;	
	color = ofColor::black;
}

主プログラム

ofApp.h
#pragma once

#include "ofMain.h"
#include "Cell.h"

class ofApp : public ofBaseApp {

public:
	void setup();
	void update();
	void draw();

	void keyPressed(int key);
	void mousePressed(int x, int y, int button);

	void keyReleased(int key);
	void mouseMoved(int x, int y );
	void mouseDragged(int x, int y, int button);
	void mouseReleased(int x, int y, int button);
	void windowResized(int w, int h);
	void dragEvent(ofDragInfo dragInfo);
	void gotMessage(ofMessage msg);

//ライフゲームのセルに関する定義
	Cell **grid;
	int rows, cols; //セルの個数。縦と横の2次元配列
	float cellWidth, cellHeight; //グリッドの幅と高さのピクセル数
	bool fullScreen; // フルスクリーンモードの切り替え
	bool active; // オートマトンの進行のオン・オフ

	void init(int width, int height, int cellsize); //グリッドの初期化
	void pause(); //オートマトンの進行を止める
	int getNumActiveNeighbors(int colIndex, int rowIndex); //近傍の生命の数をカウントする
	void RandomCell(); //グリッド上にランダムに生命を置く
	void ClearCell(); //グリッド上の生命をクリアする。
	void goFullScreen(); //フルスクリーンモード切り替え
};
ofApp.cpp
#include "ofApp.h"

const int WIDTH = 800;
const int HEIGHT = 600;
const int CELLSIZE = 6;
const int FULLSCREEN_CELLSIZE = 8;
const int FRAMERATE = 30;

void ofApp::setup() {
	fullScreen = false;
	active = false;

	ofSetFullscreen(false);
	ofSetWindowShape(WIDTH, HEIGHT);
	ofBackground(ofColor::white);
	ofSetBackgroundAuto(true);
	ofSetWindowTitle("Conway's Game of Life");
	ofSetFrameRate(FRAMERATE);

	init(WIDTH, HEIGHT, CELLSIZE);
}

void ofApp::init(int width, int height, int cellSize) {
	cols = width/cellSize;
	rows = height/cellSize;

	grid = new Cell *[cols];
	for (int i=0; i<cols; i++) {
		grid[i] = new Cell[rows];
		for (int j=0; j<rows; j++) {
			Cell *thisCell = &grid[i][j];
		}
	}

	if (width % cellSize != 0 || (height & cellSize) != 0) {
		float ratio = width/height;
		cellWidth = cellSize * ratio;
		cellHeight = cellSize;
	} else {
		cellWidth = cellSize;
		cellHeight = cellSize;
	}
}

void ofApp::update() {
	if(active){
		// get active neighbors for each cell
		for (int i=0; i<cols; i++) {
			for (int j=0; j<rows; j++) {
				grid[i][j].activeNeighbors = getNumActiveNeighbors(i, j);
				grid[i][j].update();
			}
		}
		for (int i=0; i<cols; i++) {
			for (int j=0; j<rows; j++) {
				grid[i][j].currState = grid[i][j].nextState;		
			}
		}
	}
}

void ofApp::draw() {
	for (int i=0; i<cols; i++) {
		for (int j=0; j<rows; j++) {
			grid[i][j].draw(i*cellWidth, j*cellHeight, cellWidth, cellHeight);
		}
	}
}

void ofApp::RandomCell() {
	for (int i=0; i<cols; i++) {
		for (int j=0; j<rows; j++) {
			grid[i][j].clear();
			grid[i][j].currState = ofRandom(2);
		}
	}
}

void ofApp::ClearCell() {
	for (int i=0; i<cols; i++) {
		for (int j=0; j<rows; j++) {
			grid[i][j].clear();
		}
	}
}

int ofApp::getNumActiveNeighbors(int colIndex, int rowIndex) {
	int ret = 0;

	int prevCol = (colIndex - 1 + cols) % cols;
	int nextCol = (colIndex + 1 + cols) % cols;
	int prevRow = (rowIndex - 1 + rows) % rows;
	int nextRow = (rowIndex + 1 + rows) % rows;

	ret += grid[prevCol][prevRow].currState;
	ret += grid[prevCol][rowIndex].currState;
	ret += grid[prevCol][nextRow].currState;

	ret += grid[colIndex][prevRow].currState;
	ret += grid[colIndex][nextRow].currState;

	ret += grid[nextCol][prevRow].currState;
	ret += grid[nextCol][rowIndex].currState;
	ret += grid[nextCol][nextRow].currState;

	return ret;
}

void ofApp::goFullScreen() {
	active = false;
	ofToggleFullscreen();
	fullScreen = !fullScreen;
	if (fullScreen) {
		init(ofGetScreenWidth(), ofGetScreenHeight(), FULLSCREEN_CELLSIZE);
	} else {
		init(WIDTH, HEIGHT, CELLSIZE);
	}
}

void ofApp::pause() {
	active = false;
}

void ofApp::mousePressed(int x, int y, int button) {
	int xcell = x/cellWidth;
	int ycell = y/cellHeight;
	grid[xcell][ycell].currState = !grid[xcell][ycell].currState;
}

void ofApp::keyPressed(int key) {
	switch (key) {
	case ' ':
		active = !active;
		break;
	case 'R':
		RandomCell();
		break;
	case 'c':
		ClearCell();
		break;
	case 'f':
		goFullScreen();
		break;
	case 'g':
//		pause();
		patterns::gliderGun(grid, 2, 2);
		break;
	case 'p':
//		pause();
		patterns::pufferTrain(grid, cols/2 - 10, rows - 30);
		break;
	default:
		break;
	}
}

ここまでで、ライフゲームとして、成立する。

以下は追加の機能で典型的なパターンを定義してクラスを作る。

典型的セルパターンのクラス

  • グライダー砲とパッファートレインのパターン定義を作る。
  • ofApp.hの先頭辺りにに以下を追加すること。
#include "patterns.h"
patterns.h
#pragma once

#include "ofApp.h"

class patterns {
public:
	static void gliderGun(Cell **grid, int startPosX, int startPosY);
	static void pufferTrain(Cell **grid, int startPosX, int startPosY);
};

patterns.cpp


#include "patterns.h"

void patterns::gliderGun(Cell **grid, int startPosX, int startPosY) {
	// left box
	grid[startPosX+1][startPosY+5].currState = 1;
	grid[startPosX+1][startPosY+6].currState = 1;
	grid[startPosX+2][startPosY+5].currState = 1;
	grid[startPosX+2][startPosY+6].currState = 1;

	// left middle of gun
	grid[startPosX+11][startPosY+5].currState = 1;
	grid[startPosX+11][startPosY+6].currState = 1;
	grid[startPosX+11][startPosY+7].currState = 1;
	grid[startPosX+12][startPosY+4].currState = 1;  
	grid[startPosX+12][startPosY+8].currState = 1;
	grid[startPosX+13][startPosY+3].currState = 1;
	grid[startPosX+13][startPosY+9].currState = 1;
	grid[startPosX+14][startPosY+3].currState = 1;
	grid[startPosX+14][startPosY+9].currState = 1;
	grid[startPosX+15][startPosY+6].currState = 1;
	grid[startPosX+16][startPosY+4].currState = 1;
	grid[startPosX+16][startPosY+8].currState = 1;
	grid[startPosX+17][startPosY+5].currState = 1;
	grid[startPosX+17][startPosY+6].currState = 1;
	grid[startPosX+17][startPosY+7].currState = 1;
	grid[startPosX+18][startPosY+6].currState = 1;

	// right middle of gun
	grid[startPosX+21][startPosY+3].currState = 1;
	grid[startPosX+21][startPosY+4].currState = 1;
	grid[startPosX+21][startPosY+5].currState = 1;
	grid[startPosX+22][startPosY+3].currState = 1;
	grid[startPosX+22][startPosY+4].currState = 1;
	grid[startPosX+22][startPosY+5].currState = 1;
	grid[startPosX+23][startPosY+2].currState = 1;
	grid[startPosX+23][startPosY+6].currState = 1; 
	grid[startPosX+25][startPosY+1].currState = 1;
	grid[startPosX+25][startPosY+2].currState = 1;
	grid[startPosX+25][startPosY+6].currState = 1;
	grid[startPosX+25][startPosY+7].currState = 1;    

	// right block
	grid[startPosX+35][startPosY+3].currState = 1;
	grid[startPosX+35][startPosY+4].currState = 1;
	grid[startPosX+36][startPosY+3].currState = 1;
	grid[startPosX+36][startPosY+4].currState = 1;
}

void patterns::pufferTrain(Cell **grid, int startPosX, int startPosY) {
	grid[startPosX+1][startPosY+2].currState = 1;
	grid[startPosX+2][startPosY+1].currState = 1;
	grid[startPosX+3][startPosY+1].currState = 1;
	grid[startPosX+3][startPosY+5].currState = 1;
	grid[startPosX+4][startPosY+1].currState = 1;
	grid[startPosX+4][startPosY+2].currState = 1;
	grid[startPosX+4][startPosY+3].currState = 1;
	grid[startPosX+4][startPosY+4].currState = 1;

	grid[startPosX+8][startPosY+5].currState = 1;
	grid[startPosX+9][startPosY+3].currState = 1;
	grid[startPosX+9][startPosY+4].currState = 1;
	grid[startPosX+10][startPosY+3].currState = 1;
	grid[startPosX+11][startPosY+3].currState = 1;
	grid[startPosX+12][startPosY+4].currState = 1;

	grid[startPosX+15][startPosY+2].currState = 1;
	grid[startPosX+16][startPosY+1].currState = 1;
	grid[startPosX+17][startPosY+1].currState = 1;
	grid[startPosX+17][startPosY+5].currState = 1;
	grid[startPosX+18][startPosY+1].currState = 1;
	grid[startPosX+18][startPosY+2].currState = 1;
	grid[startPosX+18][startPosY+3].currState = 1;
	grid[startPosX+18][startPosY+4].currState = 1;
}


クラスの記述を洗練させた

Cell.h
#pragma once
#include "ofMain.h"

#define NEIGHBORS 8

class Cell {
public:
	int currState; //現在の状態
	int nextState; //次回の状態
	int liveNeighbors; //近傍の生命の数
	ofColor color; //生きているセルの色
	ofColor gcolor; //グリッドの色
	Cell* nCell[NEIGHBORS]; //近傍のセルへのポインタ

	Cell();
	void update();
	void draw(float x, float y, float w, float h);
	void clear();
	int getNumLiveNeighbors(); //近傍の生きているセルの数を得る

};
Cell.cpp
#include "Cell.h"

//初期状態の設定
Cell::Cell(){
	currState = 0;
	nextState = 0;	
	color = ofColor::black; // cell color
	gcolor = ofColor(150, 150, 150); // grid color
}

//セルの状態変異測
void Cell::update(){
	liveNeighbors = getNumLiveNeighbors();  //近傍の生きているセルの数を得る
	if (currState) {
		switch(liveNeighbors){
		case 0:
		case 1:
			nextState = 0;
			break;
		case 2:
		case 3:
			nextState = 1;
			color = ofColor::black; // live cell color
			break;
		case 4:
		case 5:
		case 6:
		case 7:
		case 8:
			nextState = 0;
			break;
		}
	}
	else{
		switch(liveNeighbors){
		case 3:
			nextState = 1;
			color = ofColor::green; // born cell color
			break;
		default:
			nextState = false;
			break;
		}
	}
}

//近傍の生きているセルの数を得る
int Cell::getNumLiveNeighbors() {
	int ret = 0;

	for(int i=0; i<8;i++){
		ret += nCell[i]->currState;
	}
	return ret;
}

//セルを描画
void Cell::draw(float x, float y, float w, float h){
	ofSetColor(gcolor);
	ofNoFill();
	ofRect(x, y, w, h);
	if (currState) {
		ofSetColor(color);
		ofFill();				
		ofRect(x, y, w, h);
		ofNoFill();
	}
}

void Cell::clear() {
	currState = 0;
	nextState = 0;	
	color = ofColor::black;
}
ofApp.h
#pragma once

#include "ofMain.h"
#include "Cell.h"
#include "patterns.h"

class ofApp : public ofBaseApp {

public:
	void setup();
	void update();
	void draw();

	void keyPressed(int key);
	void mousePressed(int x, int y, int button);

	void keyReleased(int key);
	void mouseMoved(int x, int y );
	void mouseDragged(int x, int y, int button);
	void mouseReleased(int x, int y, int button);
	void windowResized(int w, int h);
	void dragEvent(ofDragInfo dragInfo);
	void gotMessage(ofMessage msg);

//ライフゲームのセルに関する宣言
	Cell **grid;
	int cols, rows; //セルの個数。縦と横の2次元配列
	float cellWidth, cellHeight; //グリッドの幅と高さのピクセル数
	bool fullScreen; // フルスクリーンモードにするか否か
	bool highlight; // 新しく誕生したセルをハイライトするか否か
	bool active; // オートマトンの進行のオン・オフ

	void init(int width, int height, int cellsize); //グリッドの初期化
	void pause(); //オートマトンの進行を止める
	void RandomCell(); //グリッド上にランダムに生命を置く
	void ClearCell(); //グリッド上の生命をクリアする。
	void goFullScreen();//フルスクリーンモード切り替え
};
ofApp.cpp
#include "ofApp.h"

const int WIDTH = 800;
const int HEIGHT = 600;
const int CELLSIZE = 6;
const int FULLSCREEN_CELLSIZE = 8;
const int FRAMERATE = 30;

void ofApp::setup() {
	fullScreen = false;
	active = false;

	ofSetFullscreen(false);
	ofSetWindowShape(WIDTH, HEIGHT);
	ofBackground(ofColor::white);
	ofSetBackgroundAuto(true);
	ofSetWindowTitle("Conway's Game of Life");
	ofSetFrameRate(FRAMERATE);

	init(WIDTH, HEIGHT, CELLSIZE); //ライフゲームの初期化
}

void ofApp::init(int width, int height, int cellSize) {
	cols = width/cellSize;
	rows = height/cellSize;

	grid = new Cell *[cols];
	for (int i=0; i<cols; i++) {
		grid[i] = new Cell[rows];
	}
// 自分の8近傍のセルのポインタをつける
// 剰余を使って、上下左右の通過を定義
	for (int i=0; i<cols; i++) {
		for (int j=0; j<rows; j++) {
			grid[i][j].nCell[0] = &(grid[(i-1 + cols)%cols][(j-1 + rows)%rows]);
			grid[i][j].nCell[1] = &(grid[(i   + cols)%cols][(j-1 + rows)%rows]);
			grid[i][j].nCell[2] = &(grid[(i+1 + cols)%cols][(j-1 + rows)%rows]);
			grid[i][j].nCell[3] = &(grid[(i-1 + cols)%cols][(j   + rows)%rows]);
			grid[i][j].nCell[4] = &(grid[(i+1 + cols)%cols][(j   + rows)%rows]);
			grid[i][j].nCell[5] = &(grid[(i-1 + cols)%cols][(j+1 + rows)%rows]);
			grid[i][j].nCell[6] = &(grid[(i   + cols)%cols][(j+1 + rows)%rows]);
			grid[i][j].nCell[7] = &(grid[(i+1 + cols)%cols][(j+1 + rows)%rows]);
		}
	}

	if (width % cellSize != 0 || (height & cellSize) != 0) {
		float ratio = width/height;
		cellWidth = cellSize * ratio;
		cellHeight = cellSize;
	} else {
		cellWidth = cellSize;
		cellHeight = cellSize;
	}
}

void ofApp::update() {
	if(active){
// セルの現在の状態を調べる
		for (int i=0; i<cols; i++) {
			for (int j=0; j<rows; j++) {
				grid[i][j].update();
			}
		}
// セルの状態変数の更新
		for (int i=0; i<cols; i++) {
			for (int j=0; j<rows; j++) {
				grid[i][j].currState = grid[i][j].nextState;		
			}
		}
	}
}
// 
void ofApp::draw() {
	for (int i=0; i<cols; i++) {
		for (int j=0; j<rows; j++) {
			grid[i][j].draw(i*cellWidth, j*cellHeight, cellWidth, cellHeight);
		}
	}
}

void ofApp::RandomCell() {
	for (int i=0; i<cols; i++) {
		for (int j=0; j<rows; j++) {
			grid[i][j].clear();
			grid[i][j].currState = ofRandom(2);
		}
	}
}

void ofApp::ClearCell() {
	for (int i=0; i<cols; i++) {
		for (int j=0; j<rows; j++) {
			grid[i][j].clear();
		}
	}
}

void ofApp::goFullScreen() {
	active = false;
	ofToggleFullscreen();
	fullScreen = !fullScreen;
	delete grid;
	if (fullScreen) {
		init(ofGetScreenWidth(), ofGetScreenHeight(), FULLSCREEN_CELLSIZE);
	} else {
		init(WIDTH, HEIGHT, CELLSIZE);
	}
}

void ofApp::pause() {
	active = false;
}

void ofApp::mousePressed(int x, int y, int button) {
	int xcell = x/cellWidth;
	int ycell = y/cellHeight;
	grid[xcell][ycell].currState = !grid[xcell][ycell].currState;
}

void ofApp::keyPressed(int key) {
	switch (key) {
	case ' ':
		active = !active;
		break;
	case 'R':
		RandomCell();
		break;
	case 'c':
		ClearCell();
		break;
	case 'f':
		goFullScreen();
		break;
	case 'g':
		patterns::gliderGun(grid, 2, 2);
		break;
	case 'p':
		patterns::pufferTrain(grid, cols/2 - 10, rows - 30);
		break;
	default:
		break;
	}
}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){

}
//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){

}
//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){

}
//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){

}
//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){

}
//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){

}
//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){ 

}

参考

ジェネラティブアート論

個人用ツール
名前空間

変種
操作
案内
ツールボックス