// // 伝染病のシミュレーション // // 緑:健康 // 黄:潜伏期間 // 赤:発症 // 青:免疫獲得 // 灰:死亡 // // // 感染率:接触時の感染率(0~100%) // 毒性の強さ:発症時のHP減少の大きさ(0-999) // 潜伏期間:感染から発症までの期間 // 発症時間:周りに感染を広めながらHPが減少していく期間 // 免疫期間:発症終了後に抗体ができて再感染しない期間 // // 移動:人の移動量の設定(4段階) // 国境:国境移動の制限(検疫:発症している人のみ通行不可 封鎖:完全封鎖) // 隔離:「ON」にすると発症している人を画面右下方向に隔離 // // 左クリックでワクチンを適用(30%の確率で免疫を獲得) // // // 解説など:http://game.g.hatena.ne.jp/Nao_u/20090430#p1 // package { import flash.display.Sprite; import flash.events.*; [SWF(width="465", height="465", backgroundColor="0xFFFFFF", frameRate="20")] public class FlashTest extends Sprite { public function FlashTest() { Main = this; initialize(); stage.addEventListener(Event.ENTER_FRAME,update); stage.addEventListener(MouseEvent.MOUSE_DOWN, viewClickEvent); } } } var kansen:int = 80; // 感染率(%) var dokusei:int = 5; // 毒性の強さ var senpuku:int = 10; // 潜伏期間 var hassyou:int = 7; // 発症時間 var meneki:int = 50; // 免疫期間 var moveRate:Number = 0.55; // 移動の自由度 var kakuri:Number = 0; var MoveRateAry:Array = new Array(); import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.* import flash.text.TextField; import flash.geom.*; import flash.utils.getTimer; var Main:Sprite; var SCREEN_W:Number = 465; var SCREEN_H:Number = 465; var View: Bitmap; var BmpData: BitmapData; var BgColor:int = 0x101010; var WallColor:int = 0xf08080; var WallColor2:int = 0x807070; var ColGreen:int = 0x408040; var ColYellow:int = 0xffff40; var ColRed0:int = 0xff6060; var ColRed1:int = 0xff4040; var ColBlue:int = 0x2070a0; var ColGray:int = 0x404040; var BITMAP_W:int = 384; var BITMAP_H:int = 384; var AgentAry:Vector.<Agent> = new Vector.<Agent>; var Text0:TextField = new TextField(); var Text1:TextField = new TextField(); var Time:int = 0; var StateAry:Array = new Array(); var ResetButton:Button; var KakuriButton:Button; var Idou:TextField = new TextField(); var Kokkyou:TextField = new TextField(); var IdouType:int = 2; var KokkyouType:int = 2; function initialize():void{ MoveRateAry.push( 0.05 ); MoveRateAry.push( 0.25 ); MoveRateAry.push( 0.55 ); MoveRateAry.push( 0.85 ); BmpData = new BitmapData(BITMAP_W, BITMAP_H, false, 0xffffff); View = new Bitmap(BmpData); View.scaleX = 1.0; View.scaleY = 1.0; Main.addChild(View); clearBmp(); createAgent(); Text0.x = 10; Text0.y = 386; Text0.defaultTextFormat = new TextFormat( "AXIS Std R", 13, 0, false ); Main.addChild(Text0); Text1.x = 59; Text1.y = 388; Text1.width = 384; Text1.defaultTextFormat = new TextFormat( "AXIS Std R", 10, 0, false ); Main.addChild(Text1); createResetButton(); createKakuriButton(); createIdouButton(); createKokkyouButton(); new InputArea(10,410, "感染率 ", kansen.toString(), function(str:String):void{kansen=parseInt(str)} ); new InputArea(10,426, "毒性の強さ ", dokusei.toString(), function(str:String):void{dokusei=parseInt(str)} ); new InputArea(118,410, "潜伏期間 ", senpuku.toString(), function(str:String):void{senpuku=parseInt(str)} ); new InputArea(118,426, "発症時間 ", hassyou.toString(), function(str:String):void{hassyou=parseInt(str)} ); new InputArea(118,442, "免疫期間 ", meneki.toString(), function(str:String):void{meneki=parseInt(str)} ); var t:TextField; t = new TextField(); t.text = "%"; t.x = 95; t.y = 410; t.width = 16; t.height = 16; Main.addChild(t); t = new TextField(); t.text = "日"; t.x = 193; t.y = 410; t.width = 16; t.height = 19; Main.addChild(t); t = new TextField(); t.text = "日"; t.x = 193; t.y = 426; t.width = 16; t.height = 19; Main.addChild(t); t = new TextField(); t.text = "日"; t.x = 193; t.y = 442; t.width = 16; t.height = 19; Main.addChild(t); } function viewClickEvent(event:MouseEvent):void{ var mx:Number = Main.stage.mouseX; var my:Number = Main.stage.mouseY; for each( var agent:Agent in AgentAry ){ if( agent.Status == 0 && Math.random() < 0.3 ){ var dx:Number = mx-agent.X; var dy:Number = my-agent.Y; var dist:Number = Math.sqrt( dx*dx + dy*dy ); if( dist < 35 ){ agent.Status = 3; agent.CountDown = meneki * 10; } } } } function createAgent():void{ AgentAry = new Vector.<Agent>; var i:int; var agent:Agent; for( i=0; i<4096; i++ ){ agent = new Agent( 0,0,BITMAP_W,BITMAP_H ); } for( i=0; i<1500; i++ ){ agent = new Agent( 0,60,128, BITMAP_H-128); } for( i=0; i<1024; i++ ){ agent = new Agent( 128,0,BITMAP_W,80 ); } for( i=0; i<2048; i++ ){ agent = new Agent( 128,0,BITMAP_W,80 ); } for( i=0; i<4096; i++ ){ agent = new Agent( 0,200,BITMAP_W,180 ); } } function createResetButton():void{ ResetButton = new Button(64, 18, 8, "Reset", 16); ResetButton.x = 468-120 ResetButton.y = 468-22 ResetButton.addEventListener(MouseEvent.CLICK, function(event:MouseEvent):void{createAgent();Time=0;} ); Main.addChild(ResetButton); } function createKakuriButton():void{ KakuriButton = new Button(64, 18, 8, "隔離 OFF", 10); KakuriButton.x = 468-120 KakuriButton.y = 468-52 KakuriButton.addEventListener(MouseEvent.CLICK, function(event:MouseEvent):void{ if( kakuri == 0.0 ) { KakuriButton.setLabel("隔離 ON"); kakuri = 0.45; }else{ KakuriButton.setLabel("隔離 OFF"); kakuri = 0.0; } } ); Main.addChild(KakuriButton); } function createIdouButton():void{ Idou.border = true; Idou.borderColor = 0xCCCCCC; Idou.x = 234; Idou.y = 415; Idou.width = 79; Idou.height = 17; Idou.defaultTextFormat = new TextFormat( "AXIS Std R", 10, 0, false ); Idou.text = "移動:通常" Main.addChild(Idou); var up:Button = new Button(14,9, 1, "▲", 9); var down:Button = new Button(14,9, 1, "▼", 9); up.x = 317; up.y = 409; down.x = 317; down.y = 424; Main.addChild(up); Main.addChild(down); up.addEventListener(MouseEvent.CLICK, function(event:MouseEvent):void{ IdouType--; updateIdouType(); } ); down.addEventListener(MouseEvent.CLICK, function(event:MouseEvent):void{ IdouType++; updateIdouType(); } ); } function updateIdouType():void{ if( IdouType < 0 ) IdouType = 3; if( IdouType > 3 ) IdouType = 0; switch( IdouType ){ case 0: Idou.text = "移動:厳戒態勢 "; moveRate = MoveRateAry[IdouType]; break; case 1: Idou.text = "移動:外出制限 "; moveRate = MoveRateAry[IdouType]; break; case 2: Idou.text = "移動:通常 "; moveRate = MoveRateAry[IdouType]; break; case 3: Idou.text = "移動:活発 "; moveRate = MoveRateAry[IdouType]; break; } } function createKokkyouButton():void{ Kokkyou.border = true; Kokkyou.borderColor = 0xCCCCCC; Kokkyou.x = 234; Kokkyou.y = 443; Kokkyou.width = 79; Kokkyou.height = 17; Kokkyou.defaultTextFormat = new TextFormat( "AXIS Std R", 10, 0, false ); Kokkyou.text = "国境:自由" Main.addChild(Kokkyou); var up:Button = new Button(14,9, 2, "▲", 9); var down:Button = new Button(14,9, 2, "▼", 9); up.x = 317; up.y = 440; down.x = 317; down.y = 454; Main.addChild(up); Main.addChild(down); up.addEventListener(MouseEvent.CLICK, function(event:MouseEvent):void{ KokkyouType--; updateKokkyouType(); } ); down.addEventListener(MouseEvent.CLICK, function(event:MouseEvent):void{ KokkyouType++; updateKokkyouType(); } ); } function updateKokkyouType():void{ if( KokkyouType < 0 ) KokkyouType = 2; if( KokkyouType > 2 ) KokkyouType = 0; switch( KokkyouType ){ case 0: Kokkyou.text = "国境:封鎖"; break; case 1: Kokkyou.text = "国境:検疫"; break; case 2: Kokkyou.text = "国境:通常"; break; } } function update(e :Event):void{ var num:int = 0; for( var i:int=0; i<5; i++ ) StateAry[i] = 0; var agent:Agent; for each( agent in AgentAry ) { agent.update(); num++; } clearBmp(); for each( agent in AgentAry ) agent.draw(); Time++; var date:int = Time/10; var n0:String = (StateAry[0]/num*100).toFixed(2); var n1:String = (StateAry[1]/num*100).toFixed(2); var n2:String = (StateAry[2]/num*100).toFixed(2); var n3:String = (StateAry[3]/num*100).toFixed(2); var n4:String = (StateAry[4]/num*100).toFixed(2); Text0.text = ""+ date +"日目"; Text1.htmlText = "<font color='#006000'>健康</font>: "+n0+"% " + "<font color='#b0b000'>潜伏</font>: "+n1+"% " + "<font color='#b00000'>発症</font>: "+n2+"% " + "<font color='#003090'>免疫</font>: "+n3+"% " + "<font color='#303030'>死亡</font>: "+n4+"% " ; } function clearBmp():void{ var col:int = 0xc0c0c0; for( var x:int=0; x<BITMAP_W; x++){ for( var y:int=0; y<BITMAP_H; y++){ BmpData.setPixel(x, y, BgColor); } } if( KokkyouType <= 1 ){ col = WallColor; if( KokkyouType == 1 ) col = WallColor2; for( x=0; x<BITMAP_W; x++){ BmpData.setPixel(x, 86, col); BmpData.setPixel(x, 87, col); BmpData.setPixel(x, 88, col); BmpData.setPixel(128, x, col); BmpData.setPixel(129, x, col); BmpData.setPixel(130, x, col); BmpData.setPixel(x,201, col); BmpData.setPixel(x,202, col); BmpData.setPixel(x,203, col); } } for( x=0; x<BITMAP_W; x++){ BmpData.setPixel(x, 0, WallColor); BmpData.setPixel(x, BITMAP_W-1, WallColor); BmpData.setPixel(0, x, WallColor); BmpData.setPixel(BITMAP_W-1, x, WallColor); if( x<25 || (x>55 && x<170) || (x>178 && x<297) || (x>300) ){ BmpData.setPixel(x, 85, WallColor); BmpData.setPixel(x, 86, WallColor); BmpData.setPixel(x, 87, WallColor); BmpData.setPixel(x, 88, WallColor); BmpData.setPixel(x, 89, WallColor); BmpData.setPixel(127, x, WallColor); BmpData.setPixel(128, x, WallColor); BmpData.setPixel(129, x, WallColor); BmpData.setPixel(130, x, WallColor); BmpData.setPixel(131, x, WallColor); BmpData.setPixel(x,200, WallColor); BmpData.setPixel(x,201, WallColor); BmpData.setPixel(x,202, WallColor); BmpData.setPixel(x,203, WallColor); BmpData.setPixel(x,204, WallColor); } } } class Agent{ public var X:Number; public var Y:Number; public var Hp:int, HpMax:int; public var Status:int = 0; // 0:健康 1:潜伏期間 2:発病 public var CountDown:int = 0; public function Agent( x:Number, y:Number, px:Number, py:Number ){ do{ X = x + px*Math.random(); Y = y + py*Math.random(); } while( BmpData.getPixel(X, Y) != BgColor ); Hp = 5000 + Math.random() * 5000; AgentAry.push(this); if( Math.random() < 0.001 ) Status = 1; } public function update():void{ StateAry[Status]++; if( Status != 4 ){ var add:Number = 0; if( Status == 2 ) add = kakuri; // 隔離パラメータ var sx:Number = moveRate * (Math.random()*5 - 2.5) + add; var sy:Number = moveRate * (Math.random()*5 - 2.5) + add; var bgCol:int = BmpData.getPixel(X+sx, Y+sy); if( bgCol != WallColor && !(Status == 2 && KokkyouType == 1 && bgCol == WallColor2) ){ X += sx; Y += sy; if( X < 2 ) X = 2; if( X > BITMAP_W-4 ) X = BITMAP_W-4; if( Y < 2 ) Y = 2; if( Y > BITMAP_H-4 ) Y = BITMAP_H-4; } } if( Status == 0 ){ if( Hp < HpMax ) { Hp+=3; } if( BmpData.getPixel(X+sx, Y+sy) == ColRed0 ){ if( Math.random()*100 < kansen ) { // 感染力 Status = 1; CountDown = senpuku * 10; // 潜伏期間 } } } if( Status == 1 ){ CountDown--; if( CountDown <= 0 ) { CountDown = hassyou * 10; // 発症時間 Status = 2; } } if( Status == 2 ){ Hp-= dokusei * 10; // 毒性の強さ CountDown--; if( CountDown <= 0 ) { CountDown = meneki * 10; // 免疫期間 Status = 3; } if( Hp <= 0 ) { Status = 4; } } if( Status == 3 ){ CountDown--; if( CountDown <= 0 ) { CountDown = 0; // Status = 0; } } } public function draw():void{ var col0:int, col1:int; switch( Status ){ case 0: col0 = col1 = ColGreen; break; case 1: col0 = col1 = ColYellow; break; case 2: col0 = ColRed0; col1 = ColRed1; break; case 3: col0 = col1 = ColBlue; break; case 4: col0 = col1 = ColGray; break; } BmpData.setPixel(X, Y, col0); setPixel(X+1, Y, col1); setPixel(X, Y+1, col1); setPixel(X+1, Y+1, col1); } } function setPixel( x:int, y:int, col:int):void{ if( BmpData.getPixel(x, y) == BgColor ){ BmpData.setPixel(x,y, col); } } import flash.events.*; import flash.utils.*; import flash.text.*; import flash.ui.Keyboard; class InputArea { public function InputArea( x:int, y:int, str:String, num:String, func:Function ){ var tx:TextField = new TextField(); tx.text = str; tx.x = x; tx.y = y; Main.addChild(tx); var textArea:InputTextArea = new InputTextArea(func); textArea.x = 1 + x + str.length * 10; textArea.y = y; textArea.text = num; Main.addChild(textArea); } } class InputTextArea extends TextField { public var Func:Function; public var myTimer:Timer = new Timer(2, 1); public function InputTextArea( func:Function ) { border = true; borderColor = 0xCCCCCC; width = 24; height = 16; type = TextFieldType.INPUT; restrict = "0-9"; Func = func; var format:TextFormat = new TextFormat( "AXIS Std R", 12, 0, true ); format.align = "right"; defaultTextFormat = format; addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler); addEventListener(Event.CHANGE, function(e:Event):void{Func(text);}); myTimer.addEventListener(TimerEvent.TIMER, timerHandler); addEventListener(FocusEvent.FOCUS_IN, function(e:Event):void{ myTimer.start(); }); } private function timerHandler(e:Event):void { setSelection(0, length); } private function keyDownHandler(event:KeyboardEvent):void { if ( event.keyCode == Keyboard.ENTER ) { var inputText:String = text; Func(text); dispatchEvent( new InputEvent( InputEvent.INPUT_ENTER, inputText ) ); } } } flash.events.Event; class InputEvent extends Event { public static const INPUT_ENTER:String = "InputEvent.inputEnter"; private var _text:String = ""; public function InputEvent(type:String, text:String, bubbles:Boolean=false, cancelable:Boolean=false) { super(type, bubbles, cancelable); _text = text; } public override function clone():Event { return new InputEvent(type, _text, bubbles, cancelable); } public override function toString():String { return formatToString("InputEvent", "text", "type", "bubbles", "cancelable", "eventPhase"); } public function get text():String { return _text; } } import flash.display.Sprite; import flash.events.Event; import flash.text.TextField; import flash.geom.Matrix; import flash.filters.ColorMatrixFilter; import flash.filters.GlowFilter; class Button extends Sprite{ private static const mono:ColorMatrixFilter = new ColorMatrixFilter([ 1 / 3, 1 / 3, 1 / 3, 0, 10, 1 / 3, 1 / 3, 1 / 3, 0, 10, 1 / 3, 1 / 3, 1 / 3, 0, 10, 0, 0, 0, 1, 0 ]); private var _textField:TextField = new TextField(); private var _size:int; private var _hover:Boolean = false; public function get hover():Boolean{ return _hover; } public function set hover(value:Boolean):void{ if(_hover != value){ _hover = value; filters = (_hover ? null : [mono]); } } public function Button(W:Number, H:Number, R:Number, label:String = "", size:int = 11){ var matrix:Matrix = new Matrix(); matrix.createGradientBox(W, H, Math.PI / 2); var bg:Sprite = new Sprite(); bg.graphics.beginGradientFill("linear", [0x8c99a4, 0xc5d4e1, 0xBAD2E8], [1, 1, 1], [0, 120, 136], matrix); bg.graphics.drawRoundRect(0, 0, W, H, R, R); bg.graphics.endFill(); bg.filters = [new GlowFilter(0xFFFFBE, .5, 10, 10, 2, 1, true)]; addChild(bg); var line:Sprite = new Sprite(); line.graphics.lineStyle(3, 0xBAD2E8); line.graphics.drawRoundRect(0, 0, W, H, R, R); addChild(line); filters = [mono]; buttonMode = true; mouseChildren = false; if (label != ""){ _size = size; _textField.selectable = false; _textField.autoSize = "left"; _textField.htmlText = <font size={size} color="#4B4349">{label}</font>.toXMLString(); _textField.x = (W - _textField.width) / 2; _textField.y = (H - _textField.height) / 2; addChild(_textField); } addEventListener("rollOver", buttonRollOver); addEventListener("rollOut", buttonRollOut); addEventListener("removed", function(event:Event):void{ removeEventListener("rollOver", buttonRollOver); removeEventListener("rollOut", buttonRollOut); removeEventListener("removed", arguments.callee); }); } public function setLabel( label:String ):void{ _textField.htmlText = <font size={_size} color="#4B4349">{label}</font>.toXMLString(); } protected function buttonRollOver(event:Event):void{ hover = true; } protected function buttonRollOut(event:Event):void{ hover = false; } } 伝染病のシミュレーション