ラングトンのアリ

提供:kuhalaboWiki
2020年10月29日 (木) 21:25時点におけるKuha (トーク | 投稿記録)による版

移動: 案内, 検索

ラングトンのアリwikipedia /

目次

Processing

平面が格子状に構成され、各マスが白または黒で塗られる。 アリは各ステップで上下左右のいずれかのマスに移動することができる。アリは以下の規則に従って移動する。

  1. 白いマスにアリがいた場合、90°右に方向転換し、そのマスの色を反転させ、1マス前進する。
  2. 黒いマスにアリがいた場合、90°左に方向転換し、そのマスの色を反転させ、1マス前進する。

Langton ant01 11.png

この単純な規則で驚くほど複雑な動作をする。

ラングトンのループ /

ソース例

参考 ラングトンの蟻 Processing / tado Langton's ant /

  • boolean型の変数の値は、truefalseの2値
    • この場合は、cellsの色が、黒(true)か白(false)かの2値
    •  ! 演算子は、NOT(否定)で、!(true)falseのことで !(false)trueになる。
  • 輪郭の色の指定はstroke()、塗りつぶしの色の指定は fill()
  • アリの座標は、PVector型のantで表す。
  • アリの進行方向は、ant_directionで表す。0(上),1(右),2(下),3(左)の4方向。
    • turn rightは現在の方向に1足した値の4で割った余り。
      • (ant_direction + 1) % 4; 例: 2(下)のとき、turn rightで2+1=3(左)
    • turn leftは現在の方向に3足した値の4で割った余り。
      • (ant_direction + 3) % 4; 例: 2(下)のとき、turn leftで(2+3)%4=1(右)
初期状態 initialize_cells()
0 すべて 白 :しばらく彷徨った後、行進する。
1 すべて 黒:しばらく彷徨った後、行進する。
2 中央に黒い長方形 :長方形の周囲に城壁を築く。その後、行進する。
3 ランダム :
int CELL_NUM = 100;
int CELL_SIZE = 5;

boolean[][] cells = new boolean[CELL_NUM][CELL_NUM];

PVector ant;
int ant_direction; // 0=up, 1=right, 2=down, 3=left
 
void settings() {
 size(CELL_NUM * CELL_SIZE, CELL_NUM * CELL_SIZE);
}
 
void setup(){
  // initialize cells, 0: all white, 1: all black, 2: put black rectangle, 3: random
  initialize_cells(0);
  
  // initialize ant position
  ant = new PVector(int(CELL_NUM / 2),int(CELL_NUM / 2));
  ant_direction = 0;
}

void draw(){
  // clear screen
  fill(255);
  rect(0, 0, width, height);
  
   // draw cells
   noStroke();
   fill(30);
   for(int w = 0; w < CELL_NUM; w++){
     for(int h = 0; h < CELL_NUM; h++){
       if(cells[w][h] == true){
         rect(w * CELL_SIZE, h * CELL_SIZE, CELL_SIZE, CELL_SIZE);
       }
     }
   }
  
  // draw lines
  stroke(128);
  strokeWeight(1);
  for(int i = 0; i < CELL_NUM; i++){
    line(i * CELL_SIZE, 0, i * CELL_SIZE, height);
    line(0, i * CELL_SIZE, width, i * CELL_SIZE);
  }
   
   // draw ant
   fill(255, 0, 0);
   rect(ant.x * CELL_SIZE, ant.y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
   
   //act ant
   act_ant();
}

void act_ant(){
  if(cells[(int)ant.x][(int)ant.y] == true){
    ant_direction = (ant_direction + 3) % 4; // turn left
  }else {
    ant_direction = (ant_direction + 1) % 4; // turn right
  }
  cells[(int)ant.x][(int)ant.y] = !cells[(int)ant.x][(int)ant.y];
  forward_ant();
}

void forward_ant(){
  switch(ant_direction){
    case 0:
      ant.y = (ant.y - 1 + CELL_NUM) % CELL_NUM;
      break;
    case 1:
      ant.x = (ant.x + 1) % CELL_NUM;
      break;
    case 2:
      ant.y = (ant.y + 1) % CELL_NUM;
      break;
    case 3:
      ant.x = (ant.x - 1 + CELL_NUM) % CELL_NUM;
      break;
  }
}

void initialize_cells(int method){
  switch(method){
    case 0: // all white
      for(int w = 0; w < CELL_NUM; w++){
        for(int h = 0; h < CELL_NUM; h++){
          cells[w][h] = false;
        }
      }
      break;
    case 1: // all black
      for(int w = 0; w < CELL_NUM; w++){
        for(int h = 0; h < CELL_NUM; h++){
          cells[w][h] = true;
        }
      }
      break;
    case 2: // put black rectangle 
      for(int w = 0; w < CELL_NUM; w++){
        for(int h = 0; h < CELL_NUM; h++){
          cells[w][h] = false;
        }
      }
      int rect_x = 40;
      int rect_y = 20;
      for(int w = (CELL_NUM / 2) - int(rect_x / 2); w < (CELL_NUM / 2) + int(rect_x / 2); w++){
        for(int h = (CELL_NUM / 2) - int(rect_y / 2); h < (CELL_NUM / 2) + int(rect_y / 2); h++){
          cells[w][h] = true;
        }        
      }
      break;
    case 3: // random
      for(int w = 0; w < CELL_NUM; w++){
        for(int h = 0; h < CELL_NUM; h++){
          if(random(10) > 5){
            cells[w][h] = true;
          }else{
            cells[w][h] = false;
          }
        }
      }
      break;
  }
}

ラングトンのアリの拡張

多数の色を使うように拡張する。色の変化は、循環となり、アリの動きは各色ごとに右か左に向きを変えて1マス進むことになる。これを色の順に L(左)と R(右)を並べて表す。

以下のソース例
  • mod = 4で、4の剰余系(0,1,2,3)で色を表す
  • 色はHSB系で表す。H:色相、S:彩度、B:明度
    • この例では、colorMode(HSB, mod, 100, 100)として、色相(mod=)4段階、彩度100段階、明度100段階で表す。
    • 参考:色彩
  • 各色(0123)による進路「LRLR」の時、彷徨った後、行進する(通常のラングトンのアリ)。
  • 各色(0123)による進路「RLLR」の時、正方形を描きながら彷徨う。
    • 以下のソース例は「RLLR」としている。
int CELL_NUM = 100;
int CELL_SIZE = 5;

int[][] cells = new int[CELL_NUM][CELL_NUM];
int mod = 4;
PVector ant;
int ant_direction; // 0=up, 1=right, 2=down, 3=left
 
void settings() {
 size(CELL_NUM * CELL_SIZE, CELL_NUM * CELL_SIZE);
}
 
void setup(){
  
  // initialize cells, 0: all white, 1: all black, 2: put rectangle, 3:random
   colorMode(HSB, mod, 100, 100);
   initialize_cells(0);
  
  // initialize ant position
  ant = new PVector(int(CELL_NUM / 2),int(CELL_NUM / 2));
  ant_direction = 0;
}

void draw(){
  // clear screen
  fill(0,0,95);
  rect(0, 0, width, height);
  
   // draw cells
   noStroke();
   for(int w = 0; w < CELL_NUM; w++){
     for(int h = 0; h < CELL_NUM; h++){
       if(cells[w][h] > 0){
         fill(cells[w][h], 75, 75);  // セルの色
         rect(w * CELL_SIZE, h * CELL_SIZE, CELL_SIZE, CELL_SIZE);
       }
     }
   }
  
  // draw lines
  stroke(0,0,90);
  strokeWeight(1);
  for(int i = 0; i < CELL_NUM; i++){
    line(i * CELL_SIZE, 0, i * CELL_SIZE, height);
    line(0, i * CELL_SIZE, width, i * CELL_SIZE);
  }
   
   // draw ant
   fill(0, 100, 100);
   rect(ant.x * CELL_SIZE, ant.y * CELL_SIZE, CELL_SIZE, CELL_SIZE);
   
   //act ant
   act_ant();
}

void act_ant(){
  switch(cells[(int)ant.x][(int)ant.y]){
// RLLR
    case 0:
    ant_direction = (ant_direction + 1) % 4; // turn right
      break;
    case 1:
    ant_direction = (ant_direction + 3) % 4; // turn left
      break;
    case 2:
    ant_direction = (ant_direction + 3) % 4; // turn left
      break;
    case 3:
    ant_direction = (ant_direction + 1) % 4; // turn right
      break;
  }
  cells[(int)ant.x][(int)ant.y] = ( cells[(int)ant.x][(int)ant.y] + 1 ) % mod;
  forward_ant();
}

void forward_ant(){
  switch(ant_direction){
    case 0:
      ant.y = (ant.y - 1 + CELL_NUM) % CELL_NUM;
      break;
    case 1:
      ant.x = (ant.x + 1) % CELL_NUM;
      break;
    case 2:
      ant.y = (ant.y + 1) % CELL_NUM;
      break;
    case 3:
      ant.x = (ant.x - 1 + CELL_NUM) % CELL_NUM;
      break;
  }
}

void initialize_cells(int method){
  switch(method){
    case 0: // all white
      for(int w = 0; w < CELL_NUM; w++){
        for(int h = 0; h < CELL_NUM; h++){
          cells[w][h] = 0;
        }
      }
      break;
      
    case 1: // all black
      for(int w = 0; w < CELL_NUM; w++){
        for(int h = 0; h < CELL_NUM; h++){
          cells[w][h] = mod/2;
        }
      }
      break;
    
    case 2: // put black rectangle 
      for(int w = 0; w < CELL_NUM; w++){
        for(int h = 0; h < CELL_NUM; h++){
          cells[w][h] = 0;
        }
      }
      int rect_x = 40;
      int rect_y = 20;
      for(int w = (CELL_NUM / 2) - int(rect_x / 2); w < (CELL_NUM / 2) + int(rect_x / 2); w++){
        for(int h = (CELL_NUM / 2) - int(rect_y / 2); h < (CELL_NUM / 2) + int(rect_y / 2); h++){
          cells[w][h] = mod/2;
        }        
      }
      break;
            
    case 3: // random
      for(int w = 0; w < CELL_NUM; w++){
        for(int h = 0; h < CELL_NUM; h++){
            cells[w][h] = (int)random(mod);
        }
      }
      break;

  }
}


openFrameworks

  • アリは以下の規則に従って移動する。
    • 色ありのマス(1)にアリがいた場合、90°右に方向転換し、そのマスの色を反転させ、1マス前進する。
    • 色なしのマス(0)にアリがいた場合、90°左に方向転換し、そのマスの色を反転させ、1マス前進する。
  • ofImageを使って、アリの動いた軌跡を描画している。
    • ofImageのマニュアル http://www.openframeworks.cc/documentation/graphics/ofImage.html
    • 教科書の3-2-7 「画像ファイルを扱う」(p172)
    • アリの現在位置の色はアリごとに異なる色を設定
    • アリの軌跡の色は1色に設定
    • pixels = myImage.getPixels(); はエラーになるので、
    • pixels = myImage.getPixels().getData(); に修正
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().getData();
	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().getData();
	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()

インスタンスを追加するには、

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();
	}
}

参考

ジェネラティブアート論 / スケーラブルアート論

個人用ツール
名前空間

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