テトリスAIかいりょうばん
新しいブロックが出てから置く場所を探しています。
* NEXTは見ていません。というよりNEXT自体を実装していません。
// 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)) );
}
}