ぷよぷよ
GAME :: ぷよぷよ
[←][↓][→]: 移動
[Z]: 左回転
[X]: 右回転
[C]: 下まで一気に落下
できたらいいな、300連鎖♪
/**
* Copyright daniwell ( http://wonderfl.net/user/daniwell )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/gDzo
*/
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.ShaderParameter;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.ui.Keyboard;
[SWF(backgroundColor = "0x555555", frameRate = "30", width = "465", height = "465")]
public class Puyopuyo extends Sprite
{
private const NUM_W:uint = 40; // ヨコ 個数
private const NUM_H:uint = 40; // タテ 個数
private const COLOR1:Vector.<uint> = Vector.<uint>([0xffffff, 0xff3300, 0x33ee00, 0x3366ff, 0xffcc00]);
private const COLOR2:Vector.<uint> = Vector.<uint>([0xffffff, 0xff9966, 0x88ff55, 0x88aaff, 0xffee44]);
private const BLOCK :Array =
[
[[0, 2, 0],[0, 1, 0],[0, 0, 0]],
[[0, 0, 0],[0, 1, 2],[0, 0, 0]],
[[0, 0, 0],[0, 1, 0],[0, 2, 0]],
[[0, 0, 0],[2, 1, 0],[0, 0, 0]]
];
private var phase:int;
private var map :Vector.<Vector.<Puyo>>;
private var bmd:BitmapData;
private var bmp:Bitmap;
private var nowX:int = int((NUM_W - 3) / 2);
private var nowY:int = 0;
private var num :int = 0; // BLOCK 番号
private var maxH:Vector.<int> = new Vector.<int>(NUM_W);
private var tmpH:Vector.<int> = new Vector.<int>(NUM_W);
private var nowCols :Vector.<int> = Vector.<int>([rand(), rand()]);
private var nextCols:Vector.<int> = Vector.<int>([rand(), rand()]);
private var que :Vector.<Puyo> = new Vector.<Puyo>();
private var isChain :Boolean = false;
private var interval :int = 30;
private var intervalCt :int = 0;
private var time :int = 0;
private var chainCt :uint;
private var status :Status;
private var mapRect :Rectangle = new Rectangle(0, 0, NUM_W, NUM_H);
private var keyFlags :Array = new Array();
private var keyCounts :Array = new Array();
private var tf :TextField = new TextField();
public function Puyopuyo():void
{
graphics.beginFill(0x555555); graphics.drawRect(0,0,465,465);
bmd = new BitmapData(NUM_W + 5, NUM_H, false, 0x555555);
bmp = new Bitmap(bmd);
bmp.scaleX = bmp.scaleY = 10;
addChild(bmp);
// map 初期化
map = new Vector.<Vector.<Puyo>>(NUM_H);
for (var i :int = 0; i < NUM_H; i ++)
{
map[i] = new Vector.<Puyo>(NUM_W);
for (var j :int = 0; j < NUM_W; j ++)
{
var puyo :Puyo = new Puyo();
puyo.x = j;
puyo.y = i;
map[i][j] = puyo;
}
}
status = new Status();
status.addScore(0);
status.setChain(0);
addChild(status);
status.x = status.y = bmp.x = 10;
bmp.y = int(status.y + status.height + 5);
tf.defaultTextFormat = new TextFormat("_sans", 10, 0xffffff); tf.autoSize = "left"; tf.selectable = false;
tf.text = "CLICK TO START"; tf.x = (465 - tf.width) / 2; tf.y = (465 - tf.height) / 2;
addChild(tf);
stage.addEventListener(MouseEvent.CLICK, clickHandler);
}
private function clickHandler(e:MouseEvent = null):void
{
tf.visible = false;
bmd.fillRect(new Rectangle(NUM_W + 1, 0, 3, 4), 0xffffff);
bmd.setPixel(NUM_W + 2, 1, COLOR1[nextCols[1]]);
bmd.setPixel(NUM_W + 2, 2, COLOR1[nextCols[0]]);
// 配列初期化
for (var i :int = 0; i < NUM_H; i ++)
for (var j :int = 0; j < NUM_W; j ++)
map[i][j].type = 0;
for (i = 0; i < NUM_W; i ++) { maxH[i] = NUM_H - 1; tmpH[i] = -1; }
status.reset();
interval = 30;
phase = 1;
render();
stage.removeEventListener(MouseEvent.CLICK, clickHandler);
stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
}
/* ENTER FRAME */
private function enterFrameHandler(e:Event):void
{
if (phase == 1) loopPhase1(); /* phase1 (操作) */
else if(phase == 2) loopPhase2(); /* phase2 (探索, 落下) */
}
private function loopPhase1():void
{
intervalCt ++;
// 落下速度アップ(とりあえずなし)
// time ++; if (time % 1800 == 0) interval = (2 <= interval)? interval - 1:1;
var tmp:int = (nowY != 0)? interval:Math.max(NUM_W, interval);
if (keyFlags[37]) if (keyCounts[37]++ == 0 || 4 < keyCounts[37]) move(-1);
if (keyFlags[39]) if (keyCounts[39]++ == 0 || 4 < keyCounts[39]) move(1);
if (keyFlags[90]) if (keyCounts[90]++ == 0 || 4 < keyCounts[90]) rotate();
if (keyFlags[88]) if (keyCounts[88]++ == 0 || 4 < keyCounts[88]) rotate(true);
if (keyFlags[40]) move(1, true, true);
if (keyFlags[67]) while(phase==1) move(1, true, true);
if (tmp <= intervalCt) { move(1, true); intervalCt = 0; }
}
private function loopPhase2():void
{
intervalCt ++;
switch ( intervalCt )
{
case 3: fallPuyo(); break;
case 9: searchStep(); break;
case 18: fallStep(); break;
}
}
/* 移動 */
private function move ( n :int, isY :Boolean = false, isScore :Boolean = false ) :void
{
if ( ! isY ) /* x 方向 */
{
if ( ! check(BLOCK[num], nowX + n, nowY) ) nowX += n;
}
else /* y 方向 */
{
if (isScore) status.addScore(1);
if ( ! check(BLOCK[num], nowX, nowY + n) ) nowY += n;
else { intervalCt = 0; phase = 2; return; }
}
render();
}
/* 回転 */
private function rotate ( isRight :Boolean = false ) :void
{
var nn :int;
if (isRight) nn = (num + 1 < 4)? num + 1:0;
else nn = (0 <= num - 1)? num - 1:3;
var tmp :int = num; num = nn;
if ( check(BLOCK[nn], nowX, nowY) )
{
if ( nn == 2 ) nowY --;
else if ( nn == 1 || nn == 3 )
{
if ( ! check(BLOCK[nn], nowX + (nn - 2), nowY) ) nowX += (nn - 2);
else
{
num = - (tmp - 2);
if ( check(BLOCK[num], nowX, nowY) ) nowY --;
}
}
}
render();
}
/* ぷよ接地ステップ */
private function fallPuyo () :void
{
var px :int, py :int;
var ar :Array = BLOCK[num];
chainCt = 0;
for (var i :int = 2; 0 <= i; i --)
{
py = i + nowY;
for (var j :int = 0; j < 3; j ++)
{
px = j + nowX;
if ( 0 < ar[i][j] )
{
map[py][px].type = nowCols[ar[i][j] - 1];
fall( px, py );
if (tmpH[px] < maxH[px] + 1) tmpH[px] = maxH[px] + 1;
}
}
}
render();
}
/* 探索ステップ */
private function searchStep ():void
{
var i :int, j :int;
var tH :Vector.<int> = Vector.<int>(tmpH.concat()); // Vector コピー
for (i = 0; i < NUM_W; i ++) tmpH[i] = -1; // 初期化
for (i = 0; i < NUM_W; i ++)
{
if (tH[i] == -1) continue;
for (j = tH[i]; 0 <= j; j --) search(i, j);
}
render();
}
/* 落下ステップ */
private function fallStep ():void
{
var i :int, j :int;
if (isChain) // 1 箇所でも消えた
{
chainCt ++;
for (i = 0; i < NUM_W; i ++)
{
if (tmpH[i] == -1) continue;
for (j = tmpH[i]; 0 <= j; j --) fall(i, j);
}
isChain = false;
intervalCt = 4;
}
else
{
nowX = int((NUM_W - 3) / 2);
nowY = 0;
num = 0;
intervalCt = 0;
nowCols[0] = nextCols[0]; nextCols[0] = rand();
nowCols[1] = nextCols[1]; nextCols[1] = rand();
bmd.setPixel(NUM_W + 2, 1, COLOR1[nextCols[1]]);
bmd.setPixel(NUM_W + 2, 2, COLOR1[nextCols[0]]);
if (!check(BLOCK[num], nowX, nowY)) phase = 1;
else gameover();
}
render();
status.setChain(chainCt);
}
private function gameover ():void
{
stage.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
stage.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(MouseEvent.CLICK, clickHandler);
}
/* 落下 */
private function fall (nx:int, ny:int) :void
{
var nn :int = maxH[nx];
if (0 <= nn && 0 < map[ny][nx].type)
{
if (nn != ny)
{
map[nn][nx].type = map[ny][nx].type;
map[ny][nx].type = 0;
}
maxH[nx] = nn - 1;
}
}
private function render ( ) :void
{
bmd.lock();
bmd.fillRect(mapRect, COLOR1[0]);
for (var i :int = 0; i < NUM_H; i ++)
for (var j :int = 0; j < NUM_W; j ++)
bmd.setPixel(j, i, COLOR1[map[i][j].type]);
if (phase == 1)
{
var px:int, py:int;
var ar :Array = BLOCK[num];
for (i = 0; i < 3; i ++)
{
py = i + nowY;
for (j = 0; j < 3; j ++)
{
px = j + nowX;
if ( 0 < ar[i][j] )
{
var n:int = ((num == 0 && ar[i][j] == 2) || (num == 2 && ar[i][j] == 1))? 1:0;
bmd.setPixel(px, maxH[px] - n, COLOR2[nowCols[ar[i][j] - 1]]);
bmd.setPixel(px, py, COLOR1[nowCols[ar[i][j] - 1]]);
}
}
}
}
bmd.unlock();
}
/* 衝突チェック */
private function check(ar:Array, nx:int, ny:int):Boolean
{
var px :int, py :int;
for (var i :int = 0; i < 3; i ++)
{
py = i + ny;
for (var j :int = 0; j < 3; j ++)
{
px = j + nx;
if ( 0 < ar[i][j] )
{
if ((px < 0 || NUM_W <= px) || (py < 0 || NUM_H <= py)) return true; // 壁
if (0 < map[py][px].type) return true; // ぷよ
}
}
}
return false;
}
/* 探索開始 */
private function search(nx:int, ny:int):void
{
if (map[ny][nx].type == 0) return;
searchLoop(map[ny][nx].type, nx, ny); /* 再帰探索 */
if (4 <= que.length) // 4 連結以上
{
// score 加算
status.addScore( generateScore(que.length, chainCt) );
while (que.length != 0)
{
var p:Puyo = que.pop();
p.flag = false;
p.type = 0;
tmpH[p.x] = maxH[p.x] = Math.max(maxH[p.x], p.y);
}
isChain = true;
}
else while (que.length != 0) que.pop().flag = false;
}
/* 再起探索 */
private function searchLoop(n:int, nx:int, ny:int):void
{
// 探索済み or 不一致の場合
if (map[ny][nx].flag || n != map[ny][nx].type) return;
map[ny][nx].flag = true;
que.push(map[ny][nx]);
if (0 <= nx - 1) searchLoop(n, nx - 1, ny);
if (nx + 1 < NUM_W) searchLoop(n, nx + 1, ny);
if (0 <= ny - 1) searchLoop(n, nx, ny - 1);
if (ny + 1 < NUM_H) searchLoop(n, nx, ny + 1);
}
private function keyDownHandler(e:KeyboardEvent):void
{
if (phase != 1) return;
keyCounts[e.keyCode] = 0;
keyFlags[e.keyCode] = true;
}
private function keyUpHandler(e:KeyboardEvent):void
{
keyFlags[e.keyCode] = false;
}
private function generateScore(len:uint, chain:uint):uint
{
var k:uint;
if (chain == 0) k = 1;
else if (chain < 3) k = chain * 8;
else k = (chain - 2) * 32;
if (5 <= len) k += (len - 4) + 1;
return k * (len * 10);
}
private function rand(max:int = 4, min:int = 1):int { return int(Math.random() * (max - min + 1)) + min; }
}
}
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFormat;
class Status extends Sprite
{
private var _score :uint = 0;
private var _chain :uint = 0;
private var _tfMaxChain :TextField;
private var _tfScore :TextField;
public function Status()
{
_tfMaxChain = createTextField(); _tfMaxChain.y = 13;
_tfScore = createTextField();
addChild(_tfMaxChain);
addChild(_tfScore);
}
public function addScore(n:uint):void
{
_score += n;
_tfScore.text = "score: " + _score;
}
public function setChain(n:uint):void
{
if (n < _chain) return;
_chain = n;
_tfMaxChain.text = "chain: " + _chain;
}
public function reset():void
{
_score = 0; _tfScore.text = "score: " + _score;
_chain = 0; _tfMaxChain.text = "chain: " + _chain;
}
private function createTextField():TextField
{
var format :TextFormat = new TextFormat("_等幅", 10, 0xffffff);
format.letterSpacing = 1;
var tf :TextField = new TextField();
tf.autoSize = "left";
tf.defaultTextFormat = format;
return tf;
}
}
class Puyo
{
public var type:int = 0;
public var flag:Boolean = false;
public var x :int;
public var y :int;
}