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

Garbage TETRISをズルしてみた

ごみテトリスはTD(0)の犠牲になったのだ・・

前からつくってたテトリス強化学習を使用。
素性は最大高、穴の数、1x(>=2)の溝の数, 1x(>=3)の溝の数, 1x(>=4)の溝の数。

ゴミが散らばるところのウェイトを110/30→15/30秒に
/**
 * Copyright uwi ( http://wonderfl.net/user/uwi )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/A4x3
 */

/*
ごみテトリスはTD(0)の犠牲になったのだ・・

前からつくってたテトリス強化学習を使用。
素性は最大高、穴の数、1x(>=2)の溝の数, 1x(>=3)の溝の数, 1x(>=4)の溝の数。

ゴミが散らばるところのウェイトを110/30→15/30秒に
*/
package
{
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import com.bit101.components.*;
    import net.wonderfl.score.basic.*;
    import flash.text.*;
    
    [SWF(width="465", height="465", backgroundColor="0xFFFFFF", frameRate="60")]
    public class Tetris extends Sprite
    {
        private var label:Label;
        private var btn_start:PushButton;
        private var btn_ranking:PushButton;
        private var btn_clean:PushButton;
        private var play:Boolean = false;
        
        private var form_score:BasicScoreForm;
        private var form_ranking:BasicScoreRecordViewer;
        
        private var buffer:BitmapData = new BitmapData(465, 465, false, 0x000000);
        private var screen:Bitmap = new Bitmap(buffer);
        
        private var gamedata:GameData;
        private var field:Field;
        
        public var _tf : TextField;
        
        function Tetris()
        {
           _tf = new TextField();
           _tf.height = 465;
            
            Key.setListener(this);
            KeyWatcher.watch("LEFT",  37, 100, 65);
            KeyWatcher.watch("RIGHT", 39, 102, 68);
            KeyWatcher.watch("UP",    38, 104, 87);
            KeyWatcher.watch("DOWN",  40, 98, 83);
            KeyWatcher.watch("ROT_L", 90, 75);
            KeyWatcher.watch("ROT_R", 16, 88, 74);
            func_key = new KeyWatcher();
            
            addChild(screen);
            
            Style.LABEL_TEXT = 0x000000;
            label = new Label(this, 0, 0);
            message("Garbage TETRIS");
            
            btn_start = new PushButton(this, 182, 300, "START", startGame);
            btn_ranking = new PushButton(this, 182, 330, "RANKING", showRanking);
            btn_clean = new PushButton(this, 182, 380, "CLEAN UP!", clean);
            btn_clean.visible = false;
            
            gamedata = new GameData();
            field = new Field(gamedata, this);
            addEventListener(Event.ENTER_FRAME, onEnterFrame);
            
//           addChild(_tf);
        }
        
        public function message(text:String):void
        {
            if(label != null) {this.removeChild(label);}
            
            label = new Label(this, 0, 0, text);
            label.x = 465/2 - label.width*3;
            label.y = 465/2 - label.height*3 - 50;
            label.scaleX = label.scaleY = 6;
            label.blendMode = BlendMode.INVERT;
        }
        
        public function startGame(e:Event):void
        {
            label.visible = false;
            btn_start.visible = false;
            btn_ranking.visible = false;
            btn_clean.visible = false;
            
            play = true;
            field.init();
            return;
        }
        
        public function entryRanking():void
        {
            label.visible = false;
            form_score = new BasicScoreForm(this, (465-280)/2, (465-160)/2, gamedata.score, "ENTRY", onCloseScoreForm);
            return;
        }
        
        private function onCloseScoreForm(succeeded:Boolean):void
        {
            if (form_score != null) {removeChild(form_score);}
            form_ranking = new BasicScoreRecordViewer(this, (465-220)/2, (465-240)/2, "RANKING", 30, true, onCloseRankingForm);
            return;
        }
        
        private function showRanking(e:Event):void
        {
            label.visible = false;
            btn_start.visible = false;
            btn_ranking.visible = false;
            btn_clean.visible = false;
            
            form_ranking = new BasicScoreRecordViewer(this, (465-220)/2, (465-240)/2, "RANKING", 30, true, onCloseRankingForm);
            return;
        }
        
        private function onCloseRankingForm():void
        {
            if (form_ranking != null) {removeChild(form_ranking);}
            
            btn_start.visible = true;
            btn_ranking.visible = true;
            
            if(play == true) {btn_clean.visible = true;}
            
            message("Garbage TETRIS");
            return;
        }
        
        private function clean(e:Event):void
        {
            field.garbage.fillRect(field.garbage.rect, 0x00000000);
            return;
        }
        
        public function onEnterFrame(e:Event):void
        {
            func_key.update();
            field.main();
            draw();
            return;
        }
        
        public function draw():void
        {
            field.draw(buffer);
            return;
        }
    }
}

    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import frocessing.color.FColor;
    
    var func_key:KeyWatcher;
    
    class GameData
    {
        public var score:int = 0;
    }
    
    class Field
    {
        public static const WIDTH:int = 10;
        public static const INVISIBLE_HEIGHT:int = 1;
        public static const HEIGHT:int = 20+INVISIBLE_HEIGHT;
        public static const G:int = 0x10000;
        
        private var gamedata:GameData;
        private var root:Tetris;
        
        public var map:Vector.<Vector.<int>>;
        private var minopool:ActorPool = new ActorPool();
        private var effectpool:ActorPool = new ActorPool();
        private var particlepool:ActorPool = new ActorPool();
        public var garbage:BitmapData = new BitmapData(465, 465, true, 0x00000000);
        
        public var x:int = Math.floor((465/2) - (20*WIDTH)/2);
        public var y:int = Math.floor((465/2) - (20*HEIGHT)/2) - (10*INVISIBLE_HEIGHT);
        
        private var tetromino:Tetromino;
        private var ghost:Tetromino;
        private var next:Tetromino;
        
        private var table:Array;
        private var drop_speed:int;
        private var lockdown_length:int;
        private var lockdown_time:int;
        private var halflock:Boolean;
        
        private var action:Function;
        private var count:int;
        
        private var _cb:CelestialBeing;
        
        function Field(gamedata:GameData, root:Tetris)
        {
            this.gamedata = gamedata;
            this.root = root;
            
            changeAction("act_blank");
            
            _cb = new CelestialBeing();
            _cb._tf = root._tf;
            return;
        }
        
        private function changeAction(name:String):void
        {
            action = this[name];
            count = -1;
            return;
        }
        
        public function init():void
        {
            map = new Vector.<Vector.<int>>(HEIGHT);
            for(var y:int = 0; y < HEIGHT; y++) {
                map[y] = new Vector.<int>(WIDTH);
                for(var x:int = 0; x < WIDTH; x++) {
                    map[y][x] = -1;
                }
            }
            
            do {
                table = MathEx.shuffle([0, 1, 2, 3, 4, 5, 6]);
            }while(
                table[0] == MinoPattern.S || table[0] == MinoPattern.Z
             || table[1] == MinoPattern.S || table[1] == MinoPattern.Z)
            
            drop_speed = G / 30;
            lockdown_length = 60;
            
            gamedata.score = 0;
            changeAction("act_ready");
            return;
        }
        
        public function main():void
        {
            count++;
            action();
            
            minopool.main();
            effectpool.main();
            particlepool.main();
            
            return;
        }
        
        // --------------------------------//
        // 状態
        // --------------------------------//
        private function act_blank():void
        {
            return;
        }
        
        private function act_ready():void
        {
            if(count == 0) {
                root.message("Ready");
            }
            
            if(count == 60)
            {
                root.message("");
                changeAction("act_move");
            }
            return;
        }
        
        private function act_move():void
        {
            if(count == 0)
            {
                shiftMino();
                
                // ゲームオーバー判定
                if(tetromino.hitTest() == true)
                {
                    tetromino.lock();
                    tetromino = null;
                    next = null;
                    changeAction("act_gameover");
                    return;
                }
            }
            
            
            // 回転
            if(func_key.isPress("ROT_L")) {tetromino.rotate(1);}
            else if(func_key.isPress("ROT_R")) {tetromino.rotate(-1);}
            
            // 横移動
            if(func_key.isPress("LEFT", true)) {tetromino.slide(-1);}
            else if(func_key.isPress("RIGHT", true)) {tetromino.slide(1);}
            
            if(func_key.isPress("UP") == true)
            {
                // ハードドロップ
                tetromino.drop(G*20);
                lockdown_time = lockdown_length;
            }
            else if(tetromino.isLanding() == false)
            {
                // ソフトドロップ
                if(func_key.isDown("DOWN") && drop_speed < G/2) {tetromino.drop(G/2);}
                else {tetromino.drop(drop_speed);}
                lockdown_time = 0;
                halflock = false;
            }
            
            _cb.interpose(root, tetromino.x, tetromino.rot);
            
            // 接地
            if(tetromino.isLanding() == true)
            {
                lockdown_time++;
                
                if(lockdown_time >= lockdown_length || func_key.isPress("DOWN"))
                {
                    changeAction("act_lockdown");
                }
                else if(func_key.isDown("DOWN"))
                {
                    if(halflock == false)
                    {
                        halflock = true;
                        lockdown_time = lockdown_length-7;
                    }
                }
            }
            
            // ゴースト処理
            ghost = tetromino.clone();
            ghost.ghost = true;
            ghost.drop(G*20);
            return;
        }
        
        private function act_lockdown():void
        {
            if(count == 0)
            {
                tetromino.lock();
                tetromino = null;
                ghost = null;
                
                // ライン判定
                for(var i:int = 0; i < HEIGHT; i++)
                {
                    if(checkLine(i) == true)
                    {
                        changeAction("act_erase");
                        return;
                    }
                }
            }
            
            if(count == 15)
            {
                changeAction("act_move");
                return;
            }
        }
        
        private function act_erase():void
        {
            var i:int;
            if(count == 0)
            {
                for(i = 0; i < HEIGHT; i++) {
                    if(checkLine(i) == true) {
                        for each(var mino:Mino in minopool.list) {
                            if (mino.py == i) {
                                mino.changeAction("act_break");
                            }
                        }
                        gamedata.score++;
                    }
                }
            }
            if(count == 8)
            {
                for(i = 0; i < HEIGHT; i++) {
                    if(checkLine(i) == true) {
                        eraseLine(i);
                    }
                }
            }
            
            if(count == 15)
            {
                changeAction("act_move");
                
                if(gamedata.score >= 50) {
                    drop_speed += G/100;
                }
            }
        }
        
        private function act_gameover():void
        {
            if(count == 0)
            {
                root.message("GAME OVER");
                
                for(var i:int = INVISIBLE_HEIGHT; i < HEIGHT; i++)
                {
                    for(var j:int = 0; j < WIDTH; j++)
                    {
                        if(map[i][j] != -1) {
                            map[i][j] = 7;
                        }
                    }
                    for each(var mino:Mino in minopool.list) {
                        mino.changeAction("act_gameover");
                    }
                }
            }
            
            if(count == 350) {
                root.entryRanking();
            }
        }
        
        // --------------------------------//
        // フィールド操作
        // --------------------------------//
        public function shiftMino():void
        {
            if(next == null)
            {
                tetromino = new Tetromino(this);
                tetromino.x = 3;
                tetromino.y = 0;
                tetromino.type = table.shift();
                tetromino.rot = 0;
            }
            else {
                tetromino = next;
                tetromino.ghost = false;
            }
            
            next = new Tetromino(this);
            next.x = 3;
            next.y = 0;
            next.type = table.shift();
            next.ghost = true;
            next.rot = 0;
            
            _cb.algo(map, tetromino.type, next.type);
            
            if(func_key.isDown("ROT_L") && tetromino.hitTest(0, 0, 1) == false) {tetromino.rotate(1);}
            else if(func_key.isDown("ROT_R") && tetromino.hitTest(0, 0, -1) == false) {tetromino.rotate(-1);}
            
            if(table.length == 0) {
                table = MathEx.shuffle([0, 1, 2, 3, 4, 5, 6]);
            }
            
            lockdown_time = 0;
            halflock = false;
            return;
        }
        
        public function writeMino(x:int, y:int, type:int):void
        {
            map[y][x] = type;
            minopool.add(new Mino(this, x, y, type));
        }
        
        public function eraseLine(h:int):void
        {
            for(var y:int = h; y >= 1; y--) {
                map[y] = map[y-1];
            }
            map[0] = new Vector.<int>(WIDTH);
            for(var x:int = 0; x < WIDTH; x++) {
                map[0][x] = -1;
            }
            for each(var mino:Mino in minopool.list) {
                if(mino.py < h) {
                    mino.py++;
                    mino.y += 20;
                }
            }
            return;
        }
        
        // --------------------------------//
        // 生成
        // --------------------------------//
        public function addBreakingEffect(px:int, py:int, type:int):void
        {
            effectpool.add(new BreakingMino(this, x+px*20+10, y+py*20+10, type));
            
            for(var i:int = 0; i < 5; i++) {
                particlepool.add(new Particle(x+px*20+10, y+py*20+10));
            }
        }
        
        public function addParticle(x:Number, y:Number):void
        {
            particlepool.add(new Particle(x, y));
        }
        
        // --------------------------------//
        // 判定
        // --------------------------------//
        public function hitTest(x:int, y:int):Boolean
        {
            if(x < 0 || x >= WIDTH || y >= HEIGHT) {return true;}
            if(y < 0) {return false;}
            return (map[y][x] >= 0 && map[y][x] <= 6);
        }
        
        public function checkLine(h:int):Boolean
        {
            for(var i:int = 0; i < WIDTH; i++) {
                if(hitTest(i, h) == false) {return false;}
            }
            return true;
        }
        
        // --------------------------------//
        // 描画
        // --------------------------------//
        public function draw(buffer:BitmapData):void
        {
            buffer.colorTransform(buffer.rect, new ColorTransform(1, 1, 1, 1, 24, 32, 48, 0));
            buffer.fillRect(new Rectangle(x, y+INVISIBLE_HEIGHT*20, WIDTH*20, (HEIGHT-INVISIBLE_HEIGHT)*20), 0x404040);
            for(var i:int = INVISIBLE_HEIGHT; i < HEIGHT; i++) {
                for(var j:int = 0; j < WIDTH; j++) {
                    buffer.fillRect(new Rectangle(x+j*20, y+i*20, 19, 19), 0x000000);
                }
            }
            
            minopool.draw(buffer);
            
            if(next != null) {next.draw(buffer)};
            if(tetromino != null) {tetromino.draw(buffer);}
            
            buffer.copyPixels(garbage, garbage.rect, new Point(0, 0));
            particlepool.draw(buffer);
            effectpool.draw(buffer);
            
            if(ghost != null) {ghost.draw(buffer);}
            return;
        }
    }
    
    class Tetromino
    {
        private var field:Field;
        public var x:int;
        public var y:int;
        public var type:int;
        public var ghost:Boolean = false;
        
        private var _rot:int;
        public function get rot():int {return _rot;}
        public function set rot(value:int):void
        {
            _rot = value;
            if(_rot < 0) {_rot = MinoPattern.TABLE[type].length-1;}
            if(_rot >= MinoPattern.TABLE[type].length) {_rot = 0;}
        }
        
        function Tetromino(field:Field)
        {
            this.field = field;
        }
        
        public function clone():Tetromino
        {
            var temp:Tetromino = new Tetromino(field);
            temp.x = x;
            temp.y = y;
            temp.type = type;
            temp.rot = rot;
            
            return temp;
        }
        
        // --------------------------------//
        // 移動・回転
        // --------------------------------//
        public function slide(vx:int):void
        {
            if(hitTest(vx) == true) {return;}
            
            x += vx;
            return;
        }
        
        public function drop(vy:int):void
        {
            if(isLanding() == true) {return;}
            
            while(vy >= 0)
            {
                y += Math.min(vy, 0x10000);
                vy -= 0x10000;
                
                if(isLanding() == true) {
                    y = (y >> 16) << 16;
                    break;
                }
            }
        }
        
        public function rotate(r:int):void
        {
            if(hitTest(0, 0, r) == false)
            {
                rot += r;
                return;
            }
            
            // Iは補正しない
            if(type == MinoPattern.I) {return;}
            
            // JLTは中央列以外にブロックが存在しない場合は補正しない
            if(type == MinoPattern.J || type == MinoPattern.L || type == MinoPattern.T)
            {
                var side:Boolean = false;
                loop : for(var i:int = 0; i < 3; i++) {
                    for(var j:int = 0; j < 3; j+=2) {
                        if(field.hitTest(x+j, ((y+0x8000)>>16)+i) == true) {side = true; break loop;}
                        if(field.hitTest(x+j, ((y+0xFFFF)>>16)+i) == true) {side = true; break loop;}
                    }
                }
                if(side == false) {
                    return;
                }
            }
            
            // 回転補正
            if(hitTest(1, 0, r) == false)
            {
                rot += r;
                x++;
                return;
            }
            if(hitTest(-1, 0, r) == false)
            {
                rot += r;
                x--;
                return;
            }
            
            return;
        }
        
        // --------------------------------//
        // 当たり判定
        // --------------------------------//
        public function hitTest(offset_x:int = 0, offset_y:int = 0, offset_rot:int = 0):Boolean
        {
            var temp:Tetromino = this.clone();
            temp.x += offset_x;
            temp.y += offset_y;
            temp.rot += offset_rot;
            
            var pattern:Array = MinoPattern.TABLE[temp.type][temp.rot];
            
            for(var i:int = 0; i < pattern.length; i++) {
                for(var j:int = 0; j < pattern[i].length; j++) {
                    if(pattern[i][j] != 0) {
                        if(field.hitTest(temp.x+j, ((temp.y+0x8000)>>16)+i) == true) {return true;}
                        if(field.hitTest(temp.x+j, ((temp.y+0xFFFF)>>16)+i) == true) {return true;}
                    }
                }
            }
            return false;
        }
        
        public function isLanding():Boolean
        {
            var temp:Tetromino = this.clone();
            var pattern:Array = MinoPattern.TABLE[temp.type][temp.rot];
            
            for(var i:int = 0; i < pattern.length; i++) {
                for(var j:int = 0; j < pattern[i].length; j++) {
                    if(pattern[i][j] != 0) {
                        if(field.hitTest(temp.x+j, (temp.y>>16)+(i+1)) == true) {return true;}
                    }
                }
            }
            return false;
        }
        
        public function lock():void
        {
            var pattern:Array = MinoPattern.TABLE[type][rot];
            
            for(var i:int = 0; i < pattern.length; i++) {
                for(var j:int = 0; j < pattern[i].length; j++) {
                    if(pattern[i][j] != 0) {
                        field.writeMino(x+j, (y>>16)+i, type);
                    }
                }
            }
            return;
        }
        
        // --------------------------------//
        // 描画
        // --------------------------------//
        public function draw(buffer:BitmapData):void
        {
            var i:int, j:int;
            var pattern:Array = MinoPattern.TABLE[type][rot];
            
            if(ghost == false)
            {
                for(i = 0; i < pattern.length; i++) {
                    for(j = 0; j < pattern[i].length; j++) {
                        if(pattern[i][j] != 0) {
                            buffer.fillRect(
                                new Rectangle(
                                    field.x + (x+j)*20 - 1, field.y + (y/0x10000+i)*20 - 1,
                                    21, 21
                                ),
                                FColor.HSVtoValue(
                                    FColor.hue(MinoPattern.COLOR[type]),
                                    FColor.saturation(MinoPattern.COLOR[type]),
                                    0.5
                                )
                            );
                            buffer.fillRect(
                                new Rectangle(
                                    field.x + (x+j)*20, field.y + (y/0x10000+i)*20,
                                    19, 19
                                ),
                                MinoPattern.COLOR[type]
                            );
                        }
                    }
                }
            }
            else {
                i = -1;
                do {
                    j = -1;
                    do {
                        var pc:Boolean, py:Boolean, px:Boolean;
                        pc = pattern[i]   == undefined || pattern[i][j]   == undefined || pattern[i][j]   == 0;
                        py = pattern[i+1] == undefined || pattern[i+1][j] == undefined || pattern[i+1][j] == 0;
                        px = pattern[i]   == undefined || pattern[i][j+1] == undefined || pattern[i][j+1] == 0;
                        
                        if(pc != py)
                        {
                            buffer.fillRect(
                                new Rectangle(
                                    field.x + (x+j)*20, field.y + (y/0x10000+i+1)*20-1,
                                    20, 2
                                ),
                                MinoPattern.COLOR[type]
                            );
                        }
                        if(pc != px)
                        {
                            buffer.fillRect(
                                new Rectangle(
                                    field.x + (x+j+1)*20-1, field.y + (y/0x10000+i)*20,
                                    2, 20
                                ),
                                MinoPattern.COLOR[type]
                            );
                        }
                        j++;
                    } while(j < pattern[0].length)
                    i++;
                } while(i < pattern.length)
            }
            return;
        }
    }
    
    class Actor
    {
        protected var count:int = -1;
        public var deleteflag:Boolean = false;
        
        public var x:Number = 0;
        public var y:Number = 0;
        public var visible:Boolean = true;
        
        public var action:String = "act_blank";
        
        public function main():void
        {
            count++;
            this[action]();
        }
        
        public function act_blank():void {}
        
        public function draw(buffer:BitmapData):void {}
        
        public function changeAction(name:String):void
        {
            action = name;
            count = -1;
        }
        
        public function vanish():void
        {
            deleteflag = true;
        }
    }
    
    class ActorPool
    {
        public var list:Array;
        
        function ActorPool()
        {
            list = new Array();
        }
        
        public function get length():uint {return list.length;}
        
        public function add(actor:Actor):Actor
        {
            list.push(actor);
            return actor;
        }
        
        public function main():void
        {
            for(var i:int = 0; i < list.length; i++)
            {
                list[i].main();
                if(list[i].deleteflag == true)
                {
                    list.splice(i, 1);
                    i--;
                }
            }
        }
        
        public function draw(bmp:BitmapData):void
        {
            for(var i:int = 0; i < list.length; i++)
            {
                if(list[i].visible == false) {continue;}
                list[i].draw(bmp);
            }
        }
    }
    
    class Mino extends Actor
    {
        private var field:Field;
        public var px:int;
        public var py:int;
        public var type:int;
        public var color:uint;
        
        function Mino(field:Field, px:int, py:int, type:int):void
        {
            this.px = px;
            this.py = py;
            this.type = type;
            this.field = field;
            
            x = px*20;
            y = py*20;
            
            changeAction("act_flash");
        }
        
        public function act_flash():void
        {
            if(count < 2) {
                color = 0xFFFFFF;
            }
            else if(count < 4) {
                color = 0x000000;
            }
            else if(count < 150) {
                color = FColor.RGBtoValue(
                    (0xFF + FColor.red(MinoPattern.COLOR[type]) * (count-4)) / (count-3),
                    (0xFF + FColor.green(MinoPattern.COLOR[type]) * (count-4)) / (count-3),
                    (0xFF + FColor.blue(MinoPattern.COLOR[type]) * (count-4)) / (count-3)
                );
            }
            else {
                color = MinoPattern.COLOR[type];
            }
        }
        
        public function act_break():void
        {
            if(count % 6 < 3) {
                color = 0xFFFFFF;
            }
            else {
                color = MinoPattern.COLOR[type];
            }
            
            if(count == 12+Math.abs((px - Field.WIDTH/2) * 3)) {
                field.addBreakingEffect(px, py, type);
                vanish();
            }
        }
        
        public function act_gameover():void
        {
            if(count == 0) {
                color = MinoPattern.COLOR[type];
            }
            
            if(21 - Math.floor(count/3) == py) {
                color = 0xA0A0A0;
            }
            
            if(count == 200) {
                changeAction("act_break");
            }
        }
        
        override public function draw(buffer:BitmapData):void
        {
            buffer.fillRect(new Rectangle(field.x + x, field.y + y, 19, 19), color+0xFF000000);
        }
    }
    
    class BreakingMino extends Actor
    {
        private var field:Field;
        
        private var shape:Shape;
        private var matrix:Matrix = new Matrix();
        
        private var vx:Number;
        private var vy:Number;
        private var bound:int;
        private var angle:Number;
        private var rot:Number;
        private var h:Number;
        
        function BreakingMino(field:Field, x:Number, y:Number, type:int)
        {
            this.field = field;
            this.x = x;
            this.y = y;
            
            vx = Math.random() * 10 - 5;
            vy = Math.random() * -6 - 6;
            angle = 0;
            rot = Math.random() * 20 - 10;
            h = y - Math.random() * 20;
            
            shape = new Shape();
            shape.graphics.beginFill(MinoPattern.COLOR[type]);
            shape.graphics.drawRect(-9, -9, 18, 18);
            
            changeAction("act_main");
        }
        
        public function act_main():void
        {
            if(count <= 15 && count % 5 == 0) {
                field.addParticle(x, y);
            }
            
            x += vx;
            y += vy;
            angle += rot;
            
            if(x < 0) {
                x = 0;
                vx = -vx;
                vx /= 1.5;
            }
            if(x > 465) {
                x = 465
                vx = -vx;
                vx /= 1.5;
            }
            
            if(field.garbage.getPixel32(x, y+5) != 0 || y > 465)
            {
                if(bound > 10)
                {
                    draw(field.garbage);
                    vanish();
                    return;
                }
                else if(vy > 0 && y > h)
                {
                    vx /= 1.2;
                    vy /= -1.5;
                    rot = Math.random() * 20 - 10;
                    bound++;
                }
            }
            
            vy += 0.5;
        }
        
        override public function draw(buffer:BitmapData):void
        {
            matrix.identity();
            matrix.rotate(angle);
            matrix.translate(x, y);
            
            buffer.draw(shape, matrix);
        }
    }
    
    class Particle extends Actor
    {
        public var vx:Number;
        public var vy:Number;
        public var size:Number;
        
        function Particle(x:Number, y:Number)
        {
            this.x = x;
            this.y = y;
            
            vx = Math.random() * 10 - 5;
            vy = Math.random() * 6 - 6;
            size = 30;
            
            changeAction("act_main");
        }
        
        public function act_main():void
        {
            x += vx;
            y += vy;
            
            vx /= 1.1;
            vy += 0.3;
            size /= 1.1;
            
            if(size < 1) {
                vanish();
            }
        }
        
        override public function draw(buffer:BitmapData):void
        {
            buffer.fillRect(new Rectangle(x-size/2, y-size/2, size, size), 0xFFFFFFFF);
        }
    }
    
    class MinoPattern
    {
        public static const I:int = 0, O:int = 1, S:int = 2, Z:int = 3, J:int = 4, L:int = 5, T:int = 6;
        public static const COLOR:Array = [0xFF0040, 0xE8FF00, 0xFF40FF, 0x20F000, 0x8040FF, 0xFF8000, 0x00E0FF];
        public static const TABLE:Array = new Array();
        
        TABLE[I] = [[
            [0, 0, 0, 0],
            [1, 1, 1, 1],
            [0, 0, 0, 0],
            [0, 0, 0, 0]
        ], [
            [0, 1, 0, 0],
            [0, 1, 0, 0],
            [0, 1, 0, 0],
            [0, 1, 0, 0]
        ]];
        TABLE[O] = [[
            [0, 0, 0],
            [0, 1, 1],
            [0, 1, 1]
        ]];
        TABLE[S] = [[
            [0, 0, 0],
            [0, 1, 1],
            [1, 1, 0]
        ],[
            [1, 0, 0],
            [1, 1, 0],
            [0, 1, 0]
        ]];
        TABLE[Z] = [[
            [0, 0, 0],
            [1, 1, 0],
            [0, 1, 1]
        ],[
            [0, 0, 1],
            [0, 1, 1],
            [0, 1, 0]
        ]];
        TABLE[J] = [[
            [0, 0, 0],
            [1, 1, 1],
            [0, 0, 1]
        ],[
            [0, 1, 0],
            [0, 1, 0],
            [1, 1, 0]
        ],[
            [0, 0, 0],
            [1, 0, 0],
            [1, 1, 1]
        ],[
            [0, 1, 1],
            [0, 1, 0],
            [0, 1, 0]
        ]];
        TABLE[L] = [[
            [0, 0, 0],
            [1, 1, 1],
            [1, 0, 0]
        ],[
            [1, 1, 0],
            [0, 1, 0],
            [0, 1, 0]
        ],[
            [0, 0, 0],
            [0, 0, 1],
            [1, 1, 1]
        ],[
            [0, 1, 0],
            [0, 1, 0],
            [0, 1, 1]
        ]];
        TABLE[T] = [[
            [0, 0, 0],
            [1, 1, 1],
            [0, 1, 0]
        ],[
            [0, 1, 0],
            [1, 1, 0],
            [0, 1, 0]
        ],[
            [0, 0, 0],
            [0, 1, 0],
            [1, 1, 1]
        ],[
            [0, 1, 0],
            [0, 1, 1],
            [0, 1, 0]
        ]];
    }
    
    class Key
    {
        private static var down:Array = new Array(256);
        
        public static function setListener(target:InteractiveObject):void
        {
            target.stage.focus = target;
            
            target.addEventListener(KeyboardEvent.KEY_DOWN, function (event:KeyboardEvent):void {down[event.keyCode] = true;});
            target.addEventListener(KeyboardEvent.KEY_UP, function (event:KeyboardEvent):void {down[event.keyCode] = false;});
            target.addEventListener(FocusEvent.FOCUS_OUT, onFocusOut);
        }
        
        private static function onFocusOut(event:FocusEvent):void
        {
            event.currentTarget.stage.focus = event.currentTarget;
            for(var i:String in down) {down[i] = false;}
        }
        
        public static function isDown(keycode:int):Boolean
        {
            return down[keycode];
        }
    }
    
    class KeyWatcher
    {
        private static var watchlist:Object = new Object();
        private static var repeat_wait:uint = 12;
        private static var repeat_rate:uint = 2;
        
        private var keylist:Object = new Object();
        public var lock:Boolean = false;
        
        public function KeyWatcher():void
        {
            for(var name:String in watchlist)
            {
                keylist[name] = new Object();
                keylist[name].down    = false;
                keylist[name].press   = false;
                keylist[name].release = false;
                keylist[name].repeat = false;
                keylist[name].repeatcount = 0;
            }
            return;
        }
        
        public static function watch(name:String, ...keycodes):void {watchlist[name] = keycodes;}
        public static function unwatch(name:String):void {delete watchlist[name];}
        public static function unwatchAll():void {watchlist = new Object();}
        
        public function isDown(name:String):Boolean
        {
            if(keylist[name] is Object == false) {return false;}
            
            return keylist[name].down;
        }
        
        public function isPress(name:String, getrepeat:Boolean = false):Boolean
        {
            if(keylist[name] is Object == false) {return false;}
            
            if(getrepeat == false) {return keylist[name].press;}
            else {return keylist[name].press || keylist[name].repeat;}
        }
        
        public function isRelease(name:String):Boolean
        {
            if(keylist[name] is Object == false) {return false;}
            return keylist[name].release;
        }
        
        public function update():void
        {
            var name:String, keycode:int;
            var input:Object = new Object();
            
            if(lock == false) {
                for(name in watchlist) {
                    for each(keycode in watchlist[name]) {
                        if(Key.isDown(keycode) == true) {input[name] = true; break;}
                    }
                }
            }
            else {
                for each(keycode in watchlist[name]) {input[name] = false;}
            }
            
            for(name in keylist) {
                var key:Object = keylist[name];
                if(input[name] == true) {
                    if(key.down == false) {
                        key.press = true;
                        key.repeat = true;
                    }
                    else {
                        key.press = false;
                        key.repeat = false;
                    }
                    key.down = true;
                    key.release = false;
                    
                    key.repeatcount++;
                    if(key.repeatcount == repeat_wait) {
                        key.repeatcount -= repeat_rate;
                        key.repeat = true;
                    }
                }
                else {
                    if(key.down == true) {
                        key.release = true;
                    }
                    else {
                        key.release = false;
                    }
                    key.down = false;
                    key.press = false;
                    
                    key.repeatcount = 0;
                    key.repeat = false;
                }
            }
            return;
        }
    }
    
    class MathEx
    {
        public static function shuffle(array:Array):Array
        {
            for(var j:int, t:Number, i:int = array.length, a:Array = array.slice();i; j = Math.random() * i, t = a[--i], a[i] = a[j], a[j] = t){}
            return a;
        }
    }
    
    import flash.text.TextField;
    import flash.ui.*;
    import flash.events.*;
    
    // 俺がガンダムだ!
    // MinoPatternにのみ依存
    class CelestialBeing
    {
        public var _targX : int = -99;
        public var _targRot : int = -1;
        public var _tf : TextField;
        
        // mapにcurTypeのタイプのミノ、続いてnextTypeのタイプのミノが落ちてくるときの最適なx座標と回転を_targX, _targRotに格納する
        public function algo(map : Vector.<Vector.<int>>, curType : int, nextType : int) : void
        {
            var max : Number = -100000000;
            var maxX : int = -99;
            var maxRot : int = -1;
            for(var rot : uint = 0;rot < MinoPattern.TABLE[curType].length;rot++){
                for(var mx : int = -1;mx < 10;mx++){
                    if(isValid(map, curType, rot, mx)){
                        var v : Number = nextnextV(map, curType, nextType, rot, mx);
                        if(v >= max){
                            max = v;
                            maxX = mx;
                            maxRot = rot;
                        }
                    }
                }
            }
            _targX = maxX;
            _targRot = maxRot;
        }
        
        
        // mapにkindのタイプのミノをx座標mx, 回転rotで落とせるかどうか。
        // checkYの簡易版
        public function isValid(map : Vector.<Vector.<int>>, kind : int, rot : int, mx : int) : Boolean
        {
            var mino : Array = MinoPattern.TABLE[kind][rot];
            for(var y : uint = 0;y < mino.length;y++){
                for(var x : uint = 0;x < mino[y].length;x++){
                    if(mino[y][x] > 0){
                        if(x + mx >= 10 || x + mx < 0 || map[y][x + mx] >= 0){
                            return false;
                        }
                    }
                }
            }
            return true;
        }
        
        // mapのディープコピー
        private function clone(map : Vector.<Vector.<int>>) : Vector.<Vector.<int>>
        {
            var ret : Vector.<Vector.<int>> = new Vector.<Vector.<int>>();
            for each(var row : Vector.<int> in map){
                ret.push(row.concat());
            }
            return ret;
        }
        
        // mapにkindのタイプのミノをx座標mx, 回転rotで置くときの最大のy座標を求める
        public function checkY(map : Vector.<Vector.<int>>, kind : int, rot : int, mx : int) : int
        {
            var mino : Array = MinoPattern.TABLE[kind][rot];
            for(var my : uint = 0;my <= 20;my++){
            for(var y : uint = 0;y < mino.length;y++){
                for(var x : uint = 0;x < mino[y].length;x++){
                    if(mino[y][x] > 0){
                        if(y + my >= 21 || x + mx < 0 || x + mx >= 10 || map[y + my][x + mx] >= 0){
                            return my - 1;
                        }
                    }
                }
            }
            }
            return 20; 
        }
        
        // mapにkindのタイプのミノをx座標mx, y座標my, 回転rotで固定する
        public function fix(map : Vector.<Vector.<int>>, kind : int, rot : int, mx : int, my : int) : void
        {
            var mino : Array = MinoPattern.TABLE[kind][rot];
            for(var y : uint = 0;y < mino.length;y++){
                for(var x : uint = 0;x < mino[y].length;x++){
                    if(mino[y][x] > 0){
                        map[y + my][x + mx] = mino[y][x];
                    }
                }
            }
        }
        
        // mapの埋まっている行を消す
        public function elim(map : Vector.<Vector.<int>>) : int
        {
            var o : uint = 0;
            for(var y : int = 20;y >= 0;y--){
                for(var x : int = 0;x < 10 && map[y][x] >= 0;x++){}
                if(x == 10){
                    o++;
                }else{
                    for(x = 0; x < 10;x++){
                        map[y+o][x] = map[y][x];
                    }
                }
            }
            return o;
        }
        
        // mapにcurTypeのタイプのミノをx座標mx, 回転rotで落とし、続いてnextTypeのタイプのミノを落とした時の評価値の最大値を求める
        public function nextnextV(map : Vector.<Vector.<int>>, curType : int, nextType : int, rot : int, mx : int) : Number
        {
            var bf : Vector.<Vector.<int>> = clone(map);
            
            // シミュレート
            var my : int = checkY(bf, curType, rot, mx);
            fix(bf, curType, rot, mx, my);
            elim(bf); 
            
            var max : Number = -999999;
            for(var nrot : uint = 0;nrot < MinoPattern.TABLE[nextType].length;nrot++){
                for(var nmx : int = -1;nmx < 10;nmx++){
                    if(isValid(bf, nextType, nrot, nmx)){
                        var v : Number = nextV(bf, nextType, nrot, nmx);
                        if(v >= max){
                            max = v;
                        } 
                    }
                }
            }
            return max;
        }
        
        // mapにcurTypeのタイプのミノをx座標mx, 回転rotで落とした時の評価値を求める
        public function nextV(map : Vector.<Vector.<int>>, curType : int, rot : int, mx : int) : Number
        {
            var bf : Vector.<Vector.<int>> = clone(map);
            
            // シミュレート
            var my : int = checkY(bf, curType, rot, mx);
            fix(bf, curType, rot, mx, my);
            elim(bf);
            
            // 最大高さ
            var maxhe : uint = 0;
            var he : Vector.<uint> = new Vector.<uint>(12);
            he[0] = 20;
            he[11] = 20;
            for(var j : uint = 1;j <= 10;j++){
                for(var i : int = 1;i < 21 && bf[i][j-1] == -1;i++){}
                he[j] = 21 - i;
                if(maxhe < 21 - i)maxhe = 21 - i;
            }
            
            // 穴の個数
            var holes : uint = 0;
            for(j = 1;j <= 10;j++){
                for(i = 20;i > 20 - he[j];i--){
                    if(bf[i][j-1] == -1)holes++;
                }
            }
            holes = Math.min(holes, 99);
            
            // 1xnの溝の個数
            var hole1x2 : uint = 0;
            var hole1x3 : uint = 0;
            var hole1x4 : uint = 0;
            for(j = 1;j <= 10;j++){
                if(he[j-1] - he[j] >= 2 && he[j+1] - he[j] >= 2)hole1x2++;
                if(he[j-1] - he[j] >= 3 && he[j+1] - he[j] >= 3)hole1x3++;
                if(he[j-1] - he[j] >= 4 && he[j+1] - he[j] >= 4)hole1x4++;
            } 
            
            return _weightMaxhe[maxhe] + _weightHoles[holes] + _weightHole1x2[hole1x2] + _weightHole1x3[hole1x3] + _weightHole1x4[hole1x4];
        }

        // 評価値テーブル
        /*
        private var _weightMaxhe : Array = [-0.11091142699005256, -6.25E-4, -0.32607346966500167, -0.41000636771391363, -0.6643610374302218, -1.0340343254136584, -1.3678503071859431, -1.7424300282120946, -2.2467263944876454, -2.6837867525528876, -3.075497076167835, -3.589842595086747, -4.162010226384322, -4.606953883875305, -5.306497293728375, -5.83247554617529, -6.492127487404075, -7.126249072559087, -8.830381237685994, -9.99078189558987];
        private var _weightHoles : Array = [-0.9951036299616939, -2.714002652365599, -3.8290442674274283, -5.409285011727696, -6.177571617803351, -7.840497415820682, -8.711167240923661, -9.833758387813337, -10.792908322762113, -12.392298104553197, -13.209847091854382, -14.369854905642272, -15.16472785776689, -16.365247880776927, -17.66660687529888, -18.556267548338628, -19.453424366274657, -20.482871342174775, -21.5235617671148, -22.49143919562878, -23.808095956036578, -24.64470835550944, -25.785731638961217, -26.75615559455109, -27.55444276704792, -29.06715901655279, -29.536044150360507, -30.678543284523776, -31.762190036901263, -32.77004711043744, -33.594768595486954, -34.73877244419238, -35.68482011448714, -36.17211159129267, -37.59678108106607, -38.13057683755947, -39.0577242478738, -39.95876400962042, -41.102538073405235, -41.73237359765327, -42.31391835392503, -43.06695246045468, -44.63867324732728, -45.31791939016698, -46.241568220567466, -46.93832927688558, -47.47942891489311, -48.6705932464603, -49.2718018847069, -50.220942080769156, -51.5398798044316, -51.85215477092574, -53.213186976720486, -53.70291543391014, -54.264090966021186, -56.08597807507117, -56.11433104042477, -57.04749219959868, -59.06745802361925, -60.20681993123454, -60.71409574690122, -61.49714908367152, -63.567494584476805, -64.23340238682758, -65.62563480650992, -65.5441779916961, -66.65647820681842, -67.55337290088985, -68.25993520604214, -69.5609149908839, -70.79009254574095, -70.98499991635875, -71.36763619961678, -72.17095047509731, -72.40221981869087, -70.92401671628082, -70.38779401283738, -68.85189827466807, -68.46933214429859, -67.50295764047821, -65.53679488818484, -59.97994359422197, -61.45720967045648, -57.414444267584514, -54.532455620185296, -48.374642411628805, -46.50194631639071, -42.6294614498063, -39.55164789779951, -35.47965674713575, -33.19473154210716, -29.362568159411406, -25.470255620605776, -23.819748382912216, -20.59567164164653, -15.587929587653887, -16.56295597797669, -12.6360404838533, -9.37870745520282, -47.95938254310121];
        private var _weightHole1x2 : Array = [-0.19889687287859684, -0.8462733077784937, -1.0274809493762274, -1.1641907725555487, -0.9241252678180653, -0.01527659888817898];
        private var _weightHole1x3 : Array = [-0.25310299623311694, -1.0039579951089181, -1.232228278307462, -1.3161977466005914, -0.27601526226508344, 0.0];
        private var _weightHole1x4 : Array = [-0.30054998596265364, -1.0909098086790752, -1.3905434875046019, -1.321860614250919, -0.08228557734776255, 0.0];
        */
        private var _weightMaxhe : Array = [-0.12285499832273643, -0.003115625, -0.3596554131070766, -0.48183885901276235, -0.6497026268860713, -1.0560474077090773, -1.393706206031591, -1.8201547523258212, -2.1877332754012775, -2.7083642559953947, -3.132544749081192, -3.6899115921965335, -4.175839590870263, -4.7270786108333, -5.300685687783131, -5.89188849983255, -6.408355997944355, -7.360296011519771, -8.55514390100181, -10.248587461156928]
        private var _weightHoles : Array = [-1.1060618870670968, -3.0076014097196113, -4.0312529560596095, -5.2701591416610345, -6.150510153881986, -7.559315401072028, -8.70968722338642, -9.887740540036138, -10.986566365367826, -12.273843689318273, -13.07547489844442, -14.273907295987023, -15.208913633349608, -16.364494495763328, -17.468255823819145, -18.65783345788446, -19.54221266099099, -20.62541399665612, -21.668076757560886, -22.747017650967795, -23.96636195063555, -24.995645453869265, -25.851995928145918, -26.85986537106277, -27.76199251118153, -29.082171207776682, -29.682446598674524, -30.702669301052563, -31.51708573881357, -32.424028516531415, -33.52363624955628, -34.53361368835691, -35.47492674615509, -36.77435556897423, -37.29478174101791, -38.43132179964314, -39.206259441342546, -39.766282632949085, -40.74028286917213, -41.80524496978496, -42.52161282208889, -43.64944069398595, -44.15174168158642, -45.14939756256758, -45.97949386533002, -46.854801805115464, -47.93180778168921, -48.545366647288645, -49.3543457578992, -50.037796896128775, -51.62691913973323, -51.92854682100636, -52.86676951681512, -54.1062900014591, -54.15991559011257, -55.55789865420325, -56.62159942087705, -58.061271352323786, -59.02821118437856, -59.75875723912188, -60.66556316650196, -62.37400034976529, -63.440028847904436, -63.06227558093295, -64.53817395104771, -67.23518070816819, -66.6620906768176, -67.59521514182784, -67.52434106611504, -69.42126256639303, -70.11194372928122, -70.861243388627, -72.24769156434128, -71.79854575297018, -71.98786915094368, -73.79080133141686, -71.190712641078, -67.4207070911865, -66.94695418258067, -68.1761205935069, -65.45825835403214, -63.607871377254156, -60.70261451698965, -55.24284383782564, -52.91846444976562, -49.587575108323854, -46.94123117012143, -44.084938128940856, -41.537419854105906, -35.719064490505446, -32.541174435396854, -27.305396413620098, -24.076759824921854, -22.412070591358997, -19.449642915660355, -18.442401339159396, -16.278374279001483, -14.698621402006435, -11.670906322287946, -43.46838072402747]
        private var _weightHole1x2 : Array = [-0.2287581167346685, -0.8858843762466385, -1.1057716862866422, -1.1835349176333332, -0.9716639242273695, -0.022617043420473895]
        private var _weightHole1x3 : Array = [-0.30276893631038954, -1.0622108348522872, -1.2628840740687635, -1.327908286185353, -0.3064975330368757, -0.002964375]
        private var _weightHole1x4 : Array = [-0.36479922505598644, -1.1782744070305664, -1.4432220809163099, -1.3344880552159064, -0.08286411224484877, 0.0]
        
        // 各キーをDOWNしてからのフレーム数
        private var _tLeft : uint = 0;
        private var _tRight : uint = 0;
        private var _tZ : uint = 0; // +1
        private var _tX : uint = 0; // -1
        private var _tUp : uint = 0;
        
        // キー入力に介入する!
        public function interpose(root : EventDispatcher, x : int, rot : int) : void
        {
            if(x == -99)return; 
            
            // 同時押しの影響で、キーが少ないほうが反映されやすい?
            _tLeft = process(root, Keyboard.LEFT, _tLeft, _targX < x);
            _tRight = process(root, Keyboard.RIGHT, _tRight, _targX > x);
//            _tZ = process(root, Keyboard.Z, _tZ, _targRot > rot && !(_targRot == 3 && rot == 0));
//            _tX = process(root, Keyboard.X, _tX, _targRot < rot || (_targRot == 3 && rot == 0));
            _tZ = process(root, Keyboard.Z, _tZ, _targRot != rot);
            if(_targRot == rot && _targX == x)_tUp = process(root, Keyboard.UP, _tUp, _targRot == rot && _targX == x);
            
            /*
            if(_tLeft > 0 || _tRight > 0 || _tZ > 0 || _tX > 0 || _tUp > 0){
                _tf.text = "" + _tLeft + "\t" + _tRight + "\t" + _tZ + "\t" + _tUp + "\n";
                _tf.appendText("" + _targX + "\t" + _targRot + "\n");
                _tf.appendText("" + x + "\t" + rot + "\n");
                return;
            }
            */
        }
        
        // 各キーの処理
        // 最初の2フレームKEY_DOWN, 次の2フレームKEY_UP
        private function process(root : EventDispatcher, key : int, t : int, flag : Boolean) : int
        { 
            if(t <= 1){
                root.dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_UP, true, false, 0, key));
            }else{
                root.dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, 0, key));
            } 
            if(t > 0)t--;
            
            return (t == 0 && flag) ? 3 : t;
        }
    }