ビハム・ミドルトン・レヴィン交通モデルを参考にしてProcessingでオリジナルの対面歩行者モデルを作成しました。
コード
コードは以下の通りです。
int len; int col, row; int[][] c; boolean pause = false; int n = 0; int a = 0; void setup() { size(600, 600); frameRate(100); len = 3; col = width/len; row = height/len; c = new int[col][row]; //最初の配置 for (int i = 0; i < col; i ++) { for (int j = 0; j < row; j ++) { float p, q; p = random(0,1); q = 0.5 /2; if(p < q){ c[i][j] = 1; //緑 } else if(p > 1 - q){ c[i][j] = 2; //赤 } else{ c[i][j] = 0; } } } } void draw() { background(255); //セルの描画 for (int i = 0; i < col; i ++) { for (int j = 0; j < row; j ++) { noStroke(); if(c[i][j] == 0){ fill(0); } else if(c[i][j] == 1){ fill(0, 255, 20); } else if(c[i][j] == 10){ fill(0, 255, 255); } else if(c[i][j] == 2){ fill(255, 0, 0); } else if(c[i][j] == 20){ fill(255, 255, 0); } rect(i*len, j*len, len, len); } } //次のステップでの生命の配置を計算 if (pause == false) { //描画が動いているとき a += 1; int[][] next = new int[col][row]; int b = 0; for (int i = 2; i < col+2; i ++) { //i=1だと端1行だけ空白になる for (int j = 2; j < row+2; j ++) { //i = i % 250; // j = j % 250; if(a % 2 == 1) { if(c[i % col][j % col] == 10 || c[i % col][j % col] == 20){ if(a % 5000 == 0){ b += 1; } } if(c[(i) % col][(j) % col] == 1 || c[(i) % col][(j) % col] == 10) { //緑のセル if(c[(i) % col][(j-1) % col] == 0) { //前方が空白の時 next[(i) % col][(j-1) % col] = 1; //前進 next[(i) % col][(j) % col] = 0; } else{ //前方が埋まっているとき //(c[(i) % col][(j-1) % col] >= 1 || c[(i+1) % col][(j-1) % col] == 1 || c[(i-1) % col][(j-1) % col] == 1) if(c[(i+1) % col][(j) % col] == 0 && c[(i+1) % col][(j+1) % col] != 1 && c[(i+1) % col][(j+1) % col] != 10) { //右が空いていたら next[(i+1) % col][(j) % col] = 1; //右に移動 next[(i) % col][(j) % col] = 0; } else{//(c[(i+1) % col][(j) % col] >= 1 || c[(i+1) % col][(j+1) % col] == 1 || c[(i+2) % col][(j) % col] == 1) 右が埋まっていたら if(c[(i-1) % col][(j) % col] == 0 && c[(i-2) % col][(j) % col] != 1 && c[(i-2) % col][(j) % col] != 10 && c[(i-1) % col][(j+1) % col] != 1 && c[(i-1) % col][(j+1) % col] != 10) { //左が空いているとき next[(i-1) % col][(j) % col] = 1; //左斜め前に前進 next[(i) % col][(j) % col] = 0; } else{ next[(i) % col][(j) % col] = 10; } } } } else if(c[i % col][j % col] == 2){ //赤のセルは next[i % col][j % col] = 2; //その場にとどまる } else if(c[i % col][j % col] == 20){ //赤のセルは next[i % col][j % col] = 20; //その場にとどまる } } else if(a % 2 == 0){ if(c[i % col][j % col] == 10 || c[i % col][j % col] == 20){ if(a % 500 == 0){ b += 1; } } if (c[(i) % col][(j) % col] == 2 || c[(i) % col][(j) % col] == 20) { //赤のセル if (c[(i) % col][(j+1) % col] == 0) { //前方が空白の時 next[(i) % col][(j+1) % col] = 2; //前進 next[(i) % col][(j) % col] = 0; } else{ //前方が埋まっているときc[(i) % col][(j+1) % col] >= 1 || (c[(i+1) % col][(j+1) % col] == 2 || c[(i-1) % col][(j+1) % col] == 2) if (c[(i-1) % col][(j) % col] == 0 && c[(i-1) % col][(j-1) % col] != 2 && c[(i-1) % col][(j-1) % col] != 20) { //右が空いていたら next[(i-1) % col][(j) % col] = 2; //右に移動 next[(i) % col][(j) % col] = 0; } else{ //右が埋まっていたら(c[(i-1) % col][(j) % col] >= 1 || c[(i-1) % col][(j+1) % col] == 2 || c[(i-2) % col][(j) % col] == 2) if(c[(i+1) % col][(j) % col] == 0 && c[(i+2) % col][(j) % col] != 2 && c[(i+2) % col][(j) % col] != 20 && c[(i+1) % col][(j-1) % col] != 2 && c[(i+1) % col][(j-1) % col] != 20) { //左が空いているとき next[(i+1) % col][(j) % col] = 2; //左斜め前に移動 next[(i) % col][(j) % col] = 0; } else{ next[(i) % col][(j) % col] = 20; //その場にとどまる } } } } else if(c[i % col][j % col] == 1){ //緑のセルは next[i % col][j % col] = 1; //その場にとどまる } else if(c[i % col][j % col] == 10){ //赤のセルは next[i % col][j % col] = 10; //その場にとどまる } } } } c = next; //次のステップの配置を適用 } }
歩行者は上に進行するものと下に進行するものの二種類います。
上に移動する歩行者たちが移動したら次の時刻では下に移動する歩行者たちが移動し、その次の時刻ではまた上に移動する歩行者たちが移動し…というように交互に移動するようになっています。
これは、両種類の歩行者を同時に動かすと重なって数が減少してしまうことがあるからです。
2 種類の歩行者はそれぞれ基本的に直進し、直進できない場合はある条件を満たすとき進行方向に対して右に移動します。右に移動できない場合は左に移動します。
ある条件についての説明です。単に右によけるプログラムだと、同じセルに二人の歩行者が同時に移動して片方が消滅してしまう場合がありました。そのため一斉に移動しても重ならないように条件を作りました。
あるセルが右に進むとき、そのセルの右斜め下のセルが直進してくるとお互いに重なり片方が消えてしまいます。そこで、右斜め下に同じ種類のセルが存在しないことを右に進める条件に組み込むことでこれを回避しました。
ただし別種のセルが右斜め下に存在しても右に移動することはできます。なぜなら同種のセルだけが同時に動くので重なることがないためです。左に移動するときも同様です。
したがってこの条件下では直進>右>左の順に移動が優先されることになります。
2種類の歩行者の初期割合が両方ともq/2(0≤q≤1)になるようにしています。
シミュレーション結果
以下ではこのモデルのパラメータqを変化させたときに見られた興味深い現象を紹介します。
q=0.2付近
q=0.2あたりを境に渋滞が発生します。q<0.2では渋滞は発生せず、歩行者はいくつかの列をなしてきれいに移動します。時折小規模な渋滞が発生することもありますがすぐに消滅してしまいます。
一方でq>0.2では渋滞が発生します。
0.2 < q < 0.7のとき
小規模の渋滞が多数発生します。これらの渋滞は時間が経過するにつれて合体していき、最終的に横長の大規模な渋滞となります。
q>0.7のとき
シミュレーション開始後速やかに多重渋滞構造を形成します。小規模な渋滞が形成される余地がない程に高密度であることが原因だと考えられます。
渋滞での現象
次に渋滞そのものや渋滞内で発生する現象について記述します。
渋滞の移動
0.2 < q < 0.7で最終的に形成される大きな横長の渋滞は、切れ目が存在する場合、切れ目が閉じることはほぼ確実にありませんでした。
代わりに切れ目自身はゆっくりと移動しており、そのことが原因で渋滞の形状自体も変化していきます。まるで渋滞が移動しているかのようにも見えます。
切れ目が閉じることはないと思っていたのですが、この記事を書いている最中に初めて切れ目が閉じた例が現れました。
渋滞内を横断するセル
渋滞の移動も予想していなかった現象ですが、この現象はもっと思いもよらない現象でした。
渋滞の切れ目からセル(歩行者)が渋滞内部に侵入し渋滞を横断していくように見える現象がみられました。
この横断現象は空白セルに渋滞セルが前進することで色が変わり、あたかも歩行者セルが移動しているかのように振る舞うことで発生します。
そのため横断する向きは本来移動するべき方向の逆になっています。また左右の移動も反転しています。
歩行者セルが移動しているように見えるだけで、実際に移動しているのは空白セルということになります。