渋滞シミュレーション

先日、友人たちと車で箱根に向かいました。土曜日ということもあり、高速道路は絵に描いたような大渋滞。その時、友人が「少し減速など小さな事の積み重ねで渋滞は発生するんだって」といいました。
ホンマカイナと思いましたが、頭の中でぐちゃぐちゃ考えるとうまくシミュレーションできそうです。
早速ASで組んでみました。


まず、ステージ上に車を円上に並べます。
それぞれの車にルールを与えます。
1.毎フレーム中心から90度の方向に進む。
2.前の車に近づきすぎたら減速する。遠くなれば等速になるまで加速する。

真ん中の「Traffic Jam」ボタンを押すと、任意の一台の車がスピードが半減します。

渋滞シミュレーション – wonderfl build flash online

たしかに、最初はとても小さな影響でしたが、徐々に大きくなり確かに渋滞は発生しました。
大成功。
など、喜んでおりましたが横浜市のH.Yさんからのご意見です。
「たしかに渋滞の発生はしたけど、現存の渋滞はいつかなくなる。しかしこのシミュレーションはいつまでたっても渋滞が減らない。シミュレーションとしては不自然ではないか?」
なるほど、手厳しい。
もう少し考えてみたほうがよさそうです。

そこで等速を目指すのではなく、初期の距離と初速度を保つように加減速するようなルールにしました。
渋滞を起こしやすい等速モード(Uniform Mode)と渋滞を減らす加速モード(Acceleration Mode)を用意しました。
左上のボタンで切り替えてください。

forked from: 渋滞シミュレーション – wonderfl build flash online

[sourcecode language=”as3″]
package {
import flash.display.Sprite;
public class FlashTest extends Sprite {
public function FlashTest() {
// write as3 code here..
var hw:Hightway=new Hightway()
addChild(hw)
hw.init();

var tf:TrafficJam=new TrafficJam(hw);
addChild(tf)
tf.init()
}
}
}
import flash.display.Sprite;
class Hightway extends Sprite {
private var n:Number
private var R:Number
private var scx:Number;
private var scy:Number;
private var c_ar:Array=new Array()
public function Hightway() {
// constructor code
n=100;
R=180;
}
public function init():void{
scx=stage.stageWidth*.5
scy=stage.stageHeight*.5
var i:uint;
var c:Car
for(i=0;i<n;i++){
c=new Car();
addChild(c)
var theta:Number=i*2*Math.PI/n
c.x=scx+R*Math.cos(theta);
c.y=scy+R*Math.sin(theta);
c_ar.push(c);
}
for(i=0;i<n;i++){
c=c_ar[i]
var front:Car;
if(i==n-1){
front=c_ar[0]
}else{
front=c_ar[i+1]
}
c.startMove(front,R)
}
}
public function accident():void{
var c:Car=c_ar[Math.floor((Math.random()*c_ar.length))];
c.accident()
}
public function humanNature(flag:Boolean):void{

var i:uint;
for(i=0;i<c_ar.length;i++){
var c:Car=c_ar[i];
c.Heart=flag;
}
}
}

import flash.display.Sprite;
import flash.display.Graphics;
import flash.events.Event;
import flash.utils.setTimeout;
import flash.display.MovieClip;
class Car extends Sprite{
public var Heart:Boolean=false;
//
public var _frontcar:Car
public var _theta:Number;
public var _R:Number;
private var scx:Number
private var scy:Number;
private var _v:Number;
private var g:Graphics
private var fd:Number;
public function Car() {
// constructor code
g=this.graphics;
g.beginFill(0x0000FF);
g.drawCircle(0,0,1);
_v=2
}
public function init(theta:Number,R:Number,frontcar:Car):void{
_theta=theta;
_R=R;
}
public function startMove(frontcar:Car,R:Number):void{
_R=R;
//
scx=stage.stageWidth*.5;
scy=stage.stageHeight*.5;
//
_frontcar=frontcar;
fd=Math.sqrt(Math.pow(_frontcar.x-this.x,2)+Math.pow(_frontcar.y-this.y,2));
//
addEventListener(Event.ENTER_FRAME,ent)
}
public function accident():void{
_v*=0.5;
setMark()
var id:uint=0
var i:uint;
for(i=0;i<2;i++){
id=setTimeout(setMark,100+50*i);
}
}
//private
private function setMark():void{
var ring:MovieClip=new MovieClip;
addChild(ring);
var rg:Graphics=ring.graphics;
rg.beginFill(0xFF0000,0.5);
rg.drawCircle(0,0,30);
ring.scaleX=0;
ring.scaleY=0;
ring.addEventListener(Event.ENTER_FRAME,ripple);
}
private function ripple(evt:Event):void{
var ring:MovieClip=evt.currentTarget as MovieClip;
var dx:Number=1-ring.scaleX;
ring.scaleX+=dx*0.15;
ring.scaleY=ring.scaleX;
ring.alpha=1-ring.scaleX;
if(dx<0.03){
ring.removeEventListener(Event.ENTER_FRAME,ripple);
removeChild(ring);
}
}
private function ent(evt:Event):void{
var theta:Number=-0.5*Math.PI+Math.atan2(scy-this.y,scx-this.x)
var d:Number=Math.sqrt(Math.pow(_frontcar.x-this.x,2)+Math.pow(_frontcar.y-this.y,2));
if(Heart){
//渋滞から抜けたいいう気持ち
_v=2*(d/fd)
}else{
if(d<fd*.5){
_v*=0.5
}else{
_v+=(2-_v)*0.05;
}
}
//
g=this.graphics;
if(_v<1){
g.clear();
g.beginFill(0xFF0000);
g.drawCircle(0,0,1);
}else{
g.clear();
g.beginFill(0x0000FF);
g.drawCircle(0,0,1);
}
//
var _x:Number=this.x+_v*Math.cos(theta);
var _y:Number=this.y+_v*Math.sin(theta);
theta=Math.atan2(_y-scy,_x-scx);
this.x=scx+_R*Math.cos(theta);
this.y=scy+_R*Math.sin(theta);
}
}

import com.bit101.components.*;
import flash.events.MouseEvent;
import flash.display.Sprite;
class TrafficJam extends Sprite {
private var hw:Hightway;
//
private var lbl:Label;
private var pbtn:PushButton;
private var hn:PushButton;
private var heart:Boolean=false;
public function TrafficJam(heightway:Hightway):void {
// constructor code
hw=heightway;
}
public function init():void{
hn=new PushButton();
hn.label="Acceleration Mode";
hn.x=2;
hn.y=2;
hn.addEventListener(MouseEvent.CLICK,hn_clk)
addChild(hn);
pbtn=new PushButton();
pbtn.label="Traffic Jam";
pbtn.x=stage.stageWidth*.5-pbtn.width*.5
pbtn.y=stage.stageHeight*.5-pbtn.height*.5
addChild(pbtn)
pbtn.addEventListener(MouseEvent.CLICK,clk);
//
lbl=new Label();
lbl.text="Uniform Mode";
lbl.width=pbtn.width;
addChild(lbl)
lbl.x=pbtn.x;
lbl.y=pbtn.y-lbl.height;
}
//
private function hn_clk(evt:MouseEvent):void{
//hn.visible=false;
heart=!heart;
if(heart){
lbl.text="Acceleration Mode";
hn.label="Uniform Mode";
}else{
lbl.text="Uniform Mode";
hn.label="Acceleration Mode";
}
hw.humanNature(heart);
}
private function clk(evt:MouseEvent):void{
hw.accident();
}
}
[/sourcecode]

なるべく減速しないようにすること、なるべく加速できるようにすること、全体に進もうと加速していく意志が渋滞を減らしていくのですね。
ぐちゃぐちゃ書きましたが、当たり前といえば当たり前ですね。

皆様も快適で安全なドライブをお楽しみください。
まぁ、私は免許がないので後ろでガースカ寝てるだけですが。