带简单AI的俄罗斯方块

<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行。。自己留个底吧

你可能感兴趣的:(JavaScript,cache,IE,prototype,J#)