﻿/*--
标题：围堵
设计：王集鹄
博客：http://blog.csdn.net/zswang
日期：2009年12月5日
--*/

//2009年12月06日 王集鹄 添加 地图参数、马和象的走法、操作次数、图像样式的兼容
//2009年12月09日 王集鹄 添加 扩展目标地
//2009年12月10日 王集鹄 添加 悔棋功能

function Box(options) {
	options = options || {};
	var self = this;
	this.rowCount = options.rowCount || 15; // 行数
	this.colCount = options.colCount || 15; // 列数
	this.kickCount = options.kickCount || 1; // 玩家操作多少次一个周期
	this.parent = options.parent || document.body; // 容器
	this.caption = options.caption || "Replay"; // 标题
	this.map = (options.map || "").replace(/[\s\\]+/g, ""); // 地图数据
	
	this.button_replay = document.createElement("input");
	this.button_replay.type = "button";
	this.button_replay.value = this.caption;
	this.button_replay.onclick = function() { 
		self.replay(); 
	};
	this.parent.appendChild(this.button_replay);

	this.button_repent = document.createElement("input");
	this.button_repent.type = "button";
	this.button_repent.value = "悔棋";
	this.button_repent.onclick = function() {
		self.repent();
	}
	this.parent.appendChild(this.button_repent);

	this.table_back = document.createElement("table");
	this.table_back.className = "table_back";
	this.table_back.cellPadding = "0px";
	this.table_back.cellSpacing = "0px";
	this.playing = false; // 是否在进行中
	this.floors = {}; // 矩阵地板矩阵 [y,x]
	for (var i = 0; i < this.rowCount; i++) {
		var tr = this.table_back.insertRow(-1);
		for (var j = 0; j < this.colCount; j++) {
			var td = tr.insertCell(-1);
			this.floors[i + "," + j] = new Floor({
				td: td
				, box: this
				, pos: { x: j, y: i }
			});
		}
	}
	this.parent.appendChild(this.table_back);
	this.replay();
}

// 重新开始
Box.prototype.replay = function() {
	this.playing = true;
	this.mickeys = [];
	this.manual = []; // 棋谱
	this.step = 0;
	var k = 0;
	for (var i = 0; i < this.rowCount; i++) {
		for (var j = 0; j < this.colCount; j++) {
			var name = this.map.substr(k++, 1);
			var floor = this.floors[i + "," + j];
			floor.edge = name == "#";
			switch(name) {
				case "1": case "x": case "w": case "c":
					floor.setState("closed");
					break;
				case "r": case "b": case "n": case "q":
					var mickey = new Mickey({name: name, box: this, pos: {x: j, y: i}});
					this.mickeys.push(mickey);
					break;
				default:
					floor.setState("blank");
					break;
			}
		}
	}
};

// 下一个周期
Box.prototype.kick = function(floor) {
	if (!this.playing) return;
	this.step++;
	// 记录棋谱
	var mickeysPos = [];
	for (var i = 0; i < this.mickeys.length; i++)
		mickeysPos.push({x: this.mickeys[i].pos.x, y: this.mickeys[i].pos.y});
	this.manual.push({floor: floor, mickeysPos: mickeysPos});
	
	if (this.step % this.kickCount != 0) return;
	var count = 0;
	var freedom = false;
	for (var i = 0; i < this.mickeys.length; i++) {
		if (this.mickeys[i].run()) count++;
		if (this.mickeys[i].freedom) freedom = true;
	}
	if (freedom) // 有老鼠跑了
		this.gameover();
	else if (!count) this.win();
};

// 悔棋
Box.prototype.repent = function() {
	if (this.manual.length <= 0) return;
	this.step--;
	this.playing = true;
	var item = this.manual.pop();
	for (var i = this.mickeys.length - 1; i >= 0; i--)
		this.mickeys[i].move(item.mickeysPos[i]);
	item.floor.setState("blank");
}

// 游戏结束
Box.prototype.gameover = function() {
	if (!this.playing) return;
	this.playing = false;
	if (this.ongameover)
		this.ongameover();
	else alert(this.caption + "跑掉了。o(╯□╰)o");
};

Box.prototype.win = function() {
	if (!this.playing) return;
	this.playing = false;
	if (this.onwin) 
		this.onwin();
	else alert(this.caption + "O(∩_∩)O成功！");
};

// 地板
function Floor(options) {
	options = options || {};
	var self = this;
	this.td = options.td || {};
	this.td.innerHTML = "&nbsp;";
	this.td.onclick = function() {
		if (!self.box.playing) return; // 游戏已经结束
		if (self.state != "blank") return;
		if (typeof playSound == "function") playSound("move"); // 播放声音
		self.setState("closed");
		self.box.kick(self);
	}
	this.box = options.box || {};
	this.pos = options.pos || {};
	this.edge = options.edge;
	this.state = "none";
	this.doChange();
}

// 状态变化
Floor.prototype.doChange = function() {
	var className = "floor";
	if (this.state == "blank" && this.edge) // 边缘
		className += " edge";
	className += " " + this.state;
	if (typeof this.arrow != "undefined") 
		className += " " + this.state + this.arrow;
	if (this.td.className != className)
		this.td.className = className;
};

Floor.prototype.setState = function(state, arrow) {
	if (this.state == state) return;
	this.state = state;
	this.arrow = arrow;
	this.doChange();
};

// 老鼠类
function Mickey(options) {
	options = options || {};
	this.box = options.box || {};
	this.name = options.name || "rook"; 
	this.pos = {};
	if (this.name.length == 1)
		this.name = { "n": "knight", "q": "queen", "r": "rook", "b": "bishop" }[this.name];
	this.route = []; // 逃跑路线
	this.freedom = false; // 是否获得自由
	this.move(options.pos || {});
	this.offsets = this.types[this.name].offsets;
}

Mickey.prototype.types = {
	"knight": {
		caption: "♞"
		, offsets: [
			{ x: -2, y: -1 }
			, { x: -1, y: -2 }
			, { x: +2, y: -1 }
			, { x: +1, y: -2 }
			, { x: +2, y: +1 }
			, { x: +1, y: +2 }
			, { x: -2, y: +1 }
			, { x: -1, y: +2 }
		]
	}
	, "queen": {
		caption: "♛"
		, offsets: [
			{x: 0, y: -1}
			, {x: +1, y: 0}
			, {x: 0, y: +1}
			, {x: -1, y: 0}
			, {x: -1, y: -1}
			, {x: +1, y: -1}
			, {x: +1, y: +1}
			, {x: -1, y: +1}
		]
	}
	, "rook": {
		caption: "♜"
		, offsets: [
			{x: 0, y: -1}
			, {x: +1, y: 0}
			, {x: 0, y: +1}
			, {x: -1, y: 0}
		]
	}
	, "bishop": {
		caption: "♝"
		, offsets: [
			{x: -1, y: -1}
			, {x: +1, y: -1}
			, {x: +1, y: +1}
			, {x: -1, y: +1}
		]
	}
};

// 移动位置
Mickey.prototype.move = function(pos) {
	pos = pos || {};
	if (pos.x == this.pos.x && pos.y == this.pos.y) return;
	var floor = this.box.floors[this.pos.y + "," + this.pos.x];
	if (floor) floor.setState("blank");
	this.pos = pos;
	floor = this.box.floors[this.pos.y + "," + this.pos.x];
	floor.setState(this.name, this.arrow);
	this.freedom = floor.edge;
};

// 老鼠跑
Mickey.prototype.run = function() {
	this.flags = {};
	this.route = [];
	if (this.search(this.pos, this.route)) {
		var item = this.route.shift();
		this.arrow = item.arrow;
		this.move(item.pos);
		return true;
	}
};

// 搜索逃跑路径
Mickey.prototype.search = function(pos, route) {
	if (this.flags[pos.y + "," + pos.x]) return false;
	this.flags[pos.y + "," + pos.x] = true; // 标记已经扫描

	// 搜索直观的路径
	var directions = {}; // 每个方向的路径
	var min = -1; // 最短的距离
	for (var i = 0; i < this.offsets.length; i++) {
		directions[i] = [];
		for (var j = 1; ; j++) {
			var test = {
				x: pos.x + this.offsets[i].x * j
				, y: pos.y + this.offsets[i].y * j
			};
			var index = test.y + "," + test.x;
			if (!(test.x >= 0 && test.y >= 0
				&& test.x < this.box.colCount && test.y < this.box.rowCount 
				&& !this.flags[index] // 未搜索过
				&& this.box.floors[index].state == "blank")) {
				directions[i] = [];
				break;
			}
			directions[i].push(test);
			if (this.box.floors[index].edge) {
				if (min < 0 || directions[min].length > directions[i].length) min = i;
				break;
			}
		}
	}
	if (min >= 0) {
		for (var i = 0; i < directions[min].length; i++)
			route.push({pos: directions[min][i], arrow: min});
		return true;
	}

	// 回溯搜索
	var k = Math.floor(Math.random() * this.offsets.length);
	for (var i = 0; i < this.offsets.length; i++) {
		var j = (k + i) % this.offsets.length;
		var test = {
			x: pos.x + this.offsets[j].x
			, y: pos.y + this.offsets[j].y
		};
		var index = test.y + "," + test.x;
		if (test.x >= 0 && test.y >= 0
			&& test.x < this.box.colCount && test.y < this.box.rowCount 
			&& !this.flags[index] // 未搜索过
			&& this.box.floors[index].state == "blank") { // 非空地
			route.push({pos: test, arrow: j});
			if (this.box.floors[index].edge)
				return true;
			if (this.search(test, route)) return true;
			route.pop();
		}
	}
	return false;
};