3 x 3 Random Tetris
[←][↓][→]: 移動
[Z]: 左回転
[X]: 右回転
[C]: 下まで一気に落下
落ちてくるブロックのサイズが3×3(以内)ランダムのテトリス。
/**
* 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;
}
}
}