ラングトンのアリ
提供:kuhalaboWiki
- アリは以下の規則に従って移動する。
- 色ありのマス(1)にアリがいた場合、90°右に方向転換し、そのマスの色を反転させ、1マス前進する。
- 色なしのマス(0)にアリがいた場合、90°左に方向転換し、そのマスの色を反転させ、1マス前進する。
- ofImageを使って、アリの動いた軌跡を描画している。
- ofImageのマニュアル http://www.openframeworks.cc/documentation/graphics/ofImage.html
- 教科書の3-2-7 「画像ファイルを扱う」(p172)
- アリの現在位置の色はアリごとに異なる色を設定
- アリの軌跡の色は1色に設定
- ofApp.h
- ofApp.hの中にAntクラスを記述している。
- Antクラスにはプロパティだけで、メソッドがない。
#pragma once
#include "ofMain.h"
class Ant { // Antクラス
public:
int row; // アリの x座標
int col; // アリの y座標
ofColor bcolor; // アリの色
int dir; // アリの進行方向
};
class ofApp : public ofBaseApp{
public:
void setup();
void update();
void draw();
void keyPressed(int key);
void keyReleased(int key);
void mouseMoved(int x, int y );
void mouseDragged(int x, int y, int button);
void mousePressed(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);
static const int FIELD_RATE = 4; // マス目の大きさ
static const int DRAW_RATE = 10; // 描画を間引く間隔
static const int WIDTH = 1024 / FIELD_RATE;
static const int HEIGHT = 768 / FIELD_RATE;
int field[WIDTH][HEIGHT]; // アリが動くマス目 色あり 1 色なし 0
static const int ANTS_NUM = 3; // アリの数
Ant ants[ANTS_NUM];
ofImage myImage; //マス目(アリの歩いた跡)の描画用
unsigned char * pixels;
};
- ofApp.cpp
#include "ofApp.h"
//--------------------------------------------------------------
void ofApp::setup(){
ofBackground(0, 0, 0);
ofSetFrameRate(30);
myImage.allocate(WIDTH, HEIGHT, OF_IMAGE_GRAYSCALE); //
pixels = myImage.getPixels();
for (int i = 0; i < WIDTH * HEIGHT; i++) {
pixels[i] = 0;
}
for (int i = 0; i < WIDTH; i++) {
for (int j = 0; j < HEIGHT; j++) {
field[i][j] = 0;
}
}
myImage.update();
for (int i = 0; i < ANTS_NUM; i++) {
ants[i].row = int(ofRandom(WIDTH));
ants[i].col = int(ofRandom(HEIGHT));
ants[i].bcolor = ofColor(ofRandom(200,255), ofRandom(0,255), ofRandom(0,200), 255);
ants[i].dir = i % 4;
}
}
//--------------------------------------------------------------
void ofApp::update(){
for (int i = 0; i < ANTS_NUM; i++) { //アリの個体
for(int j = 0; j < DRAW_RATE; j++){ // 描画間隔
if(field[ants[i].row][ants[i].col] == 1){ //fieldに色があるとき
ants[i].dir = ( ants[i].dir + 1 ) % 4; // 右に90度方向転換
field[ants[i].row][ants[i].col] = 0;
pixels[ants[i].col * WIDTH + ants[i].row] = 0;
} else { //fieldに色がないとき
ants[i].dir = ( ants[i].dir + 3 ) % 4; // 左に90度方向転換
field[ants[i].row][ants[i].col] = 1;
pixels[ants[i].col * WIDTH + ants[i].row] = 255;
}
switch(ants[i].dir)
{
case 0: // 東(X軸の正)へ進む
ants[i].row += 1;
break;
case 1: // 南(Y軸の正)へ進む
ants[i].col += 1;
break;
case 2: // 西(X軸の負)へ進む
ants[i].row -= 1;
break;
case 3: // 北(Y軸の負)へ進む
ants[i].col -= 1;
break;
}
ants[i].row = (ants[i].row + WIDTH) % WIDTH; // X方向境界処理
ants[i].col = (ants[i].col + HEIGHT) % HEIGHT; // Y方向境界処理
}
}
myImage.update();
}
//--------------------------------------------------------------
void ofApp::draw(){
ofSetColor(0, 0, 255);
myImage.draw(0, 0, WIDTH * FIELD_RATE, HEIGHT * FIELD_RATE); // fieldの描画
for (int i = 0; i < ANTS_NUM; i++) { // antの描画
ofSetColor(ants[i].bcolor);
ofRect(ants[i].row * FIELD_RATE, ants[i].col * FIELD_RATE, FIELD_RATE, FIELD_RATE);
}
}
アリごとに異なる軌跡色を設定
- Antクラスのプロパティに 歩いた軌跡の色である tcolorを設定
- ofImageをOF_IMAGE_COLORに設定する。
- myImageは RGBの3プレーンで構成される。
- Antクラス
class Ant {
public:
int row;
int col;
ofColor bcolor; // antの現在位置の色
ofColor tcolor; // antの軌跡の色
int dir;
};
- ofApp.cpp
- アリは、色のないフィールドにいると、自分の軌跡色をmyImageにセルに設定する。
//--------------------------------------------------------------
void ofApp::setup(){
ofBackground(0, 0, 0);
ofSetFrameRate(30);
myImage.allocate(WIDTH, HEIGHT, OF_IMAGE_COLOR); //ofImageをカラーモードで割当
pixels = myImage.getPixels();
for (int i = 0; i < WIDTH * HEIGHT * 3 ; i++) { //RGB 3 planeのピクセル
pixels[i] = 0;
}
for (int i = 0; i < WIDTH; i++) {
for (int j = 0; j < HEIGHT; j++) {
field[i][j] = 0;
}
}
myImage.update();
for (int i = 0; i < ANTS_NUM; i++) {
ants[i].row = int(ofRandom(WIDTH));
ants[i].col = int(ofRandom(HEIGHT));
ants[i].bcolor = ofColor(ofRandom(0, 255), ofRandom(0, 255), ofRandom(0, 255) );
ants[i].tcolor = ofColor(ofRandom(0, 255) , ofRandom(0, 255) , ofRandom(0, 255) );
ants[i].dir = i % 4;
}
}
//--------------------------------------------------------------
void ofApp::update(){
for (int i = 0; i < ANTS_NUM; i++) { //アリの個体
for(int j = 0; j < DRAW_RATE; j++){ // 描画間隔
if(field[ants[i].row][ants[i].col] == 1){ //fieldに色があるとき
ants[i].dir = ( ants[i].dir + 1 ) % 4;
field[ants[i].row][ants[i].col] = 0;
pixels[ants[i].col * 3 * WIDTH + ants[i].row * 3] = 0; // R値
pixels[ants[i].col * 3 * WIDTH + ants[i].row * 3 + 1] = 0; // G値
pixels[ants[i].col * 3 * WIDTH + ants[i].row * 3 + 2] = 0; // B値
} else { //fieldに色がないとき
ants[i].dir = ( ants[i].dir + 3 ) % 4;
field[ants[i].row][ants[i].col] = 1;
pixels[ants[i].col * 3 * WIDTH + ants[i].row * 3] = ants[i].tcolor.r; // R値
pixels[ants[i].col * 3 * WIDTH + ants[i].row * 3 + 1] = ants[i].tcolor.g; // G値
pixels[ants[i].col * 3 * WIDTH + ants[i].row * 3 + 2] = ants[i].tcolor.b; // B値
}
switch(ants[i].dir)
{
case 0: // 東(X軸の正)へ進む
ants[i].row += 1;
break;
case 1: // 南(Y軸の正)へ進む
ants[i].col += 1;
break;
case 2: // 西(X軸の負)へ進む
ants[i].row -= 1;
break;
case 3: // 北(Y軸の負)へ進む
ants[i].col -= 1;
break;
}
ants[i].row = (ants[i].row + WIDTH) % WIDTH; // X方向境界処理
ants[i].col = (ants[i].col + HEIGHT) % HEIGHT; // Y方向境界処理
}
}
myImage.update();
}
//--------------------------------------------------------------
void ofApp::draw(){
ofSetColor(255, 255, 255); // 描画色をリセット
ofSetRectMode(OF_RECTMODE_CORNER);
myImage.draw(0, 0, WIDTH * FIELD_RATE, HEIGHT * FIELD_RATE); // fieldの描画
ofSetRectMode(OF_RECTMODE_CENTER);
for (int i = 0; i < ANTS_NUM; i++) {// antの描画
ofSetColor(ants[i].bcolor);
ofRect(ants[i].row * FIELD_RATE, ants[i].col * FIELD_RATE, FIELD_RATE, FIELD_RATE);
}
}
動的配列vectorの利用
アリは、初めはいないが、マウスクリックした場所にAntインスタンスが動的に現れる。
- 動的配列(vector)は、定義時に、配列の要素数を決めなくてもよい。プログラム実行中に動的に追加や削除ができる。
従来は、予め配列数を決めて、例えば
クラス名 インスタンス名[100]
と宣言していたが、vectorでは。
vector <クラス名> インスタンス名
と宣言し、配列の個数は、動的に追加、削除できる。
今回の例では、
Ant ants[100];
とするところを
vector <Ant> ants;
とすると、アリを後から追加できる。 配列の個数(大きさは)
ants.size()
で参照できる。
- define文を使って、文字列を一気に置き換えることができる。
#define ANTS_NUM ants.size() /pre> インスタンスを追加するには、 <pre> ants.push_back()
を使う。
マウスクリックした位置に新しいアリを生成する部分は以下のとおり。
oid ofApp::mouseReleased(int x, int y, int button){
Ant a; //新しくアリのインスタンスを作成
a.row = mouseX * WIDTH / ofGetWidth(); //マウスの位置に配置
a.col = mouseY * HEIGHT / ofGetHeight();
a.bcolor = ofColor(ofRandom(0, 255), ofRandom(0, 255), ofRandom(0, 255) ); //色をランダムに選ぶ
a.tcolor = ofColor(ofRandom(0, 255) , ofRandom(0, 255) , ofRandom(0, 255) );
a.dir = mouseX % 4; //方向を決める
ants.push_back(a); // 生成したaをantsに追加
}
すべての要素を削除するには、
ants.clear()
とする。
Cを押すと、アリと画面をクリアする場合
void ofApp::keyReleased(int key){
if( key == 'C'){
ants.clear();
for (int i = 0; i < WIDTH * HEIGHT * 3 ; i++) { //RGB 3 planeのピクセル
pixels[i] = 0;
}
for (int i = 0; i < WIDTH; i++) {
for (int j = 0; j < HEIGHT; j++) {
field[i][j] = 0;
}
}
myImage.update();
}
}