<html>
<head>
<title>TETRIS AI</title>
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="-1" />
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="MSThemeCompatible" content="yes" />
<script type="text/javascript" src="./tetrisai.js"></script>
<style>
.fill {
width: 2px;
height: 2px;
background-color: #666666;
}
.blank {
width: 6px;
height: 6px;
background-color: #FFFFFF;
}
.active {
width: 6px;
height: 6px;
background-color: #3399FF;
}
.pad {
background-color: #EEEEEE
}
.error {
width: 6px;
height: 6px;
background-color: #FF0000
}
.block {
width: 6px;
height: 6px;
display:none;
}
</style>
</head>
<body onload="load()" onkeydown="EventDispatcher()">
<table>
<tr>
<td>
<div id="render1"></div>
<input type="button" value="start" onclick="ctrl1.start();">
<input type="button" value="stop" onclick="ctrl1.stop();">
<input type="button" value="clear" onclick="ctrl1.clear();">
</td>
<td>
<!-- <div id="render2"></div> -->
<!-- <input type="button" value="start" onclick="ctrl2.start();">-->
<!-- <input type="button" value="stop" onclick="ctrl2.stop();">-->
<!-- <input type="button" value="clear" onclick="ctrl2.clear();">-->
<!-- </td>-->
</tr>
</table>
</body>
</html>
tetrisai.js
/**************************
* Tetris AI Refine Edition
* @author FuLi
**************************/
/*
* The container of the basic methods of the Tetris.
*/
TetrisUtil = {
/*
* Copy all the properties of source into destination.
*/
mix : function(/* mixed */destination, /* mixed */source) {
for ( var property in source) {
destination[property] = source[property];
}
return destination;
},
/*
* Returns a new class of JavaScript that the default initialize method is
* this.initialize.
*/
createClass : function() {
return function() {
this.initialize.apply(this, arguments);
}
},
/*
* Returns a clone array of pure data(string/number),without any references.
*/
cloneAry : function(/* Array */aryin) {
var ret = [];
var length = aryin.length;
for ( var i = 0; i < length; i++) {
var element;
if (typeof aryin == "object" && aryin[i].constructor == Array) {
element = arguments.callee(aryin[i]);
} else {
element = aryin[i];
}
ret.push(element);
}
return ret;
},
/*
* Returns a clone array of pure number without any references, very fast in
* IE.
*/
cloneSimpleAry : function(/**/aryin) {
return eval("([" + aryin.toString() + "])");
},
/*
* Returns the really array object from a fake array(object set)
*/
toArray : function(/* */ary) {
var ret = [];
for ( var i = 0; i < ary.length; i++) {
ret.push(ary[i]);
}
return ret;
},
/*
* Returns a new function that content is the first argument,scopee is the
* second argument,and the others is the argument to bind to the given
* function.
*/
bind : function(/* Function,obj0[,obj1...] */) {
var args = TetrisUtil.toArray(arguments);
var __method = args.shift();
var object = args.shift();
return function() {
return __method.apply(object, args.concat(TetrisUtil.toArray(arguments)));
}
},
/*
* Returns the sum of the input array,which is a set of pure number.
*/
sumAry : function(/* Array */ary) {
var ret = 0;
for ( var i = 0; i < ary.length; i++) {
ret += ary[i];
}
return ret;
},
/*
* Inputs a 2d array[x][y],and returns sum x.
*/
sumAryX : function(/* Array[][] */ary2d,/* int */rowIndex) {
var sum = 0;
for ( var i = 0; i < ary2d.length; i++) {
sum += ary2d[i][rowIndex];
}
return sum;
},
/*
* Empty function for some default function references.
*/
emptyFunction : function() {
},
deleteLine : function(/* Array[][] */dataTable,/* int */index) {
for ( var i = index - 1; i >= 0; i--) {
for ( var j = 0; j < dataTable.length; j++) {
dataTable[j][i + 1] = dataTable[j][i];
dataTable[j].shift();
dataTable[j].unshift(0);
}
}
},
/*
* Returns the count of cleared lines,which the lines are full fixed.
*/
clearLine : function(/* Array[][] */dataTable) {
var clearCount = 0;
for ( var i = 0; i < dataTable[0].length; i++) {
if (TetrisUtil.sumAryX(dataTable, i) == dataTable.length) {
clearCount++;
TetrisUtil.deleteLine(dataTable, i);
}
}
return clearCount;
},
/*
* Merge a position array(bar array)into a base table. using the (given
* function/default function) to process
* baseTable[posAry[i][0]][posAry[i][0]] the given function would get two
* arguments,first is the effecting cell of basetable,second is posAry[i].
* FIX ME: I didn't explained it clearly.
*/
mergePos : function(/* Array[][] */baseTable,/* Array[4][2] */posAry,/* Optional,Function */operation) {
operation = operation || function() {
return 2;
}
for ( var i = 0; i < posAry.length; i++) {
if (typeof baseTable[posAry[i][0]][posAry[i][1]] == "undefined") {
throw "error";
}
baseTable[posAry[i][0]][posAry[i][1]] = operation(baseTable[posAry[i][0]][posAry[i][1]], posAry[i]);
}
return baseTable;
}
}
/*
* A timer class which manages all timing events.
*/
var Timer = TetrisUtil.createClass();
/*
* Extends the Timer's static members.
*/
TetrisUtil.mix(Timer, {
/*
* A cache of all the timers.Using timer's name to map timer instance.
*/
timers : {},
/*
* Starts all timers in the timer cache.
*/
startAll : function() {
var timers = this.timers;
for ( var i = 0; i < timers.length; i++) {
timers[i].start();
}
},
/*
* Stops all timers in the timer cache.
*/
stopAll : function() {
var timers = this.timers;
for ( var i = 0; i < timers.length; i++) {
timers[i].stop();
}
},
/*
* Returns one timer using the timer name.
*/
get : function(/* String */name) {
return this.timers[name];
},
/*
* Removes and returns a timer with the certain name.
*/
remove : function(/* String */name) {
var timers = TetrisUtil.toArray(this.timers);
var ret;
var timersnew = [];
while (timers.length) {
var tmp = timers.shift();
if (tmp.name == name) {
ret = tmp;
} else {
timersnew.push(tmp);
}
}
return ret;
},
/*
* Returns an array of all timers.
*/
getAll : function() {
return TetrisUtil.toArray(this.timers);
}
});
/*
* Mixin the members of all the timer instances.
*/
TetrisUtil.mix(Timer.prototype, {
/*
* A switch of timer.
*/
started :false,
/*
* invertal of the timer.
*/
invertal :1000,
/*
* Timer name.
*/
name :"",
/*
* Default method for timer to invok.
*/
method : function() {
},
/*
* Constructor,to initialize a timer.If the name is used ,it will throw an
* exception.
*/
initialize : function(/* String */name,/* int */invertal,/* Function */method) {
if (this.constructor.timers[name]) {
throw "has this timer already";
} else {
this.constructor.timers[name] = this;
}
this.name = name;
if (typeof invertal != "undefined") {
this.invertal = invertal;
}
;
if (method) {
this.method = method;
}
;
},
/*
* The core of Timer,Cycle invok this method in order to invok this timer's
* method.
*/
doTimer : function() {
if (!this.started) {
return;
}
setTimeout(TetrisUtil.bind(arguments.callee, this), this.invertal);
this.method();
},
/*
* To start this timer.
*/
start : function() {
if (!this.started) {
this.started = true;
this.doTimer();
}
},
/*
* To stop this timer,the current running time is not effected.
*/
stop : function() {
this.started = false;
},
showBar : function(bar,render) {
render.appendChild( this.createPad(10,10).table);
}
});
/*
* Main Class of the tetris Game.
*/
var Tetris = TetrisUtil.createClass();
/*
* Static consts of the game,now only conatins the type of the controller.
*/
Tetris.CONSTS = {
KEYBOARD :"keyboard",
AI :"ai"
}
/*
* Add all base behavior of tetris game.
*/
TetrisUtil.mix(Tetris.prototype, {
/*
* Specify the current status of the game.
*/
status : {},
/*
* All bar data in this 2d array.each is an array of bar.
*/
barData : [ [ [ 4, 2 ], [ 4, 3 ], [ 5, 3 ], [ 6, 3 ] ], [ [ 4, 2 ], [ 5, 2 ], [ 4, 3 ], [ 4, 4 ] ], [ [ 4, 2 ], [ 5, 2 ], [ 6, 2 ], [ 6, 3 ] ], [ [ 5, 2 ], [ 5, 3 ], [ 5, 4 ], [ 4, 4 ] ], [ [ 6, 2 ], [ 6, 3 ], [ 4, 3 ], [ 5, 3 ] ], [ [ 4, 2 ], [ 4, 3 ], [ 4, 4 ], [ 5, 4 ] ], [ [ 4, 2 ], [ 4, 3 ], [ 5, 2 ], [ 6, 2 ] ], [ [ 4, 2 ], [ 5, 2 ], [ 5, 3 ], [ 5, 4 ] ], [ [ 4, 2 ], [ 5, 2 ], [ 5, 3 ], [ 6, 3 ] ], [ [ 5, 2 ], [ 5, 3 ], [ 4, 3 ], [ 4, 4 ] ], [ [ 4, 3 ], [ 5, 3 ], [ 5, 2 ], [ 6, 2 ] ], [ [ 4, 2 ], [ 4, 3 ], [ 5, 3 ], [ 5, 4 ] ],
[ [ 4, 2 ], [ 5, 2 ], [ 6, 2 ], [ 5, 3 ] ], [ [ 4, 2 ], [ 4, 3 ], [ 4, 4 ], [ 5, 3 ] ], [ [ 5, 2 ], [ 5, 3 ], [ 4, 3 ], [ 6, 3 ] ], [ [ 5, 2 ], [ 5, 3 ], [ 5, 4 ], [ 4, 3 ] ], [ [ 4, 2 ], [ 5, 2 ], [ 4, 3 ], [ 5, 3 ] ], [ [ 4, 2 ], [ 5, 2 ], [ 4, 3 ], [ 5, 3 ] ], [ [ 3, 2 ], [ 4, 2 ], [ 5, 2 ], [ 6, 2 ] ], [ [ 5, 2 ], [ 5, 3 ], [ 5, 4 ], [ 5, 5 ] ] ],
/*
* Method of initialize,specify the game's name,render, and the col/row
* counts.
*/
initialize : function(name, render, width, height) {
this.render = render = document.getElementById(render);
var padInfo = this.createPad(width, height);
this.name = name;
this.table = padInfo.table;
this.tableMap = padInfo.tableMap;
this.dataTable = padInfo.dataTable;
this.rect = {
width :width,
height :height
}
render.appendChild(this.table);
this.status = {};
pageContext.tetris[name] = this;
},
/*
* replace the current game with an stopped empty game.
*/
clear : function() {
var padInfo = this.createPad(this.rect.width, this.rect.height);
this.table = padInfo.table;
this.tableMap = padInfo.tableMap;
this.dataTable = padInfo.dataTable;
this.render.innerHTML = "";
this.render.appendChild(this.table);
this.status = {};
this.refresh();
},
/*
* Create the game pad to display,and the data model's in memory;
*/
createPad : function(width, height) {
var table = document.createElement('table');
table.className = "pad";
var tbody = document.createElement('tbody');
table.appendChild(tbody);
var tableMap = [];
var dataTable = [];
for ( var i = 0; i < height; i++) {
var tr = document.createElement("tr");
if (i < 2) {
tr.style.display = "none";
}
tbody.appendChild(tr);
for ( var j = 0; j < width; j++) {
var td = document.createElement("td");
td.className = "blank";
tr.appendChild(td);
tableMap[j] = tableMap[j] || [];
dataTable[j] = dataTable[j] || [];
tableMap[j][i] = td;
dataTable[j][i] = 0;
}
}
return {
table :table,
tableMap :tableMap,
dataTable :dataTable
};
},
/*
* Get a new bar.If the bar No. is not specified ,use an random bar.
*/
newBar : function(barSN) {
barSN = (typeof barSN == "undefined") ? Math.floor(Math.random() * this.barData.length) : barSN;
var ret ;
if(!this.status.nextBar) {
this.status.nextBar = this.barData[Math.floor(Math.random() * this.barData.length)];
}
ret = this.status.bar = this.status.nextBar;
this.status.nextBar = this.barData[barSN];
return ret;
},
/*
* Check if the specified step is available .Param absPos is a absolute
* position array of a bar;
*/
validateStep : function(absPos) {
var copyOfDataTable = TetrisUtil.cloneAry(this.dataTable);
try {
TetrisUtil.mergePos(copyOfDataTable, absPos, function() {
return arguments[0] + 2;
});
var toCheckAry = TetrisUtil.cloneSimpleAry(copyOfDataTable);
for ( var i = 0; i < toCheckAry.length; i++) {
if (toCheckAry[i] > 2) {
return false;
}
}
return true;
} catch (e) {
return false;
}
},
/*
* Refresh the table to display.
*/
refresh : function() {
for ( var i = 0; i < this.dataTable.length; i++) {
for ( var j = 2; j < this.dataTable[i].length; j++) {
switch (this.dataTable[i][j]) {
case 0:
this.tableMap[i][j].className = "blank";
break;
case 1:
this.tableMap[i][j].className = "fill";
break;
case 2:
this.tableMap[i][j].className = "active";
break;
default:
this.tableMap[i][j].className = "error";
break;
}
}
}
},
/*
* Join the offset to the given position(bar),and returns a new one ,without
* modifying the given param.
*/
joinOffsetToPos : function(posAry, offset) {
var ret = [];
for ( var i = 0; i < posAry.length; i++) {
ret[i] = TetrisUtil.cloneSimpleAry(posAry[i]);
ret[i][0] += offset[0];
ret[i][1] += offset[1];
}
return ret;
},
/*
* Make a bar static,mostly used when a bar shouldn't move.
*/
fixBar : function(absPos) {
TetrisUtil.mergePos(this.dataTable, absPos, function() {
return 1;
});
},
/*
* Set a absolute position of the tetris dataTable active(plus 2).
*/
setActive : function(absPos) {
TetrisUtil.mergePos(this.dataTable, absPos, function() {
return 2;
});
},
/*
* Clears the active pos in the tetris dataTable.
*/
clearActive : function() {
for ( var i = 0; i < this.dataTable.length; i++) {
for ( var j = 0; j < this.dataTable[i].length; j++) {
if (this.dataTable[i][j] == 2) {
this.dataTable[i][j] = 0;
}
}
}
},
/*
* Roll's the given bar into a new bar array,then return the new array;
*/
rollBar : function(bar) {
var ret = [];
var centerX = Math.round((bar[0][0] + bar[1][0] + bar[2][0] + bar[3][0]) / 4);
var centerY = Math.round((bar[0][1] + bar[1][1] + bar[2][1] + bar[3][1]) / 4);
for ( var i = 0; i < 4; i++) {
ret[i] = [];
ret[i][0] = centerX - bar[i][1] + centerY;
ret[i][1] = bar[i][0] + centerY - centerX;
}
return ret;
},
/*
* Invoks the interface of the ControllFactory,and give the current tetirs
* instance to the controller.
*/
buildController : function(controllerFactory) {
return controllerFactory.getNewController(this);
},
/*
* Returns the max height the given bar could drop.
*/
getDropHeight : function(absBar) {
var dropHeight = this.rect.height;
for ( var i = 0; i < absBar.length; i++) {
var col = this.dataTable[absBar[i][0]];
var colDropHeight = 0
for ( var j = absBar[i][1] + 1; j < col.length; j++) {
if (col[j] == 0) {
colDropHeight++;
} else
break;
}
if (colDropHeight < dropHeight) {
dropHeight = colDropHeight;
}
}
return dropHeight;
}
});
/*
* Declare the basic behavior of the controllers(abstract class).
*/
var BasicControllerBehavior = {
/*
* The controller status.
*/
started :false,
/*
* Timer instance of this controller.
*/
timer :null,
/*
* The speed(inveretal) of the controller for down bar.Default is 1000.
*/
speed :1000,
/*
* Start the controller,as well as the timer in it;
*/
start : function() {
if (this.started) {
return;
}
this.started = true;
this.timer = this.timer || new Timer(this.tetris.name, this.speed, TetrisUtil.bind(this.control, this));
this.timer.start();
this.startNewBar();
},
/*
* Stop the controller,as well as the timer in it;
*/
stop : function() {
this.started = false;
Timer.get(this.tetris.name).stop();
},
/*
* Trigger the gameover of the tetris.
*/
gameOver : function() {
alert(this.tetris.name + " Game Over!");
this.stop();
},
/*
* Stop the game and get a new stopped game.
*/
clear : function() {
this.stop();
this.tetris.clear();
},
/*
* Set the speed(invertal) of the game.
*/
setSpeed : function(speed) {
this.speed = speed;
},
/*
* Trigger the bar go left.If cannot move ,returns false.
*/
left : function() {
var ret = false;
if (!this.started) {
return false;
}
var _t = this.tetris;
_t.clearActive();
if (!_t.status.bar) {
_t.newBar();
}
if (!_t.status.offset) {
_t.status.offset = [ 0, 0 ];
}
var tmpOffset = TetrisUtil.cloneSimpleAry(_t.status.offset);
tmpOffset[0]--;
var absPos = _t.joinOffsetToPos(_t.status.bar, tmpOffset);
if (_t.validateStep(absPos)) {
_t.status.offset[0]--;
ret = true;
}
_t.setActive(_t.joinOffsetToPos(_t.status.bar, _t.status.offset));
_t.refresh();
return ret;
},
/*
* Trigger the bar go right.If cannot move ,returns false.
*/
right : function() {
var ret = false;
if (!this.started) {
return false;
}
var _t = this.tetris;
_t.clearActive();
if (!_t.status.bar) {
_t.newBar();
}
if (!_t.status.offset) {
_t.status.offset = [ 0, 0 ];
}
var tmpOffset = TetrisUtil.cloneSimpleAry(_t.status.offset);
tmpOffset[0]++;
var absPos = _t.joinOffsetToPos(_t.status.bar, tmpOffset);
if (_t.validateStep(absPos)) {
_t.status.offset[0]++;
ret = true;
}
_t.setActive(_t.joinOffsetToPos(_t.status.bar, _t.status.offset));
_t.refresh();
return ret;
},
/*
* Trigger the bar go down.If cannot move ,make it static.
*/
down : function() {
if (!this.started) {
return false;
}
var ret;
var _t = this.tetris;
_t.clearActive();
if (!_t.status.bar) {
_t.newBar();
}
if (!_t.status.offset) {
_t.status.offset = [ 0, 0 ];
}
var tmpOffset = TetrisUtil.cloneSimpleAry(_t.status.offset);
tmpOffset[1]++;
var absPos = _t.joinOffsetToPos(_t.status.bar, tmpOffset);
if (_t.validateStep(absPos)) {
_t.status.offset[1]++;
ret = true;
} else {
var _fixPos = _t.joinOffsetToPos(_t.status.bar, _t.status.offset);
_t.fixBar(_fixPos);
_t.clearActive();
TetrisUtil.clearLine(_t.dataTable);
_t.status.offset = [ 0, 0 ];
_t.newBar();
this.startNewBar();
for ( var i = 0; i < _fixPos.length; i++) {
if (_fixPos[i][1] <= 2) {
this.gameOver();
return false;
}
}
ret = false;
}
_t.setActive(_t.joinOffsetToPos(_t.status.bar, _t.status.offset));
_t.refresh();
return ret;
},
/*
* Roll the current bar..If cannot roll ,returns false.
*/
rollBar : function() {
if (!this.started) {
return false;
}
var _t = this.tetris;
_t.clearActive();
if (!_t.status.bar) {
_t.newBar();
}
if (!_t.status.offset) {
_t.status.offset = [ 0, 0 ];
}
var tmpBar = _t.rollBar(_t.status.bar);
var absPos = _t.joinOffsetToPos(tmpBar, _t.status.offset);
if (_t.validateStep(absPos)) {
_t.status.bar = tmpBar;
_t.setActive(_t.joinOffsetToPos(_t.status.bar, _t.status.offset));
_t.refresh();
return true;
} else {
_t.setActive(_t.joinOffsetToPos(_t.status.bar, _t.status.offset));
return false;
}
},
/*
* Drop the bar on to the bottom and make it static.
*/
drop : function() {
if (!this.started) {
return false;
}
var ret;
var _t = this.tetris;
_t.clearActive();
if (!_t.status.bar) {
_t.newBar();
}
if (!_t.status.offset) {
_t.status.offset = [ 0, 0 ];
}
var absPos = _t.joinOffsetToPos(_t.status.bar, _t.status.offset);
_t.clearActive();
var dropHeight = _t.getDropHeight(absPos);
_t.status.offset[1] += dropHeight;
var _fixPos = _t.joinOffsetToPos(_t.status.bar, _t.status.offset);
_t.fixBar(_fixPos);
_t.clearActive();
TetrisUtil.clearLine(_t.dataTable);
_t.status.offset = [ 0, 0 ];
_t.newBar();
this.startNewBar();
for ( var i = 0; i < _fixPos.length; i++) {
if (_fixPos[i][1] == 0) {
this.gameOver();
return false;
}
}
_t.setActive(_t.joinOffsetToPos(_t.status.bar, _t.status.offset));
_t.refresh();
ret = false;
return ret;
},
/*
* The function of downing bar.
*/
control : function() {
if (this.started) {
this.down();
}
},
/*
* Triggered by starting a new bar.Default is an empty function.
*/
startNewBar :TetrisUtil.emptyFunction
}
/*
* KeyBoardController factory. For a tetris game to get a keyboard controller.
*/
var KeyBoardControllerFactory = {
/*
* Returns a new controller,with the reference of the tetris.
*/
getNewController : function(tetris) {
return new KeyBoardController(tetris);
}
}
/*
* AIControllerFactory factory. For a tetris game to get a AI controller.
*/
var AIControllerFactory = {
getNewController : function(tetris) {
return new AIController(tetris);
}
}
/*
* AI controller class.
*/
var AIController = TetrisUtil.createClass();
/*
* Extends the controllers' base behavior into AI controller;
*/
TetrisUtil.mix(AIController.prototype, BasicControllerBehavior);
/*
* Add the implement of the AI controller.
*/
TetrisUtil.mix(AIController.prototype, {
/*
* Init of the controller.Creates the controller and cache in pageContext.
*/
initialize : function(tetris) {
this.tetris = tetris;
this.type = Tetris.CONSTS.AI;
pageContext.controller[this.tetris.name] = this;
},
/*
* Calculate the best position and move.
*/
aiMove : function() {
var best = this.calcBest();
this.move(best.x, best.roll);
},
/*
* Calculate the best position and roll.Iterate every possible situation.
*/
calcBest : function() {
var _t = this.tetris;
var bar = TetrisUtil.cloneAry(_t.status.bar);
var status = {
x :0,
roll :0,
value :0
};
for ( var roll = 0; roll < 4; roll++) {
var offset = [ 0, 0 ];
var absPos = _t.joinOffsetToPos(bar, offset);
while (_t.validateStep(absPos)) {
var result = this.calc(absPos);
if (result > status.value) {
status.x = offset[0];
status.roll = roll;
status.value = result;
}
offset[0]--;
absPos = _t.joinOffsetToPos(bar, offset);
}
offset = [ 1, 0 ];
absBar = _t.joinOffsetToPos(bar, offset);
while (_t.validateStep(absBar)) {
var result = this.calc(absBar);
if (result > status.value) {
status.x = offset[0];
status.roll = roll;
status.value = result;
}
offset[0]++;
absBar = _t.joinOffsetToPos(bar, offset);
}
bar = _t.rollBar(bar);
}
return status;
},
/*
* Calculate the value of an absolute position(didn't drop).The lager,the
* better.
*/
calc : function(absBar) {
var _t = this.tetris;
var offset = [ 0, _t.getDropHeight(absBar) ];
absBar = _t.joinOffsetToPos(absBar, offset);
var copyTable = TetrisUtil.cloneAry(_t.dataTable);
TetrisUtil.mergePos(copyTable, absBar, function() {
return 1;
});
return this.calcValue(copyTable, absBar);
},
/*
* Calculate the value of an absolute position(dropped).The lager,the
* better.
*/
calcValue : function(fixedTable, absBar) {
var value = 1;
var empty = 1;
var shape = 0;
var floor = 1;
var beside = 1;
TetrisUtil.mergePos(fixedTable, absBar, function() {
return arguments[0] + 2;
});
for ( var i = 0; i < absBar.length; i++) {
floor += absBar[i][1];
if (typeof fixedTable[absBar[i][0]][absBar[i][1] + 1] == "undefined") {
shape += 2;
} else {
if (fixedTable[absBar[i][0]][absBar[i][1] + 1] == 1) {
shape += 2;
} else if (fixedTable[absBar[i][0]][absBar[i][1] + 1] == 0) {
empty++;
} else if (fixedTable[absBar[i][0]][absBar[i][1] + 1] == 3) {
shape -= 0.2;
}
for ( var j = absBar[i][1] + 2; j < fixedTable.length; j++) {
if (fixedTable[absBar[i][0]][absBar[i][j]] == 0) {
empty += 1;
}
}
}
if ((typeof fixedTable[absBar[i][0] + 1] == "undefined") || (typeof fixedTable[absBar[i][0] - 1] == "undefined")) {
beside++;
} else if (fixedTable[absBar[i][0] + 1][absBar[i][1]] == 1) {
beside++;
} else if (fixedTable[absBar[i][0] - 1][absBar[i][1]] == 1) {
beside++;
}
}
var rowsFilld = 0;
var count = 0;
for ( var j = 0; j < this.tetris.rect.height; j++) {
var sum = TetrisUtil.sumAryX(fixedTable, j);
if (sum > 0) {
count++;
}
rowsFilld += sum;
}
rowsFilld = rowsFilld / count;
var lines = TetrisUtil.clearLine(fixedTable);
this.tetris.refresh();
var ret = this.AIFunction(floor, lines, shape, beside, rowsFilld, empty);
return ret;
},
/*
* Internal function.while a new bar started ,it is invoked.
*/
startNewBar : function() {
this.aiMove();
},
/*
* Give x offset and times of rolling,to move to the specified position.
*/
move : function(x, roll) {
var _s = this.tetris.status;
for ( var i = 0; i < roll; i++) {
if (!this.rollBar()) {
return;
}
}
if (x > _s.offset[0]) {
while (x > _s.offset[0]) {
if (!this.right()) {
return;
}
}
} else if (x < _s.offset[0]) {
while (x < _s.offset[0]) {
if (!this.left()) {
return;
}
}
}
},
/*
* The function to calculate the position weight.
*/
AIFunction : function(floor, lines, shape, beside, rowsFilld, empty) {
return Math.pow(lines * lines + 1, 3) * Math.pow(shape, 3) * Math.pow(beside + 2, 5) * Math.pow(rowsFilld, 2) * Math.pow(floor, 5) / (Math.pow(empty, 4));
}
});
/*
* Keyboard controller class.
*/
var KeyBoardController = TetrisUtil.createClass();
/*
* Extends the controllers' base behavior into Keyboard controller;
*/
TetrisUtil.mix(KeyBoardController.prototype, BasicControllerBehavior);
/*
* Implements the Keyboard controller.
*/
TetrisUtil.mix(KeyBoardController.prototype, {
/*
* Init of the controller.Creates the controller and cache in pageContext
* whith a default key map.
*/
initialize : function(tetris) {
this.tetris = tetris;
this.type = Tetris.CONSTS.KEYBOARD;
pageContext.controller[this.tetris.name] = this;
this.setKeyMap( {
UP :38,
DOWN :40,
LEFT :37,
RIGHT :39,
DROP :32
});
},
/*
* Replace the old key map with a new one.
*/
setKeyMap : function(keyMap) {
pageContext.keyMap[this.tetris.name] = keyMap;
},
/*
* Dispatch the event to the specified method to control the tetris.
*/
move : function(keyCode) {
var name = this.tetris.name;
switch (keyCode) {
case pageContext.keyMap[name].UP:
this.rollBar();
break;
case pageContext.keyMap[name].DOWN:
this.down();
break;
case pageContext.keyMap[name].LEFT:
this.left();
break;
case pageContext.keyMap[name].RIGHT:
this.right();
break;
case pageContext.keyMap[name].DROP:
this.drop();
break;
}
}
});
/*
* The keyboard event dispatcher .
*/
var EventDispatcher = function() {
for ( var name in pageContext.controller) {
if (pageContext.controller[name].type == Tetris.CONSTS.KEYBOARD) {
pageContext.controller[name].move(event.keyCode);
}
}
};
/*
* Context of the page.
*/
var pageContext = {
tetris : {},
controller : {},
keyMap : {}
};
/*
* Load method on page complete.AI
*/
function load() {
var tetris = new Tetris("myTetris1", "render1", 10, 19);
window.ctrl1 = tetris.buildController(AIControllerFactory);
window.ctrl1.setSpeed(25);
ctrl1.start();
}
//Keyboard
//function load() {
// var tetris = new Tetris("myTetris1", "render1", 10, 19);
// window.ctrl1 = tetris.buildController(KeyBoardControllerFactory);
// ctrl1.setKeyMap({
// UP:87,
// DOWN:83,
// LEFT:65,
// RIGHT:68,
// DROP:18
// });
// ctrl1.start();
//}
洋洋洒洒1000行。。自己留个底吧