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

3 x 3 Random Tetris

[←][↓][→]: 移動
[Z]: 左回転
[X]: 右回転
[C]: 下まで一気に落下
 
落ちてくるブロックのサイズが3×3(以内)ランダムのテトリス。
Get Adobe Flash player
by daniwell 11 Feb 2012
/**
 * Copyright daniwell ( http://wonderfl.net/user/daniwell )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/wIMn
 */

package  
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.text.TextFormat;
	
	[SWF(backgroundColor = "0xbbbbbb", frameRate = "30", width = "465", height = "465")]
	public class Tetris extends Sprite
	{
		/* サイズ */
		private const SIZE  :int = 3;
		/* ヨコ */
		private const NUM_W :int = 12;
		/* タテ */
		private const NUM_H :int = 24;
		
		private const SCALE :int = 16;
		
		private var field :Vector.<Vector.<int>> = new Vector.<Vector.<int>>(NUM_H);
		private var blocks:Vector.<Block> = new Vector.<Block>();
		
		private var keyFlags  :Array = [];
		private var keyCounts :Array = [];
		
		private var now  :int;
		private var nowX :int;
		private var nowY :int;
		private var nowBlock  :Block;
		private var nextBlock :Block;
		
		private var bmd :BitmapData;
		private var bmp :Bitmap;
		
		private var nextBmd :BitmapData;
		private var nextBmp :Bitmap;
		
		private var phase :int = 0;
		private var count :int = 0;
		private var score :int = 0;
		
		private var tf      :TextField;
		private var tfScore :TextField;
		
		public function Tetris() 
		{
			graphics.beginFill(0xbbbbbb); graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
			
			bmd = new BitmapData(NUM_W, NUM_H, false);
			bmp = new Bitmap(bmd);
			addChild(bmp);
			
			nextBmd = new BitmapData(SIZE + 2, SIZE + 2, false);
			nextBmp = new Bitmap(nextBmd);
			addChild(nextBmp);
			
			nextBmp.scaleX = nextBmp.scaleY = bmp.scaleX = bmp.scaleY = SCALE;
			
			bmp.x = int((stage.stageWidth - (nextBmp.width + bmp.width + SCALE)) / 2);
			nextBmp.x = bmp.x + bmp.width + SCALE;
			nextBmp.y = bmp.y = int((stage.stageWidth - (bmp.height)) / 2);
			
			for (var i :int = 0; i < NUM_H; i ++)
			{
				field[i] = new Vector.<int>(NUM_W);
				for (var j :int = 0; j < NUM_W; j++) field[i][j] = 0;
			}
			
			// "Score" TextField
			tfScore = addChild(new TextField()) as TextField;
			tfScore.defaultTextFormat = new TextFormat("_等幅", 11, 0xffffff);
			tfScore.autoSize = "left"; addScore(0);
			tfScore.x = nextBmp.x; tfScore.y = bmp.y + bmp.height - tfScore.height;
			
			// "Click To Start" TextField
			tf = addChild(new TextField()) as TextField;
			tf.defaultTextFormat = new TextFormat("_sans", 10, 0xaaaaaa); tf.autoSize = "left"; tf.selectable = false;
            tf.text = "CLICK TO START"; tf.x = bmp.x + (bmp.width - tf.width) / 2; tf.y = bmp.y + (bmp.height - tf.height) / 2;
            addChild(tf);
            
			stage.addEventListener(MouseEvent.CLICK, clickHandler);
		}
		
		/* ゲーム開始 */
		private function clickHandler(e:MouseEvent):void
		{
			tf.visible = false; stage.removeEventListener(MouseEvent.CLICK, clickHandler);
			
			nextBlock = new Block(SIZE); reset();
			
			stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
			stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
			stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
		}
		/* リセット & 初期化 */
		private function reset():void
		{
			now = count = 0;
			phase = 1; nowX  = (NUM_W - SIZE) / 2;
			
			nowBlock = nextBlock;
			nextBlock = new Block(SIZE);
			
			LABEL: for (var i :int = 0; i < SIZE; i ++) for (var j :int = 0; j < SIZE; j ++) if (0 < nowBlock.data[now][i][j]) break LABEL;
			nowY = - i;
			
			nextBmd.fillRect(nextBmd.rect, 0xffffff);
			renderBlock(nextBlock, nextBmd, 1, 1);
			render();
			
			// Game End
			if (hitCheck(nowBlock.data[now], nowX, nowY)) { stage.removeEventListener(Event.ENTER_FRAME, enterFrameHandler); tf.text = " - GAME END - "; tf.visible = true; }
		}
		/* X 軸移動 */
		private function moveX(n:int):void
		{
			if (! hitCheck(nowBlock.data[now], nowX + n, nowY)) nowX += n;
			render();
		}
		/* Y 軸移動 */
		private function moveY (score:int = 0):void
		{
			if (! hitCheck(nowBlock.data[now], nowX, nowY + 1))
			{
				addScore(score);
				
				nowY ++;
				render();
			}
			else
			{
				count = 0
				phase = 2;
				
				setBlock();
				search();
			}
		}
		/* 探索 */
		private function search ():void
		{
			for (var i :int = 0, ct :int = 0; i < NUM_H; i ++)
			{
				for (var j :int = 0; j < NUM_W; j++) if (field[i][j] == 0) break;
				if (j == NUM_W) { fall(i); ct ++ }
			}
			if (0 < ct) addScore(ct * ct * 100);
			render();
		}
		/* 落下 */
		private function fall(line:int):void
		{
			for (var i :int = line; 0 <= i; i --)
			{
				for (var j :int = 0; j < NUM_W; j++) 
				{
					if (i != 0)	field[i][j] = field[i-1][j];
					else        field[i][j] = 0;
				}
			}
		}
		
		/* 回転 */
		private function rotate(n:int):void
		{
			var nn :int = now + n;
			if (nn < 0) nn = 3; if (3 < nn) nn = 0;
			
			if (! hitCheck(nowBlock.data[nn], nowX, nowY)) now = nn;
			
			render();
		}
		/* ブロックの接地 */
		private function setBlock ():void
		{
			var px :int, py :int;
			
			for (var i :int = 0; i < SIZE; i ++)
			{
				py = i + nowY;
				for (var j :int = 0; j < SIZE; j++)
				{
					px = j + nowX;
					if (nowBlock.data[now][i][j] == 0 || px < 0 || py < 0 || NUM_W <= px || NUM_H <= py) continue;
					field[py][px] = 1;
				}
			}
		}
		
		/* 衝突判定 */
		private function hitCheck (data:Array, nX:int, nY:int):Boolean
		{
			var px :int, py :int;
			
			for (var i :int = 0; i < SIZE; i ++)
			{
				py = i + nY;
				for (var j :int = 0; j < SIZE; j++)
				{
					px = j + nX;
					if (data[i][j] == 0) continue;
					
					if (px < 0 || NUM_W <= px || NUM_H <= py) return true;
					if (0 <= py && 0 < field[py][px]) return true;
				}
			}
			return false;
		}
		/* レンダリング */
		private function render():void
		{
			bmd.lock();
			bmd.fillRect(bmd.rect, 0xffffff);
			
			for (var i :int = 0; i < NUM_H; i ++)
				for (var j :int = 0; j < NUM_W; j++) 
					if (0 < field[i][j]) bmd.setPixel(j, i, 0);
			
			if (phase == 1) renderBlock(nowBlock, bmd, nowX, nowY);
			
			bmd.unlock();
		}
		/* 操作中のブロックのレンダリング */
		private function renderBlock (bl:Block, bd:BitmapData, ox:int, oy:int) :void
		{
			for (var i :int = 0; i < SIZE; i ++)
				for (var j :int = 0; j < SIZE; j++)
					if (0 < bl.data[now][i][j]) bd.setPixel(j + ox, i + oy, 0);
		}
		/* スコア加算 */
		private function addScore (value :int) :void
		{
			score += value;
			tfScore.text = "SCORE: " + score;
		}
		
		private function enterFrameHandler (e:Event):void
		{
			count ++;
			
			if (phase == 1)
			{
				if (30 <= count)
				{
					moveY();
					count = 0;
				}
				
				if (keyFlags[37]) if (keyCounts[37]++ == 0 || 4 < keyCounts[37]) moveX(-1);
				if (keyFlags[39]) if (keyCounts[39]++ == 0 || 4 < keyCounts[39]) moveX(1);
				if (keyFlags[90]) if (keyCounts[90]++ == 0 || 4 < keyCounts[90]) rotate(-1);
				if (keyFlags[88]) if (keyCounts[88]++ == 0 || 4 < keyCounts[88]) rotate(1);
				if (keyFlags[40]) moveY(1);
				if (keyFlags[67]) while (phase == 1) moveY(1);
			}
			else if (phase == 2)
			{
				if (6 <= count) reset();
			}
		}
		
		private function keyDownHandler (e:KeyboardEvent):void
		{
			keyCounts[e.keyCode] = 0;
			keyFlags[e.keyCode] = true;
		}
		private function keyUpHandler (e:KeyboardEvent):void
		{
			keyFlags[e.keyCode] = false;
		}
	}
}

class Block
{
	private var _data :Array;
	
	public function Block(size:int)
	{
		_data = [];
		_data[k] = BlockUtil.getBlockData(size);
		for (var k :int = 1; k < 4; k ++)
		{
			_data[k] = [];
			for (var i :int = 0; i < size; i ++)
			{
				_data[k][i] = [];
				for (var j :int = 0; j < size; j++) _data[k][i][j] = _data[k-1][size-j-1][i];
			}
		}
	}
	public function get data( ) :Array { return _data; }
}

class BlockUtil
{
	public static function getBlockData (size:int) :Array
	{
		var d :Array = [];
		for (var i :int = 0; i < size; i ++)
		{
			d[i] = [];
			for (var j :int = 0; j < size; j++) d[i][j] = 0;
		}
		_update(d, size, [int(Math.random()*size), int(Math.random()*size)], [int(Math.random()*size), int(Math.random()*size)]);
		return d;
	}
	private static function _update (d:Array, size:int, p1:Array , p2:Array) :void
	{
		d[p1[1]][p1[0]] = 1;
		d[p2[1]][p2[0]] = 2;
		
		if (p1[0] == p2[0] && p1[1] == p2[1]) return;
		
		_getp(size, p1, int(Math.random() * 4));
		_getp(size, p2, int(Math.random() * 4));
		
		if (d[p1[1]][p1[0]] == 2) return;
		if (d[p2[1]][p2[0]] == 1) return;
		
		_update(d, size, p1, p2);
	}
	private static function _getp (size:int, p:Array, n:int) :void
	{
		switch ( n % 4 ) {
		case 0: p[0] ++; if (size <= p[0]) { p[0] --; _getp(size, p, n + 1); } break;
		case 1: p[1] ++; if (size <= p[1]) { p[1] --; _getp(size, p, n + 1); } break;
		case 2: p[0] --; if (p[0]  <  0)   { p[0] ++; _getp(size, p, n + 1); } break;
		case 3: p[1] --; if (p[1]  <  0)   { p[1] ++; _getp(size, p, n + 1); } break;
		}
	}
}