Garbage TETRISをズルしてみた
ごみテトリスはTD(0)の犠牲になったのだ・・
前からつくってたテトリス強化学習を使用。
素性は最大高、穴の数、1x(>=2)の溝の数, 1x(>=3)の溝の数, 1x(>=4)の溝の数。
ゴミが散らばるところのウェイトを110/30→15/30秒に
/**
* Copyright uwi ( http://wonderfl.net/user/uwi )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/A4x3
*/
/*
ごみテトリスはTD(0)の犠牲になったのだ・・
前からつくってたテトリス強化学習を使用。
素性は最大高、穴の数、1x(>=2)の溝の数, 1x(>=3)の溝の数, 1x(>=4)の溝の数。
ゴミが散らばるところのウェイトを110/30→15/30秒に
*/
package
{
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import com.bit101.components.*;
import net.wonderfl.score.basic.*;
import flash.text.*;
[SWF(width="465", height="465", backgroundColor="0xFFFFFF", frameRate="60")]
public class Tetris extends Sprite
{
private var label:Label;
private var btn_start:PushButton;
private var btn_ranking:PushButton;
private var btn_clean:PushButton;
private var play:Boolean = false;
private var form_score:BasicScoreForm;
private var form_ranking:BasicScoreRecordViewer;
private var buffer:BitmapData = new BitmapData(465, 465, false, 0x000000);
private var screen:Bitmap = new Bitmap(buffer);
private var gamedata:GameData;
private var field:Field;
public var _tf : TextField;
function Tetris()
{
_tf = new TextField();
_tf.height = 465;
Key.setListener(this);
KeyWatcher.watch("LEFT", 37, 100, 65);
KeyWatcher.watch("RIGHT", 39, 102, 68);
KeyWatcher.watch("UP", 38, 104, 87);
KeyWatcher.watch("DOWN", 40, 98, 83);
KeyWatcher.watch("ROT_L", 90, 75);
KeyWatcher.watch("ROT_R", 16, 88, 74);
func_key = new KeyWatcher();
addChild(screen);
Style.LABEL_TEXT = 0x000000;
label = new Label(this, 0, 0);
message("Garbage TETRIS");
btn_start = new PushButton(this, 182, 300, "START", startGame);
btn_ranking = new PushButton(this, 182, 330, "RANKING", showRanking);
btn_clean = new PushButton(this, 182, 380, "CLEAN UP!", clean);
btn_clean.visible = false;
gamedata = new GameData();
field = new Field(gamedata, this);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
// addChild(_tf);
}
public function message(text:String):void
{
if(label != null) {this.removeChild(label);}
label = new Label(this, 0, 0, text);
label.x = 465/2 - label.width*3;
label.y = 465/2 - label.height*3 - 50;
label.scaleX = label.scaleY = 6;
label.blendMode = BlendMode.INVERT;
}
public function startGame(e:Event):void
{
label.visible = false;
btn_start.visible = false;
btn_ranking.visible = false;
btn_clean.visible = false;
play = true;
field.init();
return;
}
public function entryRanking():void
{
label.visible = false;
form_score = new BasicScoreForm(this, (465-280)/2, (465-160)/2, gamedata.score, "ENTRY", onCloseScoreForm);
return;
}
private function onCloseScoreForm(succeeded:Boolean):void
{
if (form_score != null) {removeChild(form_score);}
form_ranking = new BasicScoreRecordViewer(this, (465-220)/2, (465-240)/2, "RANKING", 30, true, onCloseRankingForm);
return;
}
private function showRanking(e:Event):void
{
label.visible = false;
btn_start.visible = false;
btn_ranking.visible = false;
btn_clean.visible = false;
form_ranking = new BasicScoreRecordViewer(this, (465-220)/2, (465-240)/2, "RANKING", 30, true, onCloseRankingForm);
return;
}
private function onCloseRankingForm():void
{
if (form_ranking != null) {removeChild(form_ranking);}
btn_start.visible = true;
btn_ranking.visible = true;
if(play == true) {btn_clean.visible = true;}
message("Garbage TETRIS");
return;
}
private function clean(e:Event):void
{
field.garbage.fillRect(field.garbage.rect, 0x00000000);
return;
}
public function onEnterFrame(e:Event):void
{
func_key.update();
field.main();
draw();
return;
}
public function draw():void
{
field.draw(buffer);
return;
}
}
}
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import frocessing.color.FColor;
var func_key:KeyWatcher;
class GameData
{
public var score:int = 0;
}
class Field
{
public static const WIDTH:int = 10;
public static const INVISIBLE_HEIGHT:int = 1;
public static const HEIGHT:int = 20+INVISIBLE_HEIGHT;
public static const G:int = 0x10000;
private var gamedata:GameData;
private var root:Tetris;
public var map:Vector.<Vector.<int>>;
private var minopool:ActorPool = new ActorPool();
private var effectpool:ActorPool = new ActorPool();
private var particlepool:ActorPool = new ActorPool();
public var garbage:BitmapData = new BitmapData(465, 465, true, 0x00000000);
public var x:int = Math.floor((465/2) - (20*WIDTH)/2);
public var y:int = Math.floor((465/2) - (20*HEIGHT)/2) - (10*INVISIBLE_HEIGHT);
private var tetromino:Tetromino;
private var ghost:Tetromino;
private var next:Tetromino;
private var table:Array;
private var drop_speed:int;
private var lockdown_length:int;
private var lockdown_time:int;
private var halflock:Boolean;
private var action:Function;
private var count:int;
private var _cb:CelestialBeing;
function Field(gamedata:GameData, root:Tetris)
{
this.gamedata = gamedata;
this.root = root;
changeAction("act_blank");
_cb = new CelestialBeing();
_cb._tf = root._tf;
return;
}
private function changeAction(name:String):void
{
action = this[name];
count = -1;
return;
}
public function init():void
{
map = new Vector.<Vector.<int>>(HEIGHT);
for(var y:int = 0; y < HEIGHT; y++) {
map[y] = new Vector.<int>(WIDTH);
for(var x:int = 0; x < WIDTH; x++) {
map[y][x] = -1;
}
}
do {
table = MathEx.shuffle([0, 1, 2, 3, 4, 5, 6]);
}while(
table[0] == MinoPattern.S || table[0] == MinoPattern.Z
|| table[1] == MinoPattern.S || table[1] == MinoPattern.Z)
drop_speed = G / 30;
lockdown_length = 60;
gamedata.score = 0;
changeAction("act_ready");
return;
}
public function main():void
{
count++;
action();
minopool.main();
effectpool.main();
particlepool.main();
return;
}
// --------------------------------//
// 状態
// --------------------------------//
private function act_blank():void
{
return;
}
private function act_ready():void
{
if(count == 0) {
root.message("Ready");
}
if(count == 60)
{
root.message("");
changeAction("act_move");
}
return;
}
private function act_move():void
{
if(count == 0)
{
shiftMino();
// ゲームオーバー判定
if(tetromino.hitTest() == true)
{
tetromino.lock();
tetromino = null;
next = null;
changeAction("act_gameover");
return;
}
}
// 回転
if(func_key.isPress("ROT_L")) {tetromino.rotate(1);}
else if(func_key.isPress("ROT_R")) {tetromino.rotate(-1);}
// 横移動
if(func_key.isPress("LEFT", true)) {tetromino.slide(-1);}
else if(func_key.isPress("RIGHT", true)) {tetromino.slide(1);}
if(func_key.isPress("UP") == true)
{
// ハードドロップ
tetromino.drop(G*20);
lockdown_time = lockdown_length;
}
else if(tetromino.isLanding() == false)
{
// ソフトドロップ
if(func_key.isDown("DOWN") && drop_speed < G/2) {tetromino.drop(G/2);}
else {tetromino.drop(drop_speed);}
lockdown_time = 0;
halflock = false;
}
_cb.interpose(root, tetromino.x, tetromino.rot);
// 接地
if(tetromino.isLanding() == true)
{
lockdown_time++;
if(lockdown_time >= lockdown_length || func_key.isPress("DOWN"))
{
changeAction("act_lockdown");
}
else if(func_key.isDown("DOWN"))
{
if(halflock == false)
{
halflock = true;
lockdown_time = lockdown_length-7;
}
}
}
// ゴースト処理
ghost = tetromino.clone();
ghost.ghost = true;
ghost.drop(G*20);
return;
}
private function act_lockdown():void
{
if(count == 0)
{
tetromino.lock();
tetromino = null;
ghost = null;
// ライン判定
for(var i:int = 0; i < HEIGHT; i++)
{
if(checkLine(i) == true)
{
changeAction("act_erase");
return;
}
}
}
if(count == 15)
{
changeAction("act_move");
return;
}
}
private function act_erase():void
{
var i:int;
if(count == 0)
{
for(i = 0; i < HEIGHT; i++) {
if(checkLine(i) == true) {
for each(var mino:Mino in minopool.list) {
if (mino.py == i) {
mino.changeAction("act_break");
}
}
gamedata.score++;
}
}
}
if(count == 8)
{
for(i = 0; i < HEIGHT; i++) {
if(checkLine(i) == true) {
eraseLine(i);
}
}
}
if(count == 15)
{
changeAction("act_move");
if(gamedata.score >= 50) {
drop_speed += G/100;
}
}
}
private function act_gameover():void
{
if(count == 0)
{
root.message("GAME OVER");
for(var i:int = INVISIBLE_HEIGHT; i < HEIGHT; i++)
{
for(var j:int = 0; j < WIDTH; j++)
{
if(map[i][j] != -1) {
map[i][j] = 7;
}
}
for each(var mino:Mino in minopool.list) {
mino.changeAction("act_gameover");
}
}
}
if(count == 350) {
root.entryRanking();
}
}
// --------------------------------//
// フィールド操作
// --------------------------------//
public function shiftMino():void
{
if(next == null)
{
tetromino = new Tetromino(this);
tetromino.x = 3;
tetromino.y = 0;
tetromino.type = table.shift();
tetromino.rot = 0;
}
else {
tetromino = next;
tetromino.ghost = false;
}
next = new Tetromino(this);
next.x = 3;
next.y = 0;
next.type = table.shift();
next.ghost = true;
next.rot = 0;
_cb.algo(map, tetromino.type, next.type);
if(func_key.isDown("ROT_L") && tetromino.hitTest(0, 0, 1) == false) {tetromino.rotate(1);}
else if(func_key.isDown("ROT_R") && tetromino.hitTest(0, 0, -1) == false) {tetromino.rotate(-1);}
if(table.length == 0) {
table = MathEx.shuffle([0, 1, 2, 3, 4, 5, 6]);
}
lockdown_time = 0;
halflock = false;
return;
}
public function writeMino(x:int, y:int, type:int):void
{
map[y][x] = type;
minopool.add(new Mino(this, x, y, type));
}
public function eraseLine(h:int):void
{
for(var y:int = h; y >= 1; y--) {
map[y] = map[y-1];
}
map[0] = new Vector.<int>(WIDTH);
for(var x:int = 0; x < WIDTH; x++) {
map[0][x] = -1;
}
for each(var mino:Mino in minopool.list) {
if(mino.py < h) {
mino.py++;
mino.y += 20;
}
}
return;
}
// --------------------------------//
// 生成
// --------------------------------//
public function addBreakingEffect(px:int, py:int, type:int):void
{
effectpool.add(new BreakingMino(this, x+px*20+10, y+py*20+10, type));
for(var i:int = 0; i < 5; i++) {
particlepool.add(new Particle(x+px*20+10, y+py*20+10));
}
}
public function addParticle(x:Number, y:Number):void
{
particlepool.add(new Particle(x, y));
}
// --------------------------------//
// 判定
// --------------------------------//
public function hitTest(x:int, y:int):Boolean
{
if(x < 0 || x >= WIDTH || y >= HEIGHT) {return true;}
if(y < 0) {return false;}
return (map[y][x] >= 0 && map[y][x] <= 6);
}
public function checkLine(h:int):Boolean
{
for(var i:int = 0; i < WIDTH; i++) {
if(hitTest(i, h) == false) {return false;}
}
return true;
}
// --------------------------------//
// 描画
// --------------------------------//
public function draw(buffer:BitmapData):void
{
buffer.colorTransform(buffer.rect, new ColorTransform(1, 1, 1, 1, 24, 32, 48, 0));
buffer.fillRect(new Rectangle(x, y+INVISIBLE_HEIGHT*20, WIDTH*20, (HEIGHT-INVISIBLE_HEIGHT)*20), 0x404040);
for(var i:int = INVISIBLE_HEIGHT; i < HEIGHT; i++) {
for(var j:int = 0; j < WIDTH; j++) {
buffer.fillRect(new Rectangle(x+j*20, y+i*20, 19, 19), 0x000000);
}
}
minopool.draw(buffer);
if(next != null) {next.draw(buffer)};
if(tetromino != null) {tetromino.draw(buffer);}
buffer.copyPixels(garbage, garbage.rect, new Point(0, 0));
particlepool.draw(buffer);
effectpool.draw(buffer);
if(ghost != null) {ghost.draw(buffer);}
return;
}
}
class Tetromino
{
private var field:Field;
public var x:int;
public var y:int;
public var type:int;
public var ghost:Boolean = false;
private var _rot:int;
public function get rot():int {return _rot;}
public function set rot(value:int):void
{
_rot = value;
if(_rot < 0) {_rot = MinoPattern.TABLE[type].length-1;}
if(_rot >= MinoPattern.TABLE[type].length) {_rot = 0;}
}
function Tetromino(field:Field)
{
this.field = field;
}
public function clone():Tetromino
{
var temp:Tetromino = new Tetromino(field);
temp.x = x;
temp.y = y;
temp.type = type;
temp.rot = rot;
return temp;
}
// --------------------------------//
// 移動・回転
// --------------------------------//
public function slide(vx:int):void
{
if(hitTest(vx) == true) {return;}
x += vx;
return;
}
public function drop(vy:int):void
{
if(isLanding() == true) {return;}
while(vy >= 0)
{
y += Math.min(vy, 0x10000);
vy -= 0x10000;
if(isLanding() == true) {
y = (y >> 16) << 16;
break;
}
}
}
public function rotate(r:int):void
{
if(hitTest(0, 0, r) == false)
{
rot += r;
return;
}
// Iは補正しない
if(type == MinoPattern.I) {return;}
// JLTは中央列以外にブロックが存在しない場合は補正しない
if(type == MinoPattern.J || type == MinoPattern.L || type == MinoPattern.T)
{
var side:Boolean = false;
loop : for(var i:int = 0; i < 3; i++) {
for(var j:int = 0; j < 3; j+=2) {
if(field.hitTest(x+j, ((y+0x8000)>>16)+i) == true) {side = true; break loop;}
if(field.hitTest(x+j, ((y+0xFFFF)>>16)+i) == true) {side = true; break loop;}
}
}
if(side == false) {
return;
}
}
// 回転補正
if(hitTest(1, 0, r) == false)
{
rot += r;
x++;
return;
}
if(hitTest(-1, 0, r) == false)
{
rot += r;
x--;
return;
}
return;
}
// --------------------------------//
// 当たり判定
// --------------------------------//
public function hitTest(offset_x:int = 0, offset_y:int = 0, offset_rot:int = 0):Boolean
{
var temp:Tetromino = this.clone();
temp.x += offset_x;
temp.y += offset_y;
temp.rot += offset_rot;
var pattern:Array = MinoPattern.TABLE[temp.type][temp.rot];
for(var i:int = 0; i < pattern.length; i++) {
for(var j:int = 0; j < pattern[i].length; j++) {
if(pattern[i][j] != 0) {
if(field.hitTest(temp.x+j, ((temp.y+0x8000)>>16)+i) == true) {return true;}
if(field.hitTest(temp.x+j, ((temp.y+0xFFFF)>>16)+i) == true) {return true;}
}
}
}
return false;
}
public function isLanding():Boolean
{
var temp:Tetromino = this.clone();
var pattern:Array = MinoPattern.TABLE[temp.type][temp.rot];
for(var i:int = 0; i < pattern.length; i++) {
for(var j:int = 0; j < pattern[i].length; j++) {
if(pattern[i][j] != 0) {
if(field.hitTest(temp.x+j, (temp.y>>16)+(i+1)) == true) {return true;}
}
}
}
return false;
}
public function lock():void
{
var pattern:Array = MinoPattern.TABLE[type][rot];
for(var i:int = 0; i < pattern.length; i++) {
for(var j:int = 0; j < pattern[i].length; j++) {
if(pattern[i][j] != 0) {
field.writeMino(x+j, (y>>16)+i, type);
}
}
}
return;
}
// --------------------------------//
// 描画
// --------------------------------//
public function draw(buffer:BitmapData):void
{
var i:int, j:int;
var pattern:Array = MinoPattern.TABLE[type][rot];
if(ghost == false)
{
for(i = 0; i < pattern.length; i++) {
for(j = 0; j < pattern[i].length; j++) {
if(pattern[i][j] != 0) {
buffer.fillRect(
new Rectangle(
field.x + (x+j)*20 - 1, field.y + (y/0x10000+i)*20 - 1,
21, 21
),
FColor.HSVtoValue(
FColor.hue(MinoPattern.COLOR[type]),
FColor.saturation(MinoPattern.COLOR[type]),
0.5
)
);
buffer.fillRect(
new Rectangle(
field.x + (x+j)*20, field.y + (y/0x10000+i)*20,
19, 19
),
MinoPattern.COLOR[type]
);
}
}
}
}
else {
i = -1;
do {
j = -1;
do {
var pc:Boolean, py:Boolean, px:Boolean;
pc = pattern[i] == undefined || pattern[i][j] == undefined || pattern[i][j] == 0;
py = pattern[i+1] == undefined || pattern[i+1][j] == undefined || pattern[i+1][j] == 0;
px = pattern[i] == undefined || pattern[i][j+1] == undefined || pattern[i][j+1] == 0;
if(pc != py)
{
buffer.fillRect(
new Rectangle(
field.x + (x+j)*20, field.y + (y/0x10000+i+1)*20-1,
20, 2
),
MinoPattern.COLOR[type]
);
}
if(pc != px)
{
buffer.fillRect(
new Rectangle(
field.x + (x+j+1)*20-1, field.y + (y/0x10000+i)*20,
2, 20
),
MinoPattern.COLOR[type]
);
}
j++;
} while(j < pattern[0].length)
i++;
} while(i < pattern.length)
}
return;
}
}
class Actor
{
protected var count:int = -1;
public var deleteflag:Boolean = false;
public var x:Number = 0;
public var y:Number = 0;
public var visible:Boolean = true;
public var action:String = "act_blank";
public function main():void
{
count++;
this[action]();
}
public function act_blank():void {}
public function draw(buffer:BitmapData):void {}
public function changeAction(name:String):void
{
action = name;
count = -1;
}
public function vanish():void
{
deleteflag = true;
}
}
class ActorPool
{
public var list:Array;
function ActorPool()
{
list = new Array();
}
public function get length():uint {return list.length;}
public function add(actor:Actor):Actor
{
list.push(actor);
return actor;
}
public function main():void
{
for(var i:int = 0; i < list.length; i++)
{
list[i].main();
if(list[i].deleteflag == true)
{
list.splice(i, 1);
i--;
}
}
}
public function draw(bmp:BitmapData):void
{
for(var i:int = 0; i < list.length; i++)
{
if(list[i].visible == false) {continue;}
list[i].draw(bmp);
}
}
}
class Mino extends Actor
{
private var field:Field;
public var px:int;
public var py:int;
public var type:int;
public var color:uint;
function Mino(field:Field, px:int, py:int, type:int):void
{
this.px = px;
this.py = py;
this.type = type;
this.field = field;
x = px*20;
y = py*20;
changeAction("act_flash");
}
public function act_flash():void
{
if(count < 2) {
color = 0xFFFFFF;
}
else if(count < 4) {
color = 0x000000;
}
else if(count < 150) {
color = FColor.RGBtoValue(
(0xFF + FColor.red(MinoPattern.COLOR[type]) * (count-4)) / (count-3),
(0xFF + FColor.green(MinoPattern.COLOR[type]) * (count-4)) / (count-3),
(0xFF + FColor.blue(MinoPattern.COLOR[type]) * (count-4)) / (count-3)
);
}
else {
color = MinoPattern.COLOR[type];
}
}
public function act_break():void
{
if(count % 6 < 3) {
color = 0xFFFFFF;
}
else {
color = MinoPattern.COLOR[type];
}
if(count == 12+Math.abs((px - Field.WIDTH/2) * 3)) {
field.addBreakingEffect(px, py, type);
vanish();
}
}
public function act_gameover():void
{
if(count == 0) {
color = MinoPattern.COLOR[type];
}
if(21 - Math.floor(count/3) == py) {
color = 0xA0A0A0;
}
if(count == 200) {
changeAction("act_break");
}
}
override public function draw(buffer:BitmapData):void
{
buffer.fillRect(new Rectangle(field.x + x, field.y + y, 19, 19), color+0xFF000000);
}
}
class BreakingMino extends Actor
{
private var field:Field;
private var shape:Shape;
private var matrix:Matrix = new Matrix();
private var vx:Number;
private var vy:Number;
private var bound:int;
private var angle:Number;
private var rot:Number;
private var h:Number;
function BreakingMino(field:Field, x:Number, y:Number, type:int)
{
this.field = field;
this.x = x;
this.y = y;
vx = Math.random() * 10 - 5;
vy = Math.random() * -6 - 6;
angle = 0;
rot = Math.random() * 20 - 10;
h = y - Math.random() * 20;
shape = new Shape();
shape.graphics.beginFill(MinoPattern.COLOR[type]);
shape.graphics.drawRect(-9, -9, 18, 18);
changeAction("act_main");
}
public function act_main():void
{
if(count <= 15 && count % 5 == 0) {
field.addParticle(x, y);
}
x += vx;
y += vy;
angle += rot;
if(x < 0) {
x = 0;
vx = -vx;
vx /= 1.5;
}
if(x > 465) {
x = 465
vx = -vx;
vx /= 1.5;
}
if(field.garbage.getPixel32(x, y+5) != 0 || y > 465)
{
if(bound > 10)
{
draw(field.garbage);
vanish();
return;
}
else if(vy > 0 && y > h)
{
vx /= 1.2;
vy /= -1.5;
rot = Math.random() * 20 - 10;
bound++;
}
}
vy += 0.5;
}
override public function draw(buffer:BitmapData):void
{
matrix.identity();
matrix.rotate(angle);
matrix.translate(x, y);
buffer.draw(shape, matrix);
}
}
class Particle extends Actor
{
public var vx:Number;
public var vy:Number;
public var size:Number;
function Particle(x:Number, y:Number)
{
this.x = x;
this.y = y;
vx = Math.random() * 10 - 5;
vy = Math.random() * 6 - 6;
size = 30;
changeAction("act_main");
}
public function act_main():void
{
x += vx;
y += vy;
vx /= 1.1;
vy += 0.3;
size /= 1.1;
if(size < 1) {
vanish();
}
}
override public function draw(buffer:BitmapData):void
{
buffer.fillRect(new Rectangle(x-size/2, y-size/2, size, size), 0xFFFFFFFF);
}
}
class MinoPattern
{
public static const I:int = 0, O:int = 1, S:int = 2, Z:int = 3, J:int = 4, L:int = 5, T:int = 6;
public static const COLOR:Array = [0xFF0040, 0xE8FF00, 0xFF40FF, 0x20F000, 0x8040FF, 0xFF8000, 0x00E0FF];
public static const TABLE:Array = new Array();
TABLE[I] = [[
[0, 0, 0, 0],
[1, 1, 1, 1],
[0, 0, 0, 0],
[0, 0, 0, 0]
], [
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0]
]];
TABLE[O] = [[
[0, 0, 0],
[0, 1, 1],
[0, 1, 1]
]];
TABLE[S] = [[
[0, 0, 0],
[0, 1, 1],
[1, 1, 0]
],[
[1, 0, 0],
[1, 1, 0],
[0, 1, 0]
]];
TABLE[Z] = [[
[0, 0, 0],
[1, 1, 0],
[0, 1, 1]
],[
[0, 0, 1],
[0, 1, 1],
[0, 1, 0]
]];
TABLE[J] = [[
[0, 0, 0],
[1, 1, 1],
[0, 0, 1]
],[
[0, 1, 0],
[0, 1, 0],
[1, 1, 0]
],[
[0, 0, 0],
[1, 0, 0],
[1, 1, 1]
],[
[0, 1, 1],
[0, 1, 0],
[0, 1, 0]
]];
TABLE[L] = [[
[0, 0, 0],
[1, 1, 1],
[1, 0, 0]
],[
[1, 1, 0],
[0, 1, 0],
[0, 1, 0]
],[
[0, 0, 0],
[0, 0, 1],
[1, 1, 1]
],[
[0, 1, 0],
[0, 1, 0],
[0, 1, 1]
]];
TABLE[T] = [[
[0, 0, 0],
[1, 1, 1],
[0, 1, 0]
],[
[0, 1, 0],
[1, 1, 0],
[0, 1, 0]
],[
[0, 0, 0],
[0, 1, 0],
[1, 1, 1]
],[
[0, 1, 0],
[0, 1, 1],
[0, 1, 0]
]];
}
class Key
{
private static var down:Array = new Array(256);
public static function setListener(target:InteractiveObject):void
{
target.stage.focus = target;
target.addEventListener(KeyboardEvent.KEY_DOWN, function (event:KeyboardEvent):void {down[event.keyCode] = true;});
target.addEventListener(KeyboardEvent.KEY_UP, function (event:KeyboardEvent):void {down[event.keyCode] = false;});
target.addEventListener(FocusEvent.FOCUS_OUT, onFocusOut);
}
private static function onFocusOut(event:FocusEvent):void
{
event.currentTarget.stage.focus = event.currentTarget;
for(var i:String in down) {down[i] = false;}
}
public static function isDown(keycode:int):Boolean
{
return down[keycode];
}
}
class KeyWatcher
{
private static var watchlist:Object = new Object();
private static var repeat_wait:uint = 12;
private static var repeat_rate:uint = 2;
private var keylist:Object = new Object();
public var lock:Boolean = false;
public function KeyWatcher():void
{
for(var name:String in watchlist)
{
keylist[name] = new Object();
keylist[name].down = false;
keylist[name].press = false;
keylist[name].release = false;
keylist[name].repeat = false;
keylist[name].repeatcount = 0;
}
return;
}
public static function watch(name:String, ...keycodes):void {watchlist[name] = keycodes;}
public static function unwatch(name:String):void {delete watchlist[name];}
public static function unwatchAll():void {watchlist = new Object();}
public function isDown(name:String):Boolean
{
if(keylist[name] is Object == false) {return false;}
return keylist[name].down;
}
public function isPress(name:String, getrepeat:Boolean = false):Boolean
{
if(keylist[name] is Object == false) {return false;}
if(getrepeat == false) {return keylist[name].press;}
else {return keylist[name].press || keylist[name].repeat;}
}
public function isRelease(name:String):Boolean
{
if(keylist[name] is Object == false) {return false;}
return keylist[name].release;
}
public function update():void
{
var name:String, keycode:int;
var input:Object = new Object();
if(lock == false) {
for(name in watchlist) {
for each(keycode in watchlist[name]) {
if(Key.isDown(keycode) == true) {input[name] = true; break;}
}
}
}
else {
for each(keycode in watchlist[name]) {input[name] = false;}
}
for(name in keylist) {
var key:Object = keylist[name];
if(input[name] == true) {
if(key.down == false) {
key.press = true;
key.repeat = true;
}
else {
key.press = false;
key.repeat = false;
}
key.down = true;
key.release = false;
key.repeatcount++;
if(key.repeatcount == repeat_wait) {
key.repeatcount -= repeat_rate;
key.repeat = true;
}
}
else {
if(key.down == true) {
key.release = true;
}
else {
key.release = false;
}
key.down = false;
key.press = false;
key.repeatcount = 0;
key.repeat = false;
}
}
return;
}
}
class MathEx
{
public static function shuffle(array:Array):Array
{
for(var j:int, t:Number, i:int = array.length, a:Array = array.slice();i; j = Math.random() * i, t = a[--i], a[i] = a[j], a[j] = t){}
return a;
}
}
import flash.text.TextField;
import flash.ui.*;
import flash.events.*;
// 俺がガンダムだ!
// MinoPatternにのみ依存
class CelestialBeing
{
public var _targX : int = -99;
public var _targRot : int = -1;
public var _tf : TextField;
// mapにcurTypeのタイプのミノ、続いてnextTypeのタイプのミノが落ちてくるときの最適なx座標と回転を_targX, _targRotに格納する
public function algo(map : Vector.<Vector.<int>>, curType : int, nextType : int) : void
{
var max : Number = -100000000;
var maxX : int = -99;
var maxRot : int = -1;
for(var rot : uint = 0;rot < MinoPattern.TABLE[curType].length;rot++){
for(var mx : int = -1;mx < 10;mx++){
if(isValid(map, curType, rot, mx)){
var v : Number = nextnextV(map, curType, nextType, rot, mx);
if(v >= max){
max = v;
maxX = mx;
maxRot = rot;
}
}
}
}
_targX = maxX;
_targRot = maxRot;
}
// mapにkindのタイプのミノをx座標mx, 回転rotで落とせるかどうか。
// checkYの簡易版
public function isValid(map : Vector.<Vector.<int>>, kind : int, rot : int, mx : int) : Boolean
{
var mino : Array = MinoPattern.TABLE[kind][rot];
for(var y : uint = 0;y < mino.length;y++){
for(var x : uint = 0;x < mino[y].length;x++){
if(mino[y][x] > 0){
if(x + mx >= 10 || x + mx < 0 || map[y][x + mx] >= 0){
return false;
}
}
}
}
return true;
}
// mapのディープコピー
private function clone(map : Vector.<Vector.<int>>) : Vector.<Vector.<int>>
{
var ret : Vector.<Vector.<int>> = new Vector.<Vector.<int>>();
for each(var row : Vector.<int> in map){
ret.push(row.concat());
}
return ret;
}
// mapにkindのタイプのミノをx座標mx, 回転rotで置くときの最大のy座標を求める
public function checkY(map : Vector.<Vector.<int>>, kind : int, rot : int, mx : int) : int
{
var mino : Array = MinoPattern.TABLE[kind][rot];
for(var my : uint = 0;my <= 20;my++){
for(var y : uint = 0;y < mino.length;y++){
for(var x : uint = 0;x < mino[y].length;x++){
if(mino[y][x] > 0){
if(y + my >= 21 || x + mx < 0 || x + mx >= 10 || map[y + my][x + mx] >= 0){
return my - 1;
}
}
}
}
}
return 20;
}
// mapにkindのタイプのミノをx座標mx, y座標my, 回転rotで固定する
public function fix(map : Vector.<Vector.<int>>, kind : int, rot : int, mx : int, my : int) : void
{
var mino : Array = MinoPattern.TABLE[kind][rot];
for(var y : uint = 0;y < mino.length;y++){
for(var x : uint = 0;x < mino[y].length;x++){
if(mino[y][x] > 0){
map[y + my][x + mx] = mino[y][x];
}
}
}
}
// mapの埋まっている行を消す
public function elim(map : Vector.<Vector.<int>>) : int
{
var o : uint = 0;
for(var y : int = 20;y >= 0;y--){
for(var x : int = 0;x < 10 && map[y][x] >= 0;x++){}
if(x == 10){
o++;
}else{
for(x = 0; x < 10;x++){
map[y+o][x] = map[y][x];
}
}
}
return o;
}
// mapにcurTypeのタイプのミノをx座標mx, 回転rotで落とし、続いてnextTypeのタイプのミノを落とした時の評価値の最大値を求める
public function nextnextV(map : Vector.<Vector.<int>>, curType : int, nextType : int, rot : int, mx : int) : Number
{
var bf : Vector.<Vector.<int>> = clone(map);
// シミュレート
var my : int = checkY(bf, curType, rot, mx);
fix(bf, curType, rot, mx, my);
elim(bf);
var max : Number = -999999;
for(var nrot : uint = 0;nrot < MinoPattern.TABLE[nextType].length;nrot++){
for(var nmx : int = -1;nmx < 10;nmx++){
if(isValid(bf, nextType, nrot, nmx)){
var v : Number = nextV(bf, nextType, nrot, nmx);
if(v >= max){
max = v;
}
}
}
}
return max;
}
// mapにcurTypeのタイプのミノをx座標mx, 回転rotで落とした時の評価値を求める
public function nextV(map : Vector.<Vector.<int>>, curType : int, rot : int, mx : int) : Number
{
var bf : Vector.<Vector.<int>> = clone(map);
// シミュレート
var my : int = checkY(bf, curType, rot, mx);
fix(bf, curType, rot, mx, my);
elim(bf);
// 最大高さ
var maxhe : uint = 0;
var he : Vector.<uint> = new Vector.<uint>(12);
he[0] = 20;
he[11] = 20;
for(var j : uint = 1;j <= 10;j++){
for(var i : int = 1;i < 21 && bf[i][j-1] == -1;i++){}
he[j] = 21 - i;
if(maxhe < 21 - i)maxhe = 21 - i;
}
// 穴の個数
var holes : uint = 0;
for(j = 1;j <= 10;j++){
for(i = 20;i > 20 - he[j];i--){
if(bf[i][j-1] == -1)holes++;
}
}
holes = Math.min(holes, 99);
// 1xnの溝の個数
var hole1x2 : uint = 0;
var hole1x3 : uint = 0;
var hole1x4 : uint = 0;
for(j = 1;j <= 10;j++){
if(he[j-1] - he[j] >= 2 && he[j+1] - he[j] >= 2)hole1x2++;
if(he[j-1] - he[j] >= 3 && he[j+1] - he[j] >= 3)hole1x3++;
if(he[j-1] - he[j] >= 4 && he[j+1] - he[j] >= 4)hole1x4++;
}
return _weightMaxhe[maxhe] + _weightHoles[holes] + _weightHole1x2[hole1x2] + _weightHole1x3[hole1x3] + _weightHole1x4[hole1x4];
}
// 評価値テーブル
/*
private var _weightMaxhe : Array = [-0.11091142699005256, -6.25E-4, -0.32607346966500167, -0.41000636771391363, -0.6643610374302218, -1.0340343254136584, -1.3678503071859431, -1.7424300282120946, -2.2467263944876454, -2.6837867525528876, -3.075497076167835, -3.589842595086747, -4.162010226384322, -4.606953883875305, -5.306497293728375, -5.83247554617529, -6.492127487404075, -7.126249072559087, -8.830381237685994, -9.99078189558987];
private var _weightHoles : Array = [-0.9951036299616939, -2.714002652365599, -3.8290442674274283, -5.409285011727696, -6.177571617803351, -7.840497415820682, -8.711167240923661, -9.833758387813337, -10.792908322762113, -12.392298104553197, -13.209847091854382, -14.369854905642272, -15.16472785776689, -16.365247880776927, -17.66660687529888, -18.556267548338628, -19.453424366274657, -20.482871342174775, -21.5235617671148, -22.49143919562878, -23.808095956036578, -24.64470835550944, -25.785731638961217, -26.75615559455109, -27.55444276704792, -29.06715901655279, -29.536044150360507, -30.678543284523776, -31.762190036901263, -32.77004711043744, -33.594768595486954, -34.73877244419238, -35.68482011448714, -36.17211159129267, -37.59678108106607, -38.13057683755947, -39.0577242478738, -39.95876400962042, -41.102538073405235, -41.73237359765327, -42.31391835392503, -43.06695246045468, -44.63867324732728, -45.31791939016698, -46.241568220567466, -46.93832927688558, -47.47942891489311, -48.6705932464603, -49.2718018847069, -50.220942080769156, -51.5398798044316, -51.85215477092574, -53.213186976720486, -53.70291543391014, -54.264090966021186, -56.08597807507117, -56.11433104042477, -57.04749219959868, -59.06745802361925, -60.20681993123454, -60.71409574690122, -61.49714908367152, -63.567494584476805, -64.23340238682758, -65.62563480650992, -65.5441779916961, -66.65647820681842, -67.55337290088985, -68.25993520604214, -69.5609149908839, -70.79009254574095, -70.98499991635875, -71.36763619961678, -72.17095047509731, -72.40221981869087, -70.92401671628082, -70.38779401283738, -68.85189827466807, -68.46933214429859, -67.50295764047821, -65.53679488818484, -59.97994359422197, -61.45720967045648, -57.414444267584514, -54.532455620185296, -48.374642411628805, -46.50194631639071, -42.6294614498063, -39.55164789779951, -35.47965674713575, -33.19473154210716, -29.362568159411406, -25.470255620605776, -23.819748382912216, -20.59567164164653, -15.587929587653887, -16.56295597797669, -12.6360404838533, -9.37870745520282, -47.95938254310121];
private var _weightHole1x2 : Array = [-0.19889687287859684, -0.8462733077784937, -1.0274809493762274, -1.1641907725555487, -0.9241252678180653, -0.01527659888817898];
private var _weightHole1x3 : Array = [-0.25310299623311694, -1.0039579951089181, -1.232228278307462, -1.3161977466005914, -0.27601526226508344, 0.0];
private var _weightHole1x4 : Array = [-0.30054998596265364, -1.0909098086790752, -1.3905434875046019, -1.321860614250919, -0.08228557734776255, 0.0];
*/
private var _weightMaxhe : Array = [-0.12285499832273643, -0.003115625, -0.3596554131070766, -0.48183885901276235, -0.6497026268860713, -1.0560474077090773, -1.393706206031591, -1.8201547523258212, -2.1877332754012775, -2.7083642559953947, -3.132544749081192, -3.6899115921965335, -4.175839590870263, -4.7270786108333, -5.300685687783131, -5.89188849983255, -6.408355997944355, -7.360296011519771, -8.55514390100181, -10.248587461156928]
private var _weightHoles : Array = [-1.1060618870670968, -3.0076014097196113, -4.0312529560596095, -5.2701591416610345, -6.150510153881986, -7.559315401072028, -8.70968722338642, -9.887740540036138, -10.986566365367826, -12.273843689318273, -13.07547489844442, -14.273907295987023, -15.208913633349608, -16.364494495763328, -17.468255823819145, -18.65783345788446, -19.54221266099099, -20.62541399665612, -21.668076757560886, -22.747017650967795, -23.96636195063555, -24.995645453869265, -25.851995928145918, -26.85986537106277, -27.76199251118153, -29.082171207776682, -29.682446598674524, -30.702669301052563, -31.51708573881357, -32.424028516531415, -33.52363624955628, -34.53361368835691, -35.47492674615509, -36.77435556897423, -37.29478174101791, -38.43132179964314, -39.206259441342546, -39.766282632949085, -40.74028286917213, -41.80524496978496, -42.52161282208889, -43.64944069398595, -44.15174168158642, -45.14939756256758, -45.97949386533002, -46.854801805115464, -47.93180778168921, -48.545366647288645, -49.3543457578992, -50.037796896128775, -51.62691913973323, -51.92854682100636, -52.86676951681512, -54.1062900014591, -54.15991559011257, -55.55789865420325, -56.62159942087705, -58.061271352323786, -59.02821118437856, -59.75875723912188, -60.66556316650196, -62.37400034976529, -63.440028847904436, -63.06227558093295, -64.53817395104771, -67.23518070816819, -66.6620906768176, -67.59521514182784, -67.52434106611504, -69.42126256639303, -70.11194372928122, -70.861243388627, -72.24769156434128, -71.79854575297018, -71.98786915094368, -73.79080133141686, -71.190712641078, -67.4207070911865, -66.94695418258067, -68.1761205935069, -65.45825835403214, -63.607871377254156, -60.70261451698965, -55.24284383782564, -52.91846444976562, -49.587575108323854, -46.94123117012143, -44.084938128940856, -41.537419854105906, -35.719064490505446, -32.541174435396854, -27.305396413620098, -24.076759824921854, -22.412070591358997, -19.449642915660355, -18.442401339159396, -16.278374279001483, -14.698621402006435, -11.670906322287946, -43.46838072402747]
private var _weightHole1x2 : Array = [-0.2287581167346685, -0.8858843762466385, -1.1057716862866422, -1.1835349176333332, -0.9716639242273695, -0.022617043420473895]
private var _weightHole1x3 : Array = [-0.30276893631038954, -1.0622108348522872, -1.2628840740687635, -1.327908286185353, -0.3064975330368757, -0.002964375]
private var _weightHole1x4 : Array = [-0.36479922505598644, -1.1782744070305664, -1.4432220809163099, -1.3344880552159064, -0.08286411224484877, 0.0]
// 各キーをDOWNしてからのフレーム数
private var _tLeft : uint = 0;
private var _tRight : uint = 0;
private var _tZ : uint = 0; // +1
private var _tX : uint = 0; // -1
private var _tUp : uint = 0;
// キー入力に介入する!
public function interpose(root : EventDispatcher, x : int, rot : int) : void
{
if(x == -99)return;
// 同時押しの影響で、キーが少ないほうが反映されやすい?
_tLeft = process(root, Keyboard.LEFT, _tLeft, _targX < x);
_tRight = process(root, Keyboard.RIGHT, _tRight, _targX > x);
// _tZ = process(root, Keyboard.Z, _tZ, _targRot > rot && !(_targRot == 3 && rot == 0));
// _tX = process(root, Keyboard.X, _tX, _targRot < rot || (_targRot == 3 && rot == 0));
_tZ = process(root, Keyboard.Z, _tZ, _targRot != rot);
if(_targRot == rot && _targX == x)_tUp = process(root, Keyboard.UP, _tUp, _targRot == rot && _targX == x);
/*
if(_tLeft > 0 || _tRight > 0 || _tZ > 0 || _tX > 0 || _tUp > 0){
_tf.text = "" + _tLeft + "\t" + _tRight + "\t" + _tZ + "\t" + _tUp + "\n";
_tf.appendText("" + _targX + "\t" + _targRot + "\n");
_tf.appendText("" + x + "\t" + rot + "\n");
return;
}
*/
}
// 各キーの処理
// 最初の2フレームKEY_DOWN, 次の2フレームKEY_UP
private function process(root : EventDispatcher, key : int, t : int, flag : Boolean) : int
{
if(t <= 1){
root.dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_UP, true, false, 0, key));
}else{
root.dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, 0, key));
}
if(t > 0)t--;
return (t == 0 && flag) ? 3 : t;
}
}