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

テトリスAIかいりょうばん

新しいブロックが出てから置く場所を探しています。
* NEXTは見ていません。というよりNEXT自体を実装していません。
Get Adobe Flash player
by uwi 19 Feb 2010
// forked from rsakane's テトリスAI
/*
 * 新しいブロックが出てから置く場所を探しています。
 * NEXTは見ていません。というよりNEXT自体を実装していません。
 */
package
{
  import flash.display.Bitmap;
  import flash.display.BitmapData;
  import flash.display.Sprite;
  import flash.events.Event;
  import flash.events.MouseEvent;
  import flash.utils.ByteArray;
  import flash.utils.getTimer;
  import flash.text.TextField;
  import jp.progression.commands.lists.SerialList;
  import jp.progression.commands.Wait;
  import jp.progression.commands.Func;
  
  public class Main extends Sprite
  {
    private const HEIGHT:int = 21;
    private const WIDTH:int = 12;
    
    private var bd:BitmapData;
    private var bitmap:Bitmap;
    
    private var type:int;
    
    private var field:Array;
    private var data:Array;
    
    private var _random : XorShift128 = new XorShift128(new Date().time);
    
    private const blocks:Array = 
    [
      [
        [0, 0, 0, 0],
        [0, 1, 1, 0],
        [0, 1, 0, 0],
        [0, 1, 0, 0]
      ],
      [
        [0, 2, 0, 0],
        [0, 2, 0, 0],
        [0, 2, 0, 0],
        [0, 2, 0, 0]
      ],
      [
        [0, 0, 3, 0],
        [0, 3, 3, 0],
        [0, 3, 0, 0],
        [0, 0, 0, 0]
      ],
      [
        [0, 0, 0, 0],
        [0, 4, 0, 0],
        [4, 4, 4, 0],
        [0, 0, 0, 0]
      ],
      [
        [0, 5, 0, 0],
        [0, 5, 5, 0],
        [0, 0, 5, 0],
        [0, 0, 0, 0]
      ],
      [
        [0, 0, 0, 0],
        [0, 6, 6, 0],
        [0, 6, 6, 0],
        [0, 0, 0, 0]
      ],
      [
        [0, 0, 0, 0],
        [0, 7, 7, 0],
        [0, 0, 7, 0],
        [0, 0, 7, 0]
      ]
    ];
    
    private const rb:Array = [4, 2, 2, 4, 2, 1, 4];
    //private const rb:Array = [4, 4, 4, 4, 4, 4, 4];
    
    private var block:Array;
    
    private var px:int;
    private var py:int;
    
    private var gameover:Boolean = false;
    
    public function Main()
    {
      init();
      addEventListener(Event.ENTER_FRAME, draw);
    }
    
    private var _tf : TextField;
    private var _ctBlock : uint;
    private var _ctLine : uint;
    
    private function init():void
    {
      var f:Function = function(...a):* { return 0; };
      var f2:Function = function(...a):* { return new Array(WIDTH).map(f); }
      
      field = new Array(HEIGHT + 1).map(f2);
      data  = new Array(HEIGHT + 1).map(f2);
      
      bd = new BitmapData(WIDTH, HEIGHT + 1, false, 0xFFFFFF);
      bd.lock();
      for (var y:int = 0; y < bd.height; y++)
      {
        for (var x:int = 0; x < bd.width; x++)
        {
          if (x == 0 || x == WIDTH - 1 || y == HEIGHT - 1){
            field[y][x] = data[y][x] = Status.WALL;
          }else{
            field[y][x] = data[y][x] = Status.EMPTY;
          }  
          bd.setPixel(x, y, Status.COLORS[field[y][x]]);
        }
      }
      bd.unlock();
      
      if (bitmap) removeChild(bitmap);
      bitmap = new Bitmap(bd);
      bitmap.scaleX = bitmap.scaleY = 20;
      bitmap.x = (465 - bitmap.width) / 2;
      bitmap.y = (465 - bitmap.height) / 2;
      addChild(bitmap);
      
      _tf = new TextField();
      addChild(_tf);
      _ctBlock = 0;
      _ctLine = 0;
      
      createBlock(_random.get32() % 7);
      solve();
    }
    
    private function solve():void
    {
      var l:SerialList;
      var min:Number = Number.MAX_VALUE;
      var minErased : uint = 0;
      
      var fieldAI : Array = clone(field);
      var dataAI : Array = clone(data);
      var blockAI : Array = clone(block);
      
      // 各x座標、各回転についてシミュレート
      for (var i:int = 4; i >= -5; i--)
      {
        for (var r:int = 0; r < rb[type]; r++)
        {
          var list : SerialList = new SerialList();
          for (var r2:int = 0; r2 < r; r2++)
          {
            rotate();
            addCommandLazily(list, rotate);
          }
          
          var j:int;
          if (0 <= i)
          {
            for (j = 0; j < i; j++)
            {
              left();
              addCommandLazily(list, left);
            }
          }
          else
          {
            for (j = 0; j < -i; j++)
            {
              right();
              addCommandLazily(list, right);
            }
          }
          
          while (!checkOverlap(px, py + 1))
          {
            var yy:int = py;
            var xx:int = px;
            move(xx, yy + 1);
            addCommandLazily(list, move, [xx, yy + 1]);
          }
      
          var erased : uint = lock();
          var score : Number = 0;
          // スコア計算
          for (var y:int = 0; y < HEIGHT - 1; y++)
          {
            for (var x:int = 1; x < WIDTH - 1; x++)
            {
              // 穴をつくることへの抵抗
              // これを大きくすると、穴はできにくくなるが、深い溝ができやすくなる。
              if (y >= 1 && field[y][x] == Status.EMPTY && field[y-1][x] != Status.EMPTY && field[y-1][x] != Status.WALL)
              {
                score += 15;
              }
              
              // できるだけ下側に固まるように
              if (field[y][x] != Status.EMPTY && field[y][x] != Status.WALL)
              {
                score += (HEIGHT - y);
              }
            }
          }
          
          if (min >= score)
          {
            l = list;
            min = score;
            minErased = erased;
          }
          
          field = clone(fieldAI);
          data = clone(dataAI);
          block = clone(blockAI);
          px = 4;
          py = 0;
        }
      }
      
      l.addCommand(new Func(lock));
      l.addCommand(new Func(function() : void{
          _ctLine += minErased;
          _ctBlock++;
          _tf.text = "block : " + _ctBlock + "\nline : " + _ctLine;
          }));
      l.addCommand(new Func(createBlock, [_random.get32() % 7]));
      l.addCommand(new Func(solve));
      if(!gameover)l.execute();
    }
    
    private static function addCommandLazily(sl : SerialList, func:Function, args:Array = null):void
    {
      sl.addCommand(new Func(func, args));
      sl.addCommand(new Wait(10 / 1000.0));
    }
    
    private function left():void
    {
      if (!checkOverlap(px - 1, py)) move(px - 1, py);
    }
    
    private function right():void
    {
      if (!checkOverlap(px + 1, py)) move(px + 1, py);
    }
    
    private function checkOverlap(tx:int, ty:int):Boolean
    {
      for (var y:int = 0; y < 4; y++)
      {
        for (var x:int = 0; x < 4; x++)
        {
          if (block[y][x])
          {
            if (data[y + ty][x + tx] != 0) return true;
          }
        }
      }
      
      return false;
    }
    
    private function clone(arg:*):*
    {
      var b:ByteArray = new ByteArray();
      b.writeObject(arg);
      b.position = 0;
      return b.readObject();
    }
    
    private function rotate():void
    {
      var temp:Array = clone(block);
      
      for (var y:int = 0; y < 4; y++)
      {
        for (var x:int = 0; x < 4; x++)
        {
          block[y][x] = temp[3 - x][y];
        }
      }
      
      if (checkOverlap(px, py))
      {
        block = temp;
        return;
      }
      
      for (y = 0; y < 4; y++)
      {
        for (x = 0; x < 4; x++)
        {
            if(field[py + y][px + x] != Status.WALL){
              field[py + y][px + x] = block[y][x];
            }
        }
      }
    }
    
    // ライン消しチェック
    private function checkLines():uint
    {
      var flag:Boolean;
      var ret : uint = 0;
      
      while (true)
      {
        for (var y:int = 0; y < 20; y++)
        {
          flag = true;
          for (var x:int = 1; x < 11; x++)
          {
            if (data[y][x] == 0) flag = false;
          }
          if (flag) break;
        }
        if (!flag) break;
        
        for (x = 1; x < WIDTH - 1; x++) data[y][x] = 0;
        ret++;
        
        for (; y > 0; y--)
        {
          for (x = 1; x < WIDTH - 1; x++)
          {
            data[y][x] = data[y - 1][x];
          }
        }
      }
      
      return ret;
    }
    
    private function move(tx:int, ty:int):void
    {
      for (var y:int = 0; y < 4; y++)
      {
        for (var x:int = 0; x < 4; x++)
        {
            if(block[y][x]){
              field[py + y][px + x] = 0;
            }
        }
      }
      
      px = tx;
      py = ty;
      
      for (y = 0; y < 4; y++)
      {
        for (x = 0; x < 4; x++)
        {
            if(block[y][x]){
              field[py + y][px + x] = block[y][x];
            }
        }
      }
    }
    
    private function lock():uint
    {
      for (var y:int = 0; y < HEIGHT; y++)
      {
        for (var x:int = 0; x < WIDTH; x++)
        {
          data[y][x] = field[y][x];
        }
      }
      
      var ret : uint = checkLines();
      
      if(ret > 0){
        for (y = 0; y < HEIGHT; y++)
        {
          for (x = 0; x < WIDTH; x++)
          {
            field[y][x] = data[y][x];
          }
        }
      }
      
      return ret;
    }
    
    // solveでもrandomに生成してるのである意味いんちき
    // なので修正
    private function createBlock(type : int):Boolean
    {
      px = 4;
      py = 0;
      
//      type = Math.random() * 7;
      
      block = clone(blocks[type]);
      
      for (var y:int = 0; y < 4; y++)
      {
        for (var x:int = 0; x < 4; x++)
        {
            if(block[y][x] != 0 && field[py + y][px + x] != Status.EMPTY){
                gameover = true;
                return false;
            }
          field[py + y][px + x] = block[y][x];
        }
      }
      
      return true;
    }
    
    
    private function draw(event:Event = null):void
    {
      bd.lock();
      bd.fillRect(bd.rect, 0xFFFFFF);
      for (var y:int = 0; y < HEIGHT; y++)
      {
        for (var x:int = 0; x < WIDTH; x++)
        {
            bd.setPixel(x, y, Status.COLORS[field[y][x]]);
        }
      }
      bd.unlock();
    }
  }
}

class Status
{
  public static const WALL:int = 9;
  public static const EMPTY:int = 0;
  
  public static const COLORS : Object = {
    9 : 0x0, 
    7 : 0xcc22cc,
    6 : 0xcccc22,
    5 : 0x22cccc,
    4 : 0x2222cc,
    3 : 0x22cc22,
    2 : 0xcc2222,
    1 : 0x990039, 
    0 : 0xffffff
  };
}

class XorShift128
{
    private var a:uint;
    private var b:uint;
    private var c:uint;
    private var d:uint;
    
    public function XorShift128(seed : uint = 0)
    {
        setSeed(seed);
    }
    
    // 種を与える
    public function setSeed( seed:uint ):void
    {
        a=seed=1812433253*(seed^(seed>>30))+0;
        b=seed=1812433253*(seed^(seed>>30))+1;
        c=seed=1812433253*(seed^(seed>>30))+2;
        d=seed=1812433253*(seed^(seed>>30))+3;
    }
    
    // 整数乱数を生成
    public function get32():uint
    {
        var t:uint = (a^(a<<11));
        a=b; b=c; c=d;
        return( d=(d^(d>>>19))^(t^(t>>>8)) );
    }
  
}