In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

forked from: forked from: 少し凝ったAI + SnakeGame

とりあえず、途中の経路で蛇が通れない箇所をつくらないように
つくろうとおもう

Snake Game とりあえず100行以内で書きたかった。
おバカなAI追加
Get Adobe Flash player
by uwi 18 Feb 2010
/**
 * Copyright uwi ( http://wonderfl.net/user/uwi )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/zHyM
 */

// forked from uwi's forked from: 少し凝ったAI + SnakeGame
// forked from uwi's 少し凝ったAI + SnakeGame
// forked from bkzen's 簡易AI + SnakeGame
// forked from bkzen's SnakeGame
package  
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.geom.Rectangle;
    import flash.text.TextField;

    // とりあえず、途中の経路で蛇が通れない箇所をつくらないように
    // つくろうとおもう
    /**
     * Snake Game とりあえず100行以内で書きたかった。
     * おバカなAI追加
     */
    [SWF (backgroundColor = "0xFFFFFF", frameRate = "60", width = "465", height = "465")]
    public class SnakeGame extends Sprite
    {
        // bmp, bmd 描画されるところ。
        private var bmp: Bitmap, bmd: BitmapData;
        // key は今から進もうと思っている方向, way は今進んでいる方向。
        private var key: uint, way: uint;
        // snake は ヘビの配列、[0] が先頭。 point はヘビが取っていくドット。
        private var snake: Array, point: Snake;
        // 描画領域
        private var viewRect: Rectangle;
        // speedK は スピード調整用の係数。 1 ~ N (小さいほど早い)、 speedC は wait 用のカウンタ(いじる必要なす)、 snakeLength は ヘビの長さ。(1~)
        private var speedK: int = 10, speedC: int = 0, snakeLength: int = 1;
       
        public function SnakeGame()
        {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
       
        private function init(e: Event = null): void
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            //
            addChild(bmp = new Bitmap(bmd = new BitmapData(stageW, stageW, false, 0)));
            bmp.scaleX = bmp.scaleY = cellSize;
            bmd.fillRect(viewRect = new Rectangle(1, 1, stageW2, stageW2), 0xFFFFFF);
            snake = [new Snake(stageW >> 1, stageW >> 1)], point = new Snake();
            //stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown); // AI を作るときはこの行をコメントアウト。
            addEventListener(Event.ENTER_FRAME, loop);
            
            _tf = new TextField();
            addChild(_tf);
            _tf.textColor = 0xff0000;
            _tf.width = 200;
            _tf.height = 465;
        }
        
        private var _tf : TextField;
        
        /**
         * 壁が消えていくのに一応対応しているが、
         * すでに確定してしまったセルには戻れないため、
         * どこかで暇を潰したあと到達できるような経路が描けない
         */
        private function AStar() : Array
        {
            var map : Array = new Array(stageW * stageW);
            var i : uint, j : uint;
            // マップぬりぬり
            for(j = 0;j < stageW;j++){
            		for(i = 0;i < stageW;i++){
            			map[j*stageW+i] = new Cell(i, j);
            		}
            }
            for(i = 0;i < stageW;i++){
            		map[i].wall = -1;
            		map[i * stageW].wall = -1;
            		map[(stageW - 1) * stageW + i].wall = -1;
            		map[i * stageW + stageW - 1].wall = -1;
            }
            for(i = 1;i < snake.length;i++){
            		map[snake[i].y * stageW + snake[i].x].wall = i;
            }
            map[point.y * stageW + point.x].wall = -2;
            
            // 起点のセルをセット
            var start : uint = snake[0].y * stageW + snake[0].x;
            map[start].d = 0;
            map[start].score = 0;
            var unsolved : Object = {};
            unsolved[start] = map[start];
            _tf.appendText("" + point.x + "\t" + point.y + "\n");
			_tf.scrollV = _tf.maxScrollV; 
            
            // DPで探索
            var c : Cell;
            for(;;){
            		// スコアが最小(最もゴールに近い点)を選ぶ
            		var minScore : Number = Number.MAX_VALUE;
            		var minInd : int = -1;
            		for(var key : String in unsolved){
            			if(unsolved[key].score < minScore){
            				minScore = unsolved[key].score;
            				minInd = int(key);
            			}
            		}
            		if(minInd == -1){
            			return null;
            		}
            		delete unsolved[minInd];
            		
            		c = map[minInd];
            		if(c.wall == -2)break;
            		
            		// 直前からの移動・・曲がりの取得のためにいれたけど
            		// もう使わないかな
            		var prevDelta : int = 0;
            		if(c.prev != null){
	            		prevDelta = minInd - (c.prev.y * stageW + c.prev.x);
            		}
            		
            		// 新たに調べるべきセルを追加。
            		// 選ばれたセルの上下左右について
            		for each(var delta : int in [-1, 1, -stageW, stageW]){
            			var ind : int = minInd + delta;
            			var nc : Cell = map[ind];
            			// 壁もしくは設定済みのセルならスルー
            			if(nc.wall == -1 || nc.d > -1)continue;
            			if(nc.wall > 0 && nc.wall + c.d < snake.length)continue;
            			
            			/*
            			var nwall : int = 0; 
            			for each(var dd : int in [-1, 1, -stageW, stageW, -stageW-1, -stageW+1, stageW-1, stageW+1]){
   	         			var indd : int = ind + dd;
     	       			var ncd : Cell = map[indd];
	   	        			if(ncd.wall == -1){ nwall++; continue; }
 	           			if(ncd.wall > 1){nwall++; continue; }
            			}
            			*/
            			// でもへんだな、それだとここにひっかかるはずなのに・・
            			// だめぽおおおおお もうちょっとしっかり考えないとだめだ。
            			if(!isConnectedSpace(nc, c, snake, point))continue;
            			
	            		var d : int = c.d + 1;
            			var score : Number = Math.sqrt((nc.x - point.x) * (nc.x - point.x) + (nc.y - point.y) * (nc.y - point.y));
//            			if(score < nc.score){
            				nc.score = score;
            				nc.d = d;
	            			nc.prev = c;
//            			}
            			unsolved[ind] = nc;
            		}
            }
            
            var way : Array = [];
            for(;c.prev != null;c = c.prev){
            		way.push(c);
            }
            way.reverse();
            return way;
        }
        
        	// cとそれ以前snake.length個でできる蛇と壁によって
        	// できる白い空間は連結かどうか判定する
        private function isConnectedSpace(head : Cell, prev : Cell, snake : Array, goal : Snake) : Boolean
        {
        		var bmd : BitmapData = new BitmapData(31, 31, false, 0x000000);
        		var i : int, j : int
        		var c : Cell;
        		bmd.lock();
    			bmd.fillRect(new Rectangle(1, 1, 31-2, 31-2), 0xffffff);
    			
    			bmd.setPixel(head.x, head.y, 0x000000);
    			for(i = 0, c = prev;i < snake.length;i++, c = c.prev){
    				if(c == null){
    					for(j = 0;j < snake.length - i;j++){
		    				bmd.setPixel(snake[j].x, snake[j].y, 0x000000);
    					}
    					break;
    				}    				
    				bmd.setPixel(c.x, c.y, 0x000000);
    			}
    			/*
    			bmd.setPixel(goal.x, goal.y, 0x000000);
    			bmd.setPixel(goal.x-1, goal.y, 0x000000);
    			bmd.setPixel(goal.x+1, goal.y, 0x000000);
    			bmd.setPixel(goal.x, goal.y-1, 0x000000);
    			bmd.setPixel(goal.x, goal.y+1, 0x000000);
    			*/
    			
    			// 白い点を見つける
    			outer:
    			for(i = 1;i < 31-1;i++){
    				for(j = 1;j < 31-1;j++){
    					if(bmd.getPixel(i, j) == 0xffffff)break outer;
    				}
    			}
    			
    			bmd.floodFill(i, j, 1); // ぬりぬり
    			
    			var rect : Rectangle = bmd.getColorBoundsRect(0xffffff, 0xffffff);
    			bmd.unlock();
    			bmd.dispose();
    			return rect.isEmpty();
        }
        
        private function onKeyDown(e: KeyboardEvent ): void { changeWay(e.keyCode); }
        /**
         * 方向変更
         * @param    keyCode
         * @return    <Boolean> : 方向変更可能だったら true
         */
        private function changeWay(keyCode: uint): Boolean
        {
            var b: Boolean;
            switch (keyCode)
            {
                case UP:       if (b = (way != 1 << 1)) key = 1 << 0; break;
                case DOWN:     if (b = (way != 1 << 0)) key = 1 << 1; break;
                case LEFT:     if (b = (way != 1 << 3)) key = 1 << 2; break;
                case RIGHT:    if (b = (way != 1 << 2)) key = 1 << 3; break;
            }
            return b;
        }
        
        private var optWay : Array = null;
        private var p : uint = 0;
        
        private function loop(e: Event ): void
        {
            if (speedC++ % speedK == 0)
            {
                var s: Snake = snake[0], i: int, c: uint, tx: int, ty: int;
                var vx : int = 0, vy : int = 0;
                if(optWay != null){
	               vx = optWay[p].x - optWay[p].prev.x;
	 	           vy = optWay[p].y - optWay[p].prev.y;
	 	           p++;
                }
                tx = s.x, ty = s.y;
                s.x += vx; s.y += vy;
                c = bmd.getPixel(s.x, s.y);
                bmd.lock();
                bmd.fillRect(viewRect, 0xFFFFFF);
                bmd.setPixel(s.x, s.y, 1);
                for (i = 1; i < snakeLength; i++)
                {
                    s = snake[i];
                    vx = s.x, vy = s.y;
                    bmd.setPixel(s.x = tx, s.y = ty, 0);
                    tx = vx, ty = vy;
                } 
                bmd.setPixel(point.x, point.y, 0xFF0000);
                bmd.unlock();
                
                if(optWay == null || c == 0xff0000){
                		snakeLength = snake.push(point);
                		var pindList : Array = [];
                		for(i = 0;i < stageW * stageW;i++){
                			if(bmd.getPixel(i%stageW,i/stageW) == 0xffffff){
                				if(Math.abs(point.x - (i%stageW)) + Math.abs(point.y - uint(i/stageW)) > 1){
	                				pindList.push(i);
                				} 
                			}
                		}
                		var rind : int = pindList[int(Math.random() * pindList.length)];
                		point = new Snake(rind%stageW,rind/stageW);
                		speedK = speedK < 2 ? 1 : speedK - 1;
                		optWay = AStar();
                		if(optWay == null){
	                		removeEventListener(Event.ENTER_FRAME, loop);
	                		return;
                		}
                		p = 0;
                }else if (c == 0) { 
                		removeEventListener(Event.ENTER_FRAME, loop);
                		return;
                	}
            } 
        }
    }
}

const stageW:    int = 31;            // stage のサイズ
const stageW2:   int = stageW - 2;    // stage のサイズから、枠を除いた数
const cellSize:  int = 15;            // 一つ一つのセルのサイズ (px)
const UP:        uint = 38;           // AI が changeWay する時用の 上 Key のキーコード
const DOWN:      uint = 40;           // AI が changeWay する時用の 下 Key のキーコード
const LEFT:      uint = 37;           // AI が changeWay する時用の 左 Key のキーコード
const RIGHT:     uint = 39;           // AI が changeWay する時用の 右 Key のキーコード

class Snake
{
    public var x: int, y: int;
    function Snake(x_: int = 0, y_: int = 0)
    {
        x = x_ || Math.random() * stageW2 + 1, y = y_ || Math.random() * stageW2 + 1;
    }
}

class Cell
{
	public var wall:int;
	public var x:int;
	public var y:int;
	public var d:int;
	public var prev:Cell;
	public var score:Number;
	
	public function Cell(x : int, y : int)
	{
		this.x = x;
		this.y = y;
		this.d = -1;
		this.wall = 0;
		this.prev = null;
		this.score = Number.MAX_VALUE;
	}
}