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

Bastet AI (いじわるテトリス)

出てほしくないブロックを優先的に出すイジワルテトリスです。
詳しくはこちら: http://blahg.res0l.net/2009/01/bastet-bastard-tetris/

操作方法:
←→: 移動
↓: 落下
↑: 高速落下
z x:回転

ランキング画面はこちらを使わせていただきました。
http://wonderfl.net/c/cuY4
Get Adobe Flash player
by shohei909 29 May 2011
/**
 * Copyright shohei909 ( http://wonderfl.net/user/shohei909 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/AcJ3
 */

// forked from shohei909's TETRIS AI
//モンテカルロ法を使ったTETRIS AI
//Bastet Tetris
package {
    import flash.events.Event;
    import flash.display.Sprite;
    import com.bit101.components.*; 
    import net.wonderfl.utils.WonderflAPI;
    
    [SWF(backgroundColor="0x2F2822", frameRate="30")]
    public class Main extends Sprite {
        private var tetris1:Tetris = new Tetris();
        static public const W:uint = 465; 
        static public const H:uint = 465; 

        private var line:Label;
        private var point:Label;
        private var state:Label;
        
        private var radio:Array = []
        private var ps:Array = [ "human", "randomCPU", "normalCPU", "MonteCarloCPU" ]
        
        function Main() {
            if( stage ){ init(null) }
            else addEventListener(Event.ADDED_TO_STAGE,init);
        }        
        private function init(e:Event):void{
            var flashVars:Object = root.loaderInfo.parameters;
            ScoreWindowLoader.init( this, new WonderflAPI(flashVars), init2 );
        }
        
        private function init2():void{
            line = new Label( this, 250, 400, "LINE:" );
            line.scaleX = line.scaleY = 2;
            point = new Label( this, 250, 370, "POINT:" );
            point.scaleX = point.scaleY = 2;
            //state = new Label( this, 250, 340, "" );
            //state.scaleX = state.scaleY = 2;
            var fps:FPSMeter = new FPSMeter( this, 250, 310 )
            fps.scaleX = fps.scaleY = 2;
            
            var lbl:Label = new Label( this, 0, 0, "BASTET AI" );
            lbl.scaleX = lbl.scaleY = 3;
            
            
            
            for( var i:int=ps.length-1; i>=0; i-- ){
                new RadioButton( this, 270, 80 + 40*i, ps[i], true, start );
            }
            new PushButton( this, 270, 250, "Restart", start );
            
             
            
            tetris1.player = new Human( tetris1, stage );
            tetris1.onGameOver = onGameOver;
            var map:TetrisMap = new TetrisMap(tetris1)
            map.x = ( 250 - map.width ) / 2;
            map.y = 35 + ( 430 - map.height ) / 2;
            addChild( map );
            
            tetris1.init()
            addEventListener( "exitFrame", progress );
        }
        
        public function progress( e:Event ):void{ 
            tetris1.progress();
            ///state.text = "LIFE:" + tetris1.getState2();
            point.text = "POINT:" + tetris1.point;
            line.text = "LINE:" + tetris1.line;
        }
        
        
        public function start( e:Event ):void{
            switch( e.currentTarget.label ){
                case "normalCPU":
                    tetris1.player = new NormalCPU( tetris1 );
                    break;
                case "randomCPU":
                    tetris1.player = new RandomCPU( tetris1 );
                    break;
                case "human":
                    tetris1.player = new Human( tetris1, stage );
                    break;
                case "MonteCarloCPU":
                    tetris1.player = new MonteCarloCPU( tetris1 );
                    break;
            }
            tetris1.init();
        }
        
        public function onGameOver():void{
            if( tetris1.player is Human ){
                ScoreWindowLoader.show( tetris1.point, function f():void{} );
            }
        }

    }
}

import flash.events.EventDispatcher;
import flash.events.KeyboardEvent;
import flash.display.Stage;
import flash.geom.Matrix;
import flash.geom.ColorTransform;
import flash.events.Event;
import flash.geom.Rectangle;
import flash.display.BitmapData;
import flash.display.Bitmap;
import frocessing.color.FColor;

class TetrisMap extends Bitmap{
    public var tetris:Tetris;
    
    static public const CT:ColorTransform = new ColorTransform( 0.5, 0.7, 0.8, 1 ); 
    static public const OUT_CT:ColorTransform = new ColorTransform( 0.2, 0.2, 0.2, 1, 20, 20, 20 ); 
    static public const CELL:uint = 17;
    static private function f( u:int ):uint{ return FColor.HSVtoValue(u,0.5,0.5); }
    static public const BL_C:Array = [ f(0), f(37), f(73), f(110), f(147), f(183), f(220) ];
    
    function TetrisMap( tetris:Tetris ){
        this.tetris = tetris;
        super( new BitmapData( Tetris.W*CELL+1, Tetris.H*CELL+1, false, 0 ) );
        addEventListener( "exitFrame", draw );
    }
    
    public function draw( e:Event ):void{
        var b:BitmapData = bitmapData;
        b.colorTransform( b.rect, CT );
        for( var i:uint = 0; i < Tetris.W; i++ ){
            for( var j:uint = 0; j < Tetris.H; j++ ){ 
                if( tetris.map[i][j] > -1 ){ b.fillRect( new Rectangle(i*CELL+2, j*CELL+2, CELL-3, CELL-3), 0xFF223344 ) }
            }    
        }
        drawGrid(); drawBlock();
        b.colorTransform( new Rectangle( 0,0,Tetris.W*CELL+1,Tetris.OUT*CELL ), OUT_CT );
    }
    private function drawGrid():void{
        var b:BitmapData = bitmapData;
        for( var i:uint = 0; i < Tetris.W+1; i++ ){ b.fillRect( new Rectangle( i*CELL, 0, 1, Tetris.H*CELL+1 ), 0xFF112233 ) }
        for( var j:uint = 0; j < Tetris.H+1; j++ ){ b.fillRect( new Rectangle( 0, j*CELL, Tetris.W*CELL+1, 1 ), 0xFF112233 ) }
    }
    private function drawBlock():void{
        var b:BitmapData = bitmapData;
        var m:Matrix = Tetris.DIR[tetris.dir];
        var c:uint = BL_C[tetris.block];
        var bl:Array = Tetris.BL[tetris.block];
        for( var i:uint = 0; i < 2; i++ ){
            for( var j:uint = 0; j < 4; j++ ){
                if( bl[i][j] == 1 ){
                    var x:int = tetris.bx + j*m.a + i*m.b + m.tx;
                    var y:int = tetris.by + j*m.c + i*m.d + m.ty;
                    b.fillRect( new Rectangle( x*CELL+2, y*CELL+2, CELL-3, CELL-3 ), c )
                }
            }
        }
    }
}

class Tetris implements Game{
    static public const W:uint = 10;
    static public const H:uint = 22;
    static public const OUT:uint = 2;
    static public const BL:Array = [
        /*O*/[[0,1,1,0],[0,1,1,0]],
        /*Z*/[[1,1,0,0],[0,1,1,0]],
        /*S*/[[0,1,1,0],[1,1,0,0]],
        /*I*/[[0,0,0,0],[1,1,1,1]],
        /*L*/[[0,0,1,0],[1,1,1,0]],
        /*Γ*/[[1,0,0,0],[1,1,1,0]],
        /*T*/[[0,1,0,0],[1,1,1,0]]
    ]
    static public const DIR:Array = [
        new Matrix( 1,0,0,1,-1,-1 ),
        new Matrix( 0,-1,1,0,1,-1 ),
        new Matrix( -1,0,0,-1,1,1 ),
        new Matrix( 0,1,-1,0,-1,1 )
    ];
    static public var span:int = 100;
    static public var PT_AR:Array = [1,3,10,20];
    
    public var player:Player;
    public var bastedPlayer:Player;
    public var onGameOver:Function;
    
    public var map:Vector.<Vector.<int>> = new Vector.<Vector.<int>>();
    public var bx:int; 
    public var by:int;
    public var dir:int;
    public var block:uint;
    public var count:int;
    public var point:int;
    public var line:int; 
    
    public var gameOver:Boolean = false;
    public var activePlayer:Boolean = false;
    
    function Tetris(){
        bastedPlayer = new BastedCPU( this );
    }
    
    
    public function init():void{
        activePlayer = false;
        gameOver = false;
        count = 0;
        point = 0;
        line = 0;
        for( var i:uint = 0; i < W; i++ ){
            map[i] = new Vector.<int>()
            for( var j:uint = 0; j < H; j++ ){
                map[i][j] = -1;
            }    
        }
        if( player ){ player.init() }
    }
    
    
    private function setBlock( b:uint ):void { bx = 4; by = 1; dir = 0; block = b;  activePlayer = true; }
    
    private function checkLine():void {
        var map:Vector.<Vector.<int>> = this.map;
        var count:int = -1;
        for( var j:uint = 0; j<H; j++ ){
            s:{
                var f:Boolean = true;
                for( var i:uint = 0; i<W; i++ ){
                    if( map[i][j] == -1 ){ f = false; break s; }
                }
                count++; line++; removeLine(j)
            }
        }
        if( count >= 0 ){ point += PT_AR[count] }
    }
    
    private function removeLine( h:uint ):void { 
        var map:Vector.<Vector.<int>> = this.map;
        for( var j:uint = h; j>0; j-- ){
            for( var i:uint = 0; i<W; i++ ){ 
                map[i][j] = map[i][j-1];
            }
        }
        for( var i2:uint = 0; i2<W; i2++ ){ map[i2][0] = -1; }
    }
    
    private function stopBlock():void{
        var m:Matrix = DIR[dir];
        var bl:Array = BL[block];
        var map:Vector.<Vector.<int>> = this.map;
        for( var i:uint = 0; i < 2; i++ ){
            for( var j:uint = 0; j < 4; j++ ){
                if( bl[i][j] == 1 ){
                    var x:int = bx + j*m.a + i*m.b + m.tx;
                    var y:int = by + j*m.c + i*m.d + m.ty;
                    map[x][y] = block;
                    if( y < OUT ){ 
                        if( (!gameOver) && onGameOver != null ){ onGameOver(); }
                        gameOver = true;
                    }
                }
            }
        }
        checkLine();
        activePlayer = false;
    }
    
    
    public function progress():void{
        if(! gameOver ){
            if(! activePlayer ){ bastedPlayer.action(); }
            else if( player ){ player.action(); }
            if( count++ > span && (!_action(0,1,0)) ){ stopBlock(); }
        }
    }
    
    public function action( ...arg ):void{
        if( activePlayer ){
            var dx:int = arg[0], dy:int = arg[1], spin:int = arg[2], fall:Boolean = arg[3]; 
            if( spin > 0 ){ _action(0,0,spin%4); spin = 0 }
            while( dx > 0 ){ _action(1,0,0); dx-- }
            while( dx < 0 ){ _action(-1,0,0); dx++ }
            while( dy > 0 ){ _action(0,1,0); dy-- } 
            if( fall ){ while( _action(0,1,0) ){}; }
        }else{
            setBlock( arg[0] )
        }
    }
    
    private function _action( dx:int, dy:int, ddir:int ):Boolean{
        if( block == 0 ){ ddir = 0 }
        var m:Matrix = DIR[ (dir + ddir)%4 ];
        var bl:Array = BL[ block ];
        for( var i:uint = 0; i < 2; i++ ){
            for( var j:uint = 0; j < 4; j++ ){
                if( bl[i][j] == 1 ){
                    var x:int = bx + j*m.a + i*m.b + m.tx + dx;
                    var y:int = by + j*m.c + i*m.d + m.ty + dy;
                    if( x < 0 || x >= W || y < 0 || y >= H || map[x][y] > -1 ){ if(dy>0){stopBlock()}; return false; }
                }
            }
        }
        bx+=dx; by+=dy; dir=(dir+ddir)%4;
        if( dy > 0 ){ count = 0 }
        return true;
    }
    
    public function clone():Game{ 
        var c:Tetris = new Tetris();
        var map:Vector.<Vector.<int>> = this.map;
        c.bx = bx; c.by = by; c.dir = dir; c.block = block; c.activePlayer = activePlayer;
        for( var i:uint = 0; i < W; i++ ){
            c.map[i] = new Vector.<int>()
            for( var j:uint = 0; j < H; j++ ){ c.map[i][j] = map[i][j] }
        }
        return c;
    }

    public function getValue( ...arg ):Number{
        if ( gameOver ) { return 0; }
        var num:Number = 0;
        var arr:Vector.<Number> = _getStateArray();
        return arg[0]*arr[0] + arg[1]*arr[1] + arg[2]*arr[2];
    }
    public function getDefaultValueArg():Array {
        return [1,3.0727,5.6932];
    }
    public function getPoint():Number{
        return line;
    }

    public function _getStateArray():Vector.<Number>  {  
        
        var array:Vector.<Number> = new Vector.<Number>();
        var map:Vector.<Vector.<int>> = this.map;
        var c:int = 0;
        var p:int = 0;
        var line1:Vector.<int>, line2:Vector.<int>;
        var i:uint; var j:uint; var f:Boolean;
        
        for ( i = 0; i < W; i++ ) {
            line1 = map[i]
            f = false;
            for( j = 0; j<H; j++ ){
                if ( f ) { 
                    if ( line1[j] == -1 ) { c--; }
                } else { 
                    if ( line1[j] > -1 ) { p += j; f = true }
                }
            }
            if(!f){ p += j }; 
        }
        
        array.push(p);
        p = 0;
        
        for ( i = 0; i < W - 1; i++ ) {
            line1 = map[i];
            line2 = map[i+1];
            f = false;    
            for( j = 0; j<H; j++ ){
                if ( !f ) { 
                    if ( line1[j] > -1 || line2[j] > -1 ) { p += j; f = true }
                }
            }
            if(!f){ p += j }; 
        }
        array.push(p);
        array.push(c);
        return array;
    }
    
    public function getAction():Array{
        var arr:Array = []; 
        if( activePlayer ){
            var l:uint = block == 0 ? 1 : ( block < 4 ? 2 : 4);
            for( var s:uint = 0; s < l; s++ ){
                for( var j:uint = 0; j < W; j++ ){
                    arr.push( [j-bx,0,s,true] )
                }
            }
        }else{
            for( var k:uint = 0; k < 7; k++ ){
                arr.push( [k] )
            }
        }
        return arr; 
    }
}

interface Game{
    function action( ...arg ):void
    function clone():Game;
    function getAction():Array;
    function getValue( ...arg ):Number;
    function getDefaultValueArg():Array;
}

class Player{
    public var game:Game;
    function Player( game:Game ){
        this.game = game; 
    }
    public function action():void{} 
    public function init():void{}
    public function getPoint(target:*):Object{ return null }
}

class Human extends Player{
    public var dx:int = 0;
    public var dy:int = 0;
    public var fall:Boolean = false;
    public var spin:int = 0;
    
    function Human( game:Game, stage:Stage ){
        super( game );
        stage.addEventListener( KeyboardEvent.KEY_DOWN, onKey )
    }
    
    
    override public function action():void{
        game.action( dx, dy, spin, fall );
        dx = 0; dy = 0; spin = 0; fall = false;
    }
    
    private function onKey( e:KeyboardEvent ):void{
        switch( e.keyCode ){
            case 90: spin++; break;
            case 88: spin+=3; break;
            case 38: fall=true; break;
            case 39: dx++; break;
            case 37: dx--; break;
            case 40: dy++; break;
        }
    }
    
}

class RandomCPU extends Player{
    function RandomCPU( game:Game ){ super(game); }
    override public function action():void{ actionAt( game ); }
    static public function actionAt( game:Game ):void{
        var act:Array = game.getAction();
        game.action.apply( null, act[ (act.length*Math.random()) >>> 0 ] );
    }
}

class NormalCPU extends Player {
    static public var valueRate:Array;
    public function NormalCPU( game:Game) { if(!valueRate){ valueRate=game.getDefaultValueArg() } super(game) }
    override public function action():void{    actionAt( game ) }
    static public function actionAt( game:Game ):void {
        if(!valueRate){ valueRate=game.getDefaultValueArg() }
        var act:Array = game.getAction();
        var act2:Array = [];
        var act3:Array = [];
        var arg:Array = valueRate;
        var l2:int = 0;
        var l3:int = 0;
        
        var points:Vector.<Number> = new Vector.<Number>();
        var l:uint = act.length;
        var max:int = 0;
        for( var i:uint = 0; i<l; i++ ){
            var clone:Game = game.clone();
            clone.action.apply( null, act[ i ] );
            var p:int = clone.getValue.apply( null, arg );
            if( p >= max ){ max = p; points.push( p ); act2.push( act[i] ); l2++; }
        }
        
        for( var j:uint = 0; j<l2; j++ ){
            if( points[j] == max ){
                act3.push( act2[j] ); l3++;
            }
        }
        game.action.apply( null, act3[ (l3*Math.random()) >>> 0 ] );
    }        
}

class BastedCPU extends Player{
    function BastedCPU( game:Game ){ super(game); }
    override public function action():void{ actionAt( game ); }
    static public function actionAt( game:Game ):void{ 
        var act:Vector.<Array> = Vector.<Array>( game.getAction() );
        var points:Vector.<Number> = new Vector.<Number>;
        var clones:Vector.<Game> = new Vector.<Game>;     
        var l:uint = act.length;
        var max:uint = 0;
        
        //2手読む
        function _1():void{ 
            for( var i:uint = 0; i<l; i++ ){
                var clone:Game = game.clone();
                clone.action.apply( null, act[ i ] );
                NormalCPU.actionAt( clone );
                var p:int = clone.getValue.apply( null, clone.getDefaultValueArg() );
                if( p > max ){ max = p }
                points.push( p );
                clones.push( clone );
            }
        }
        
        //点の低いものを選び出す。
        function _2():void{
            var act2:Vector.<Array> = new Vector.<Array>;
            var points2:Vector.<Number> = new Vector.<Number>;
            var clones2:Vector.<Game> = new Vector.<Game>;  
            var l2:int = 0;
            var max2:int = 0;
            
            for( var i:uint = 0; i<l; i++ ){
                var pt:int = points[i];
                l1:{
                    for( var j:int = i; j>0; j-- ){
                        if( pt >= points2[j-1] ){ break; l1; }
                    }
                    points2.splice( j, 0, points[i] );
                    clones2.splice( j, 0, clones[i] );
                    act2.splice( j, 0, act[i] );
                }
            }
            act=act2; points=points2; clones=clones2;
        }
        
        //1番良い手を70%、2番目に良い手を15%、3番目を9%、4番目を6%で実行する。
        function _3():void {
            var r:Number = Math.random(); var i:int
            if(r<0.7){ i=0}
            else if(r<0.85){ i=1 }
            else if(r<0.96){ i=2 }
            else{ i=3 }
            game.action.apply( null, act[ i ] );
        }
        
        _1(); _2(); _3();
    }
}

class MonteCarloCPU extends Player{
    public var depth:int = 2; //読みの深さ
    public var tolerance:int = 0; //寛容さ
    public var fork:int = 12; //分岐の数
    public var valueRate:Array;
    
    function MonteCarloCPU( game:Game ){ super(game) }
    override public function action():void{
        var act:Vector.<Array> = Vector.<Array>( game.getAction() );
        var points:Vector.<Number> = new Vector.<Number>;
        var clones:Vector.<Game> = new Vector.<Game>;     
        var l:uint = act.length;
        var max:uint = 0;
        if(!valueRate){ valueRate=game.getDefaultValueArg() }
        var arg:Array = valueRate            
        
        //1手読む
        function _1():void{
            var clone:Game;
            for ( var i:uint = 0; i < l; i++ ) {
                clone = game.clone();
                clone.action.apply( null, act[ i ] );
                var p:int = clone.getValue.apply( null, arg );
                if( p > max ){ max = p }
                points.push( p );
                clones.push( clone );
            }
        }
        
        //点の高いものを選び出す。
        function _2():void{   
            var act2:Vector.<Array> = new Vector.<Array>;
            var points2:Vector.<Number> = new Vector.<Number>;
            var clones2:Vector.<Game> = new Vector.<Game>;  
            var l2:int = 0;
            for( var i:uint = 0; i<l; i++ ){
                var pt:int = points[i]
                if( pt + tolerance >= max ){
                    points2.push( points[i] );
                    clones2.push( clones[i] );
                    act2.push( act[i] );
                    l2++;
                }
            }
            act=act2; points=points2; clones=clones2;
            l = l2;
        }
        
        //各手を深読み
        function _3():void {
            var g:Game;
            for( var i:uint = 0; i<l; i++ ){
                for( var j:uint = 0; j<fork; j++ ){
                    g = clones[i].clone();
                    for( var k:uint = 0; k<depth; k++ ){
                        RandomCPU.actionAt( g );
                        NormalCPU.actionAt( g );
                        points[i] += g.getValue.apply( null, valueRate );
                    }
                }
            }
        }
        
        //もっとも点の高い手を実行する。
        function _4():void {
            max = 0;
            for( var i:uint = 0; i<l; i++ ){
                var p:int = points[i];
                if( p > max ){ max = p }
                points.push( p )
            }
            for( var j:uint = 0; j<l; j++ ){
                if( points[j] < max ){
                    points.splice( j, 1 );
                    act.splice( j, 1 );
                    j--; l--;
                }
            }
            game.action.apply( null, act[ (l*Math.random()) >>> 0 ] );
        }
        _1(); _2(); _3(); _4();
    }
}

import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Graphics;
import flash.display.Loader;
import flash.events.Event;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import net.wonderfl.utils.WonderflAPI;

class ScoreWindowLoader
{
    private static var _top: DisplayObjectContainer;
    private static var _api: WonderflAPI;
    private static var _content: Object;
    //private static const URL: String = "wonderflScore.swf";
    private static const URL: String = "http://swf.wonderfl.net/swf/usercode/5/57/579a/579a46e1306b5770d429a3738349291f05fec4f3.swf";
    private static const TWEET: String = "Playing What the Hex [score: %SCORE%] #wonderfl";
    
    public static function init(top: DisplayObjectContainer, api: WonderflAPI, handler: Function = null): void 
    {
        _top = top, _api = api;
        var loader: Loader = new Loader();
        var comp: Function = function(e: Event): void
        {
            loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, comp);
            _content = loader.content;
            handler();
        }
        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, comp);
        loader.load(new URLRequest(URL), new LoaderContext(true));
    }
    
    /**
     * Wonderfl の Score API 用
     * ランキング表示から Tweet までをひとまとめにしたSWF素材を使う
     * @param    score            : 取得スコア
     * @param    closeHandler    : Window が閉じるイベントハンドら
     */
    public static function show( score: int, closeHandler: Function): void
    {
        var window: DisplayObject = _content.makeScoreWindow(_api, score, "BASTET", 1, TWEET);
        var close: Function = function(e: Event): void
        {
            window.removeEventListener(Event.CLOSE, close);
            closeHandler();
        }
        window.addEventListener(Event.CLOSE, close);
        _top.addChild(window);
    }
    
}