ライフゲーム

提供:kuhalaboWiki
(版間での差分)
移動: 案内, 検索
(ページの作成:「=== Cellクラスを作る=== ;Cell.h <pre> #pragma once #include "ofMain.h" class Cell { public: int currState; //現在の状態 int nextState; //次回の状態 ...」)
 
 
(1人の利用者による、間の16版が非表示)
1行: 1行:
 +
== openFrameworksのサンプル ==
 +
 
=== Cellクラスを作る===
 
=== Cellクラスを作る===
  
11行: 13行:
 
int nextState; //次回の状態
 
int nextState; //次回の状態
 
int activeNeighbors; //近傍の生命の数
 
int activeNeighbors; //近傍の生命の数
ofColor color; //生きているセルの色
+
ofColor color; //セルの色
 
ofColor gcolor; //グリッドの色
 
ofColor gcolor; //グリッドの色
  
61行: 63行:
 
break;
 
break;
 
default:
 
default:
nextState = false;
+
nextState = 0;
 
break;
 
break;
 
}
 
}
86行: 88行:
 
</pre>
 
</pre>
  
 +
=== 主プログラム ===
  
 +
;ofApp.h
 
<pre>
 
<pre>
 +
#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(); //フルスクリーンモード切り替え
 +
};
 
</pre>
 
</pre>
  
 +
;ofApp.cpp
 +
<pre>
 +
#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;
 +
}
 +
}
 +
</pre>
 +
 +
ここまでで、ライフゲームとして、成立する。
 +
 +
以下は追加の機能で典型的なパターンを定義してクラスを作る。
 +
 +
=== 典型的セルパターンのクラス ===
  
 +
*グライダー砲とパッファートレインのパターン定義を作る。
 +
*ofApp.hの先頭辺りにに以下を追加すること。
 
<pre>
 
<pre>
 +
#include "patterns.h"
 
</pre>
 
</pre>
  
 +
;patterns.h
 +
<pre>
 +
#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);
 +
};
 +
</pre>
 +
 +
patterns.cpp
 
<pre>
 
<pre>
 +
 +
#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;
 +
}
 
</pre>
 
</pre>
  
  
 +
=== クラスの記述を洗練させた ===
 +
 +
;Cell.h
 
<pre>
 
<pre>
 +
#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(); //近傍の生きているセルの数を得る
 +
 +
};
 
</pre>
 
</pre>
 +
 +
;Cell.cpp
 +
<pre>
 +
#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;
 +
}
 +
</pre>
 +
 +
;ofApp.h
 +
<pre>
 +
#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();//フルスクリーンモード切り替え
 +
};
 +
</pre>
 +
 +
;ofApp.cpp
 +
<pre>
 +
#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){
 +
 +
}
 +
</pre>
 +
 +
== 参考 ==
 +
[[ジェネラティブアート論]]
 +
 +
[[Category:授業]]

2020年10月31日 (土) 00:21時点における最新版

目次

[編集] 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){ 

}

[編集] 参考

ジェネラティブアート論

個人用ツール
名前空間

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