﻿/*--
标题：中国象棋
设计：王集鹄
博客：http://blog.csdn.net/zswang
日期：2009年5月25日
--*/

//2009年06月06日 王集鹄 添加 将军时的警告图案
//2009年07月05日 王集鹄 添加 悔棋、和棋、认输功能
//2009年07月11日 王集鹄 添加 "视角"旋转棋盘功能
//2009年07月31日 王集鹄 修改 完善"棋谱"中"前中后"
//2009年08月17日 王集鹄 添加 扩展布局
//2009年08月30日 王集鹄 添加 打谱功能
//2009年09月01日 王集鹄 添加 布局功能
//2009年09月14日 王集鹄 添加 加入一统棋
//2010年03月05日 王集鹄 添加 不动不能吃、士相可升变规则

if (!Common.scripts["ChineseChess"]) {
	loadCss("/Scripts/ChineseChess.css?version=2010030401");
	Common.scripts["ChineseChess"] = true;
}

var CChessConsts = {
	classes: {
		"0": "space"
		,"1": "red1", "2": "red2", "3": "red3", "4": "red4", "5": "red5", "6": "red6", "7": "red7", "8": "red8", "9": "red9"
		,"-1": "black1", "-2": "black2", "-3": "black3", "-4": "black4", "-5": "black5", "-6": "black6", "-7": "black7", "-8": "black8", "-9": "black9"
		,"10": "red10", "11": "red11", "12": "red12", "13": "red13", "14": "red14"
		,"-10": "black10", "-11": "black11", "-12": "black12", "-13": "black13", "-14": "black14"
	}
	, initBorad: [
		  -5, -4, -3, -2, -1, -2, -3, -4, -5  
		, 00, 00, 00, 00, 00, 00, 00, 00, 00 
		, 00, -6, 00, 00, 00, 00, 00, -6, 00 
		, -7, 00, -7, 00, -7, 00, -7, 00, -7 
		, 00, 00, 00, 00, 00, 00, 00, 00, 00 
		, 00, 00, 00, 00, 00, 00, 00, 00, 00 
		, +7, 00, +7, 00, +7, 00, +7, 00, +7 
		, 00, +6, 00, 00, 00, 00, 00, +6, 00 
		, 00, 00, 00, 00, 00, 00, 00, 00, 00 
		, +5, +4, +3, +2, +1, +2, +3, +4, +5 
	]
	, boxRow: 10
	, boxCol: 6
	, chessChar: "zyxwvitpcrnmgk KGMNRCPTIVWXYZ"
	, initBox: [
		  -1, -7, -7, -7, -10, -10
		, -2, -2, -7, -7, -11, -11
		, -3, -3, -6, -6, -12, -12
		, -4, -4, -5, -5, -13, -13
		, -8, -8, -9, -9, -14, -14
		, +1, +7, +7, +7, +10, +10
		, +2, +2, +7, +7, +11, +11
		, +3, +3, +6, +6, +12, +12
		, +4, +4, +5, +5, +13, +13
		, +8, +8, +9, +9, +14, +14
	]	, names: {
		"0": " "
		, "1": "帥", "2": "仕", "3": "相", "4": "傌", "5": "俥", "6": "炮", "7": "兵", "8": "旗", "9": "铁"
		, "-1": "将", "-2": "士", "-3": "象", "-4": "马", "-5": "车", "-6": "包", "-7": "卒", "-8": "虎", "-9": "石"
		, "10": "仕", "11": "相", "12": "傌", "13": "兵", "14": "炮"
		, "-10": "士", "-11": "象", "-12": "马", "-13": "卒", "-14": "炮"
	}
	, numbers: {
		"0": "一二三四五六七八九"
		, "1": "１２３４５６７８９"
	}
	, grids: [
"┏┯┯┯┯┯┯┯┓",
"┠┼┼┼╳┼┼┼┨",
"┠╬┼┼┼┼┼╬┨",
"╠┼╬┼╬┼╬┼╣",
"┠┴┴┴┴┴┴┴┨",
"┠┬┬┬┬┬┬┬┨",
"╠┼╬┼╬┼╬┼╣",
"┠╬┼┼┼┼┼╬┨",
"┠┼┼┼╳┼┼┼┨",
"┗┷┷┷┷┷┷┷┛"
	]
};
 
function ChineseChess(parent, button_parent, manual_parent) {
	if (!parent) parent = document.body;
	if (!button_parent) button_parent = parent;
	var self = this;
	this.documentTitle = document.title;
	this.parent = parent;
	this.button_parent = button_parent;
	this.review = !!manual_parent;
	this.manual_parent = manual_parent;
	this.channel = {};
	this.state = "waiting"; // 游戏状态
	this.manuals = []; // 棋谱
	this.killed = {};  // 被杀的棋子
	this.updated = {}; // 升级的棋子
	this.whose = {}; // 棋谱名称
	this.flash = true; // 是否闪烁
	this.chess = {}; // 棋子列表
	this.types = {}; // 棋子类型
	this.movepoints = {}; // 可以移动的位置
	this.currPlace = 0; // 当前方位
	this.echoPlace = 0; // 当前执棋方
	this.currPlayer = -1; // 当前玩家执棋类型
	this.selection = -1;
	this.clock = 0; // 步时
	this.gameTime = {}; // 局时
	this.from = -1;
	this.to = -1;
	this.whose = { "-1": "开始" };
	this.div_desktop = document.createElement("div");
	this.div_desktop.className = "div_desktop";
	this.parent.appendChild(this.div_desktop);

	this.table_board = document.createElement("table");
	this.table_board.className = "table_board";
	this.div_desktop.appendChild(this.table_board);

	this.table_board.cellPadding = "0px";
	this.table_board.cellSpacing = "0px";
	var tr = this.table_board.insertRow(-1);
	this.numbers = {0: [], 1: []};
	for (var j = 0; j < 9; j++) {
		var td = tr.insertCell(-1);
		td.className = "td_number";
		this.numbers[0].push(td);
	}
	for (var i = 0; i < 10; i++) {
		var tr = this.table_board.insertRow(-1);
		for (var j = 0; j < 9; j++) {
			var td = tr.insertCell(-1);
			td.position = i * 9 + j;
			this.types[td.position] = CChessConsts.initBorad[td.position];
			this.chess[td.position] = new ChineseChessman(this, td.position, td);
		}
	}
	var tr = this.table_board.insertRow(-1);
	for (var j = 0; j < 9; j++) {
		var td = tr.insertCell(-1);
		td.className = "td_number";
		this.numbers[1].push(td);
	}
	
	this.div_selection = document.createElement("div");
	this.div_selection.className = "div_selection";

	this.div_from = document.createElement("div");
	this.div_from.className = "div_from";

	this.div_to = document.createElement("div");
	this.div_to.className = "div_to";

	if (!this.review) {
		this.button_start = document.createElement("input");
		this.button_start.type = "button";
		this.button_start.value = "我要下象棋";
		this.button_start.onclick = function() {
			publics.counter('click_start', location);
			this.disabled = true;
			if (typeof self.onaction == "function") self.onaction("apply", "");
		}
		this.button_parent.appendChild(this.button_start);

		this.button_discard = document.createElement("input");
		this.button_discard.type = "button";
		this.button_discard.value = "认输";
		this.button_discard.onclick = function() {
			publics.counter('click_discard', location);
			if (self.state != "playing" || self.currPlayer < 0) return;
			if (!confirm("确定是否认输？")) return;
			this.disabled = true;
			if (typeof self.onaction == "function") self.onaction("discard", "");
		}
		this.button_parent.appendChild(this.button_discard);
		
		this.button_repent = document.createElement("input");
		this.button_repent.type = "button";
		this.button_repent.value = "悔棋";
		this.button_repent.onclick = function() {
			publics.counter('click_repent', location);
			if (self.state != "playing" || self.currPlayer < 0) return;
			this.disabled = true;
			if (typeof self.onaction == "function") self.onaction("repent", "");
		}
		this.button_parent.appendChild(this.button_repent);

		this.button_peace = document.createElement("input");
		this.button_peace.type = "button";
		this.button_peace.value = "和棋";
		this.button_peace.onclick = function() {
			publics.counter('click_peace', location);
			if (self.state != "playing" || self.currPlayer < 0) return;
			this.disabled = true;
			if (typeof self.onaction == "function") self.onaction("peace", "");
		}
		this.button_parent.appendChild(this.button_peace);

		this.button_exit = document.createElement("input");
		this.button_exit.type = "button";
		this.button_exit.value = "我闪了，再见";
		this.button_exit.style.color = "Red";
		this.button_exit.onclick = function() {
			publics.counter('click_exit', location);
			if (self.state == "playing" && self.currPlayer != -1 && 
				!confirm("中途退出将导致游戏结束，是否确定？")) return;
			this.disabled = true;
			self.state = "waiting";
			self.currPlayer = -1;
			if (typeof self.onaction == "function") self.onaction("exit", "");
		}
		this.button_parent.appendChild(this.button_exit);
	} else {
		this.div_manual = document.createElement("div");
		this.div_manual.multiple = true;
		this.div_manual.className = "div_manual";
		this.manual_parent.appendChild(this.div_manual);

		this.button_first = document.createElement("input");
		this.button_first.type = "button";
		this.button_first.value = "最前";
		this.button_first.onclick = function() {
			publics.counter('click_first', location);
			self.navigator("first", true);
		}
		this.button_parent.appendChild(this.button_first);
		
		this.button_prior = document.createElement("input");
		this.button_prior.type = "button";
		this.button_prior.value = "向前";
		this.button_prior.onclick = function() {
			publics.counter('click_prior', location);
			self.navigator("prior", true);
		}
		this.button_parent.appendChild(this.button_prior);

		this.button_next = document.createElement("input");
		this.button_next.type = "button";
		this.button_next.value = "向后";
		this.button_next.onclick = function() {
			publics.counter('click_next', location);
			self.navigator("next", true);
		}
		this.button_parent.appendChild(this.button_next);

		this.button_last = document.createElement("input");
		this.button_last.type = "button";
		this.button_last.value = "最后";
		this.button_last.onclick = function() {
			publics.counter('click_last', location);
			self.navigator("last", true);
		}
		this.button_parent.appendChild(this.button_last);
	}
	
	this.button_clear = document.createElement("input");
	this.button_clear.type = "button";
	this.button_clear.value = "清空棋盘";
	this.button_clear.onclick = function() {
		publics.counter('click_clear', location);
		if (self.box) self.layout();
	}
	this.button_parent.appendChild(this.button_clear);

	this.button_init = document.createElement("input");
	this.button_init.type = "button";
	this.button_init.value = "原始布局";
	this.button_init.onclick = function() {
		publics.counter('click_init', location);
		if (self.box) eval("self.layout([" + initFormat(
			"ra1na2ma3ga4ka5ga6ma7na8ra9cc2cc8pd1pd3pd5pd7pd9Pg1Pg3Pg5Pg7Pg9Ch2Ch8Rj1Nj2Mj3Gj4Kj5Gj6Mj7Nj8Rj9") +"])");
	}
	this.button_parent.appendChild(this.button_init);

	this.button_angle = document.createElement("input");
	this.button_angle.type = "button";
	this.button_angle.value = "视角";
	this.button_angle.onclick = function() {
		publics.counter('click_angle', location);
		self.setCurrPlace((self.currPlace + 1) % 2);
	}
	this.button_parent.appendChild(this.button_angle);

	this.players = [{}, {}];
	this.players[0].div_player = document.createElement("div");
	this.players[0].div_player.className = "div_player";
	this.div_desktop.appendChild(this.players[0].div_player);
	
	this.players[1].div_player = document.createElement("div");
	this.players[1].div_player.className = "div_player";
	this.div_desktop.insertBefore(this.players[1].div_player, this.div_desktop.firstChild)
	
	this.players[0].div_gameTime = document.createElement("div");
	this.players[0].div_gameTime.className = "div_gameTime";
	this.div_desktop.appendChild(this.players[0].div_gameTime);

	this.players[1].div_gameTime = document.createElement("div");
	this.players[1].div_gameTime.className = "div_gameTime";
	this.div_desktop.appendChild(this.players[1].div_gameTime);
	
	this.div_clock = document.createElement("div");
	this.div_clock.className = "div_clock";
	this.div_desktop.appendChild(this.div_clock);

	this.updateChess();
	
	this.doChange();
	this.boardMousedown = function(e) {
		addEventHandler(self.table_board, "mouseup", self.boardMouseup);
		addEventHandler(self.table_board, "mousemove", self.boardMousemove);
	}
	this.boardMousemove = function(e) {
		if (window.getSelection)
			getSelection().removeAllRanges();
		else if (document.selection && document.selection.empty)
			try { document.selection.empty(); } catch (ex) { }
	}
	this.boardMouseup = function(e) {
		removeEventHandler(self.table_board, "mousemove", self.boardMousemove);
		removeEventHandler(self.table_board, "mouseup", self.boardMouseup);
	}
	addEventHandler(this.table_board, "mousedown", this.boardMousedown);
}

ChineseChess.prototype.absPosition = function(position) {
	return this.currPlace == 0 ? position : 10 * 9 - position - 1;
}

ChineseChess.prototype.play = function(manuals, inits) {
	var self = this;
	this.end = "";
	this.echoPlace = 0;
	this.from = -1;
	this.to = -1;
	this.selection = -1;
	if (inits) this.inits = inits;
	this.doInit();
	this.manuals = [];
	this.killed = {};
	this.updated = {};
	this.whose = { "-1": "开始" };
	this.moves(manuals);
	if (this.illustrate || this.research) this.currPlayer = this.echoPlace;
	if (this.research) this.state = "playing";
	
	for (var j in this.div_navigators)
		this.div_navigators[j].parentNode.removeChild(this.div_navigators[j]);
	this.div_navigators = {};
	if (this.review) {
		var l = manuals ? manuals.length : 0;
		for (var j = -1; j < l; j++)
			this.addNavigator(j);
		this.reviewIndex = l - 1;
		this.div_navigators[this.reviewIndex].style.backgroundColor = "#316AC5";
		this.div_navigators[this.reviewIndex].style.color = "White";
	}
	this.updateJiang();
	this.updateChess();
	this.updateMovepoints();
	this.doChange();
	if (typeof this.onplay == "function") this.onplay();
}

ChineseChess.prototype.addNavigator = function(index) {
	var div = document.createElement("div");
	var self = this;
	this.div_navigators[index] = div;
	div.innerHTML = "第" + (index < 9 ? " " : "") + (index + 1) + "步 "
		+ this.whose[index]
		+ (this.killed[index] ? " ☠" : "")
	div.style.backgroundColor = index % 2 ? "#eee" : "";
	div.style.cursor = "pointer";
	div.index = index;
	div.onclick = function() {
		self.navigator(this.index);
	}
	this.div_manual.appendChild(div);
}

ChineseChess.prototype.updateJiang = function() {
	this.hasJiang = !this.TryJiang((this.echoPlace + 1) % 2) && this.TryJiang(this.echoPlace);
}

ChineseChess.prototype.TryJiang = function(place) {
	var start = place == 0 ? 7 : 0;
	var step = place == 0 ? -1 : +1;
	var offset = [{ x: +1, y: 00 }, { x: -1, y: 00 }, { x: 00, y: +1 }, { x: 00, y: -1 }];
	var offsetX = [{ x: +1, y: +1 }, { x: -1, y: -1 }, { x: +1, y: -1 }, { x: -1, y: +1 }];
	var offset8 = [
		  { 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 }
	];
	var position;
	var mx, my, i;

	for (var x = 3; x <= 5; x++)
		for (var y = start;	y <= start + 2; y++) {
			if (Math.abs(this.types[y * 9 + x]) == 1) {
				// 1 帅 是否将对面
				my = y + step;
				mx = x;
				while (my >= 0 && my <= 9) {
					position = my * 9 + mx;
					if (this.types[position] != 0) {
						if (this.types[position] == 1 * step)
							return true;
						break;
					}
					my += step;
				}
				
				// 4 马
				for (i = 0; i < offset8.length; i++) {
					var check = Math.floor(i / 2);
					mx = x + offsetX[check].x;
					my = y + offsetX[check].y;
					if (mx < 0 || mx >= 9 || my < 0 || my >= 10) continue;
					if (this.types[my * 9 + mx] != 0) continue; // 中间有棋子
					mx = x + offset8[i].x;
					my = y + offset8[i].y;
					if (mx < 0 || mx >= 9 || my < 0 || my >= 10) continue;
					position = my * 9 + mx;
					if (this.types[position] == 4 * step || this.types[position] == 8 * step || this.types[position] == 12 * step)
						return true;
				}

				// 5 車
				for (i = 0; i < offset.length; i++) {
					mx = x + offset[i].x;
					my = y + offset[i].y;
					while (mx >= 0 && mx < 9 && my >= 0 && my < 10) {
						position = my * 9 + mx;
						if (this.types[position] != 0) {
							if (this.types[position] == 5 * step || this.types[position] == 8 * step)
								return true;
							break;
						}
						mx += offset[i].x;
						my += offset[i].y;
					}
				}

				// 6 炮
				for (i = 0; i < offset.length; i++) {
					mx = x + offset[i].x;
					my = y + offset[i].y;
					var kill = false;
					while (mx >= 0 && mx < 9 && my >= 0 && my < 10) {
						position = my * 9 + mx;
						if (this.types[position] != 0) {
							if (kill) {
								if (this.types[position] == 6 * step || this.types[position] == 14 * step || 
									this.types[position] == 8 * step)
									return true;
								break;
							}
							kill = true;
						}
						mx += offset[i].x;
						my += offset[i].y;
					}
				}

				// 7 兵
				for (i = 0; i < offset.length; i++) {
					if (offset[i].y == -step) continue;
					mx = x + offset[i].x;
					my = y + offset[i].y;
					if (mx < 0 || mx >= 9 || my < 0 || my >= 10) continue;
					position = my * 9 + mx;
					if (this.types[position] == 7 * step || this.types[position] == 13 * step)
						return true;
				}
			}
		}
	return false;
}
	
ChineseChess.prototype.navigator = function(index, scroll) {
	if (!this.review) return;
	switch (index) {
		case "first": index = -1; break;
		case "prior": index = this.reviewIndex - 1; break;
		case "next": index = this.reviewIndex + 1; break;
		case "last": index = this.manuals.length - 1; break;
	}
	if (index < -1 || index >= this.manuals.length) return;
	if (this.reviewIndex == index) return;
	var oldIndex = this.reviewIndex;
	this.reviewIndex = index;
	this.selection = -1;
	this.echoPlace = (this.reviewIndex + 1) % 2;
	if (this.research) this.currPlayer = this.echoPlace;

	this.from = -1;
	this.to = -1;
	this.doInit()
	for (var i = 0; i <= this.reviewIndex; i++) {
		this.from = this.manuals[i][0];
		this.to = this.manuals[i][1];
		var chess = this.types[this.from];
		switch (Math.abs(chess)) {
			case 12:
				chess = sign(chess) * 4;
				break;
			case 13:
				chess = sign(chess) * 7;
				break;
			case 14:
				chess = sign(chess) * 6;
				break;
		}
		switch (Math.abs(chess)) {
			case 10: case 11: 
				if (this.to == this.from) {
					this.types[this.to] = sign(chess) * 7;
					break;
				}
			default:
				this.types[this.to] = chess;
				this.types[this.from] = 0;
				break;
		}
	}
	this.updateJiang();
	this.updateMovepoints();
	this.updateChess();

	this.div_navigators[oldIndex].style.backgroundColor = oldIndex % 2 ? "#eee" : "";
	this.div_navigators[oldIndex].style.color = "";

	this.div_navigators[this.reviewIndex].style.backgroundColor = "#316AC5";
	this.div_navigators[this.reviewIndex].style.color = "White";
	//if (scroll) this.div_navigators[this.reviewIndex].scrollIntoView(true);
	if (typeof this.onnavigator == "function") this.onnavigator();
	this.doChange();
}

ChineseChess.prototype.moves = function(manuals) {
	if (!manuals) return;
	var l = this.manuals.length;
	for (i = 0; i < manuals.length; i++)
		this.move(manuals[i][0], manuals[i][1], false);
	this.updateJiang();
	this.updateMovepoints();
	this.updateChess();
	this.doChange();
	if (l == this.manuals.length) return;
	if (this.killed[this.manuals.length - 1])
		playSound("kill");
	else playSound("move");
	if (this.hasJiang) playSound("jiang");
	if (typeof this.onmoves == "function") this.onmoves();
}

ChineseChess.prototype.doChange = function() {
	if (typeof this.onchange == "function") this.onchange();
	if (this.currPlayer != -1) {
		if (this.state == "playing")
			this.channel.unloadmessage = "中途退出将导致游戏结束。";
		else this.channel.unloadmessage = "退出将导致举手失效。";
	} else this.channel.unloadmessage = null;
	
	this.div_selection.style.display = "none";
	this.div_from.style.display = "none";
	this.div_to.style.display = "none";
	if (this.to != this.from && this.from >= 0 && this.selection != this.from) {
		this.chess[this.absPosition(this.from)].td.appendChild(this.div_from);
		this.div_from.style.display = "";
	}
	if (this.to >= 0 && this.selection != this.to) {
		this.chess[this.absPosition(this.to)].td.appendChild(this.div_to);
		this.div_to.style.display = "";
	}
	  
	var place = this.currPlace;
	this.players[0].div_player.innerHTML = this.players[place].username ? 
		ubb2Html( 
			this.players[place].flag + this.players[place].username + 
			" [f]财富:[/f]" + this.players[place].money) : "";
	place = (place + 1) % 2;
	this.players[1].div_player.innerHTML = this.players[place].username ? 
		ubb2Html(
			this.players[place].flag + this.players[place].username +
			" [f]财富:[/f]" + this.players[place].money) : "";

	if (this.selection >= 0) {
		this.chess[this.absPosition(this.selection)].td.appendChild(this.div_selection);
		this.div_selection.style.display = "";
	}

	this.button_clear.style.display = this.box ? "" : "none";
	this.button_init.style.display = this.box ? "" : "none";

	if (this.review) {
		this.button_first.disabled = this.manuals.length <= 0 || this.reviewIndex < 0;
		this.button_prior.disabled = this.manuals.length <= 0 || this.reviewIndex < 0;
		this.button_next.disabled = this.manuals.length <= 0 || this.reviewIndex >= this.manuals.length - 1;
		this.button_last.disabled = this.manuals.length <= 0 || this.reviewIndex >= this.manuals.length - 1;
		return;
	}

	if (this.state == "playing") {
		var point = absolutePoint(this.players[0 ^ this.currPlace].div_player);
		this.players[0].div_gameTime.style.left = (point.x + 386) + "px";
		this.players[0].div_gameTime.style.top = point.y + "px";
		point = absolutePoint(this.players[1 ^ this.currPlace].div_player);
		this.players[1].div_gameTime.style.left = (point.x + 386) + "px";
		this.players[1].div_gameTime.style.top = point.y + "px";
		
		this.div_clock.style.display = "block";
		this.players[0].div_gameTime.style.display = "";
		this.players[1].div_gameTime.style.display = "";
		point = absolutePoint(this.players[this.echoPlace ^ this.currPlace].div_player);
		this.div_clock.style.left = point.x + "px";
		this.div_clock.style.top = point.y + "px";
		if (this.clock > 0)
			this.div_clock.innerHTML = ubb2Html("　　[b][size=12px]"
				+ (this.gameTime[this.echoPlace] > 0 ? "步时" : "读秒")
				+ "[/size][/b]" + formatTime(this.clock));
		else this.div_clock.style.innerHTML = "";
		
		if (this.gameTime[0] > 0)
			this.players[0].div_gameTime.innerHTML = ubb2Html("[b][size=12px]局时[/size][/b]" + formatTime(this.gameTime[0]));
		else this.players[0].div_gameTime.innerHTML = "";
		if (this.gameTime[1] > 0)
			this.players[1].div_gameTime.innerHTML = ubb2Html("[b][size=12px]局时[/size][/b]" + formatTime(this.gameTime[1]));
		else this.players[1].div_gameTime.innerHTML = "";
	} else {
		this.div_clock.style.display = "none";
		this.players[0].div_gameTime.style.display = "none";
		this.players[1].div_gameTime.style.display = "none";
	}
	
	this.button_start.disabled = this.state != "waiting" || 
		!this.channel.entering || this.currPlayer >= 0;
	this.button_exit.disabled = !this.channel.entering;
	this.button_discard.disabled = !this.channel.entering || this.currPlayer < 0 || this.state != "playing";
	this.button_repent.disabled = !this.channel.entering || this.currPlayer < 0 || this.state != "playing"
		|| this.manuals.length <= 0 || this.currPlayer == this.repentPlace;
	this.button_peace.disabled = !this.channel.entering || this.currPlayer < 0 || this.state != "playing"
		|| this.currPlayer == this.peacePlace;

	this.button_start.style.display = this.box || (this.currPlayer >= 0 && this.state != "waiting") ? "none" : "";
	this.button_discard.style.display = this.currPlayer >= 0 && this.state != "waiting" ? "" : "none";
	this.button_repent.style.display = this.currPlayer >= 0 && this.state != "waiting" ? "" : "none";
	this.button_peace.style.display = this.currPlayer >= 0 && this.state != "waiting" ? "" : "none";
	this.button_exit.style.display = this.box ? "none" : "";

	if (this.state == "playing" && this.echoPlace == this.currPlayer) {
		if (self.flash)
			document.title = "【请执棋】" + this.documentTitle;
		else document.title = "【　　　】" + this.documentTitle;
	} else document.title = document.title = this.documentTitle.replace(/^【.*?】/, "");
}

ChineseChess.prototype.setCurrPlayer = function(player) {
	if (this.currPlayer == player) return;
	this.currPlayer = player;
	this.updateChess();
	this.doChange();
}

ChineseChess.prototype.setCurrPlace = function(place) {
	if (this.place == place) return;
	this.currPlace = place;
	this.updateChess();
	this.doChange();
	if (typeof this.onangle == "function") this.onangle();
}

ChineseChess.prototype.updateChess = function() {
	for (var j = 0; j < 9; j++) {
		this.numbers[0][j].innerHTML = CChessConsts.numbers[(this.currPlace + 1) % 2].charAt(j);
		this.numbers[1][j].innerHTML = CChessConsts.numbers[this.currPlace].charAt(8 - j);
	}
	for (var i = 0; i < 10; i++) {
		for (var j = 0; j < 9; j++) {
			this.chess[i * 9 + j].doChange();
		}
	}
}

ChineseChess.prototype.updateMovepoints = function() {
	this.movepoints = {};
	if (this.box) {
		if (this.selection < 0 && this.box.selection < 0) return;
		for (var i = 0; i < 10; i++) {
			for (var j = 0; j < 9; j++) {
				var position = i * 9 + j;
				if (this.types[position] == 0)
					this.movepoints[position] = "move";
			}
		}
		return;
	}
	
	if (this.selection < 0) return;
	if (this.echoPlace != this.currPlayer) return;
	var chess = this.types[this.selection];
	var x = this.selection % 9;
	var y = Math.floor(this.selection / 9);
	var offset = [{ x: +1, y: 00 }, { x: -1, y: 00 }, { x: 00, y: +1 }, { x: 00, y: -1 }];
	var offsetX = [{ x: +1, y: +1 }, { x: -1, y: -1 }, { x: +1, y: -1 }, { x: -1, y: +1 }];
	var offset8 = [
		  { x: +2, y: -1 }
		, { x: +2, y: +1 }
		, { x: -2, y: -1 }
		, { x: -2, y: +1 }
		, { x: +1, y: +2 }
		, { x: -1, y: +2 }
		, { x: -1, y: -2 }
		, { x: +1, y: -2 }
	];
	var position;
	var mx, my, i;
	switch (Math.abs(chess)) {
		case 1: // 帅
			for (i = 0; i < offset.length; i++) {
				mx = x + offset[i].x;
				my = y + offset[i].y;
				if (mx < 3 || mx > 5 || my < 0 || my >= 10) continue;
				if (chess > 0 && my < 7) continue;
				if (chess < 0 && my > 2) continue;
				position = my * 9 + mx;
				if (this.types[position] == 0) {
					this.movepoints[position] = "move";
				} else {
					if (sign(this.types[position]) != sign(chess) && !/^(9|12|13|14)$/.test(Math.abs(this.types[position])))
						this.movepoints[position] = "kill";
				}
			}
			// 见面
			i = chess > 0 ? 3 : 2;
			mx = x + offset[i].x;
			my = y + offset[i].y;
			while (mx >= 0 && mx < 9 && my >= 0 && my < 10) {
				position = my * 9 + mx;
				if (this.types[position] != 0) {
					if (this.types[position] == -chess)
						this.movepoints[position] = "kill";
					break;
				}
				mx += offset[i].x;
				my += offset[i].y;
			}
			break;
		case 2: case 10: // 士
			for (i = 0; i < offsetX.length; i++) {
				mx = x + offsetX[i].x;
				my = y + offsetX[i].y;
				if (mx < 3 || mx > 5 || my < 0 || my >= 10) continue;
				if (chess > 0 && my < 7) continue;
				if (chess < 0 && my > 2) continue;
				position = my * 9 + mx;
				if (this.types[position] == 0) {
					this.movepoints[position] = "move";
				} else {
					if (sign(this.types[position]) != sign(chess) && !/^(9|12|13|14)$/.test(Math.abs(this.types[position])))
						this.movepoints[position] = "kill";
				}
			}
			break;
		case 3: case 11:// 象
			for (i = 0; i < offsetX.length; i++) {
				mx = x + offsetX[i].x;
				my = y + offsetX[i].y;
				if (mx < 0 || mx >= 9 || my < 0 || my >= 10) continue;
				if (this.types[my * 9 + mx] != 0) continue; // 中间有棋子
				mx += offsetX[i].x;
				my += offsetX[i].y;
				if (mx < 0 || mx > 9) continue;
				if (chess > 0 && my < 5) continue;
				if (chess < 0 && my > 4) continue;
				position = my * 9 + mx;
				if (this.types[position] == 0) {
					this.movepoints[position] = "move";
				} else {
					if (sign(this.types[position]) != sign(chess) && !/^(9|12|13|14)$/.test(Math.abs(this.types[position])))
						this.movepoints[position] = "kill";
				}
			}
			break;
		case 4: case 12: // 马
			for (i = 0; i < offset8.length; i++) {
				var check = Math.floor(i / 2);
				mx = x + offset[check].x;
				my = y + offset[check].y;
				if (mx < 0 || mx >= 9 || my < 0 || my >= 10) continue;
				if (this.types[my * 9 + mx] != 0) continue; // 中间有棋子
				mx = x + offset8[i].x;
				my = y + offset8[i].y;
				if (mx < 0 || mx >= 9 || my < 0 || my >= 10) continue;
				position = my * 9 + mx;
				if (this.types[position] == 0) {
					this.movepoints[position] = "move";
				} else {
					if (sign(this.types[position]) != sign(chess) && !/^(9|12|13|14)$/.test(Math.abs(this.types[position])))
						this.movepoints[position] = "kill";
				}
			}
			break;
		case 5: // 車
			for (i = 0; i < offset.length; i++) {
				mx = x + offset[i].x;
				my = y + offset[i].y;
				while (mx >= 0 && mx < 9 && my >= 0 && my < 10) {
					position = my * 9 + mx;
					if (this.types[position] == 0) {
						this.movepoints[position] = "move";
					} else {
						if (sign(this.types[position]) != sign(chess) && !/^(9|12|13|14)$/.test(Math.abs(this.types[position])))
							this.movepoints[position] = "kill";
						break;
					}
					mx += offset[i].x;
					my += offset[i].y;
				}
			}
			break;
		case 6: case 14: // 炮
			for (i = 0; i < offset.length; i++) {
				mx = x + offset[i].x;
				my = y + offset[i].y;
				var kill = false;
				while (mx >= 0 && mx < 9 && my >= 0 && my < 10) {
					position = my * 9 + mx;
					if (this.types[position] == 0) {
						if (!kill) this.movepoints[position] = "move";
					} else {
						if (kill) {
							if (sign(this.types[position]) != sign(chess) && !/^(9|12|13|14)$/.test(Math.abs(this.types[position])))
								this.movepoints[position] = "kill";
							break;
						}
						kill = true;
					}
					mx += offset[i].x;
					my += offset[i].y;
				}
			}
			break;
		case 7: case 13: // 兵
			for (i = 0; i < offset.length; i++) {
				if (chess > 0 && (offset[i].y > 0 || y > 4 && offset[i].x != 0)) continue;
				if (chess < 0 && (offset[i].y < 0 || y < 5 && offset[i].x != 0)) continue;
				mx = x + offset[i].x;
				my = y + offset[i].y;
				if (mx >= 0 && mx < 9 && my >= 0 && my < 10) {
					position = my * 9 + mx;
					if (this.types[position] == 0) {
						this.movepoints[position] = "move";
					} else {
						if (sign(this.types[position]) != sign(chess) && !/^(9|12|13|14)$/.test(Math.abs(this.types[position])))
							this.movepoints[position] = "kill";
					}
				}
			}
			break;
		case 8: // 虎
		    // 马
			for (i = 0; i < offset8.length; i++) {
				var check = Math.floor(i / 2);
				mx = x + offset[check].x;
				my = y + offset[check].y;
				if (mx < 0 || mx >= 9 || my < 0 || my >= 10) continue;
				if (this.types[my * 9 + mx] != 0) continue; // 中间有棋子
				mx = x + offset8[i].x;
				my = y + offset8[i].y;
				if (mx < 0 || mx >= 9 || my < 0 || my >= 10) continue;
				position = my * 9 + mx;
				if (this.types[position] == 0) {
					this.movepoints[position] = "move";
				} else {
					if (sign(this.types[position]) != sign(chess) && !/^(9|12|13|14)$/.test(Math.abs(this.types[position])))
						this.movepoints[position] = "kill";
				}
			}
			// 車
			for (i = 0; i < offset.length; i++) {
				mx = x + offset[i].x;
				my = y + offset[i].y;
				while (mx >= 0 && mx < 9 && my >= 0 && my < 10) {
					position = my * 9 + mx;
					if (this.types[position] == 0) {
						this.movepoints[position] = "move";
					} else {
						if (sign(this.types[position]) != sign(chess) && !/^(9|12|13|14)$/.test(Math.abs(this.types[position])))
							this.movepoints[position] = "kill";
						break;
					}
					mx += offset[i].x;
					my += offset[i].y;
				}
			}
			// 炮
			for (i = 0; i < offset.length; i++) {
				mx = x + offset[i].x;
				my = y + offset[i].y;
				var kill = false;
				while (mx >= 0 && mx < 9 && my >= 0 && my < 10) {
					position = my * 9 + mx;
					if (this.types[position] == 0) {
						if (!kill) this.movepoints[position] = "move";
					} else {
						if (kill) {
							if (sign(this.types[position]) != sign(chess) && !/^(9|12|13|14)$/.test(Math.abs(this.types[position])))
								this.movepoints[position] = "kill";
							break;
						}
						kill = true;
					}
					mx += offset[i].x;
					my += offset[i].y;
				}
			}
			break;
	}
}

ChineseChess.prototype.setSelection = function(position) {
	if (this.selection == position) return;
	this.selection = position;
	if (this.box && this.selection != -1) {
		this.box.selection = 0;
		this.box.setSelection(-1);
	}
	playSound("select");
	this.updateMovepoints();
	this.updateChess();
	this.doChange();
}

ChineseChess.prototype.setFrom = function(position) {
	if (this.from == position) return;
	this.from = position
	this.doChange();
}

ChineseChess.prototype.setTo = function(position) {
	if (this.to == position) return;
	this.to = position
	this.doChange();
}

ChineseChess.prototype.move = function(from, to, event, test) {
	if (from < 0 || to < 0) return;
	var fromPoint = { x: from % 9, y: Math.floor(from / 9) };
	var chess = this.types[from];
	if (!chess) return;
	if (!((Math.abs(chess) == 10 || Math.abs(chess) == 11) && (fromPoint.y == 0 || fromPoint.y == 9) && from == to)) {
		if (sign(chess) == sign(this.types[to])) return;
	}
	if (test) return true; // 只是测试

	if (this.research && this.reviewIndex < this.manuals.length - 1) {
		for (var i = this.reviewIndex + 1; i < this.manuals.length; i++) {
			if (!this.div_navigators[i]) continue;
			this.div_manual.removeChild(this.div_navigators[i]);
			delete this.updated[i];
			delete this.killed[i];
			delete this.div_navigators[i];
		}
		this.manuals.length = this.reviewIndex + 1;
	}

	this.from = from;
	this.to = to;
	this.selection = -1;
	if (this.types[this.to]) this.killed[this.manuals.length] = this.types[this.to];
	var toPoint = { x: to % 9, y: Math.floor(to / 9) };
	var echo = this.manuals.length % 2;
	if (echo == 0) {
		fromPoint.x = 8 - fromPoint.x;
		toPoint.x = 8 - toPoint.x;
		fromPoint.y = 9 - fromPoint.y;
		toPoint.y = 9 - toPoint.y;
	}
	var first = "";
	var history = "";
	if (/^(12|13|14)$/.test(Math.abs(chess))) this.updated[this.manuals.length] = chess;
	switch (Math.abs(chess)) {
		case 12:
			chess = sign(chess) * 4;
			break;
		case 13:
			chess = sign(chess) * 7;
			break;
		case 14:
			chess = sign(chess) * 6;
			break;
	}
	if (Math.abs(chess) == 8) { // 旗
		chess = sign(chess) * 5; // 車
		if (fromPoint.x != toPoint.x && fromPoint.y != toPoint.y) {
			chess = sign(chess) * 4; // 马
		} else if (fromPoint.x == toPoint.x) {
			var min = Math.min(fromPoint.y, toPoint.y);
			var max = Math.max(fromPoint.y, toPoint.y);
			for (var y = min; y <= max; y++) {
				var position = y * 9 + fromPoint.x;
				if (this.types[position] != 0) {
					chess = sign(chess) * 6; // 炮
					break;
				}
			}
		} else if (fromPoint.y == toPoint.y) {
			var min = Math.min(fromPoint.x, toPoint.x);
			var max = Math.max(fromPoint.x, toPoint.x);
			for (var x = min; x <= max; x++) {
				var position = fromPoint.y * 9 + x;
				if (this.types[position] != 0) {
					chess = sign(chess) * 6; // 炮
					break;
				}
			}
		}
	}

	if (!/^(1|2|3|11|12)$/.test(Math.abs(chess)))
		for (var y = 0; y < 10; y++) {
		var position = y * 9 + this.from % 9;
		if (this.from == position) continue;
		if (this.types[position] == chess) {
			if (y < Math.floor(this.from / 9))
				first = "后前".charAt(echo);
			else first = "前后".charAt(echo);
			if (Math.abs(chess) != 7) break;
			if (history != "" && history != first) {
				first = "中";
				break;
			}
			history = first;
		}
	}

	this.whose[this.manuals.length] = first + CChessConsts.names[chess].replace(/[帥傌俥包]/, function($1) {
		return { "帥": "帅", "傌": "马", "俥": "车", "包": "炮"}[$1];
	}) +
		(first ? "" : CChessConsts.numbers[echo].charAt(fromPoint.x)) +
		(to == from ? "升变" :
			(
				(fromPoint.y == toPoint.y ? "平" : (fromPoint.y < toPoint.y ? "进" : "退")) +
				(fromPoint.y == toPoint.y ? CChessConsts.numbers[echo].charAt(toPoint.x) :
					(/^(2|3|4|10|11|12)$/.test(Math.abs(chess)) ?
						CChessConsts.numbers[echo].charAt(toPoint.x) :
						CChessConsts.numbers[echo].charAt(Math.abs(fromPoint.y - toPoint.y) - 1)
					)
				)
			)
		);

	switch (Math.abs(chess)) {
		case 10: case 11:
			if (from == to) {
				this.updated[this.manuals.length] = chess;
				this.types[this.to] = sign(chess) * 7;
				break;
			}
		default:
			if (this.updated[this.manuals.length]) // 如果发生升变
				this.types[this.to] = chess;
			else this.types[this.to] = this.types[this.from];
			this.types[this.from] = 0;
			break;
	}
	this.manuals.push([from, to]);
	this.echoPlace = this.manuals.length % 2;
	if (this.illustrate || this.research) this.currPlayer = this.echoPlace;
	if (event) {
		if (this.research) {
			this.addNavigator(this.manuals.length - 1);
			this.navigator(this.manuals.length - 1);
			return;
		}
		if (this.killed[this.manuals.length - 1])
			playSound("kill");
		else playSound("move");
		this.updateJiang();
		this.updateMovepoints();
		this.updateChess();
		this.doChange();
		if (this.hasJiang) playSound("jiang");
		return true;
	}
}

ChineseChess.prototype.dispose = function() {
	this.disposed = true;
	for (var i in this) {
		if (this[i].disposed) continue;
		if (typeof this[i].dispose == "function") this[i].dispose();
		if (typeof this[i].parentNode == "object")
			this[i].parentNode.removeChild(this[i]);
		delete this[i];
	}
}

ChineseChess.prototype.action = function(action, param, result) {
	if (!result) return;
	var self = this;
	var playerChanged = false;
	for (var i = 0; i < 2; i++) {
		if (typeof result["p" + i] != "undefined") {
			playerChanged = true;
			this.players[i].id = result["p" + i].id;
			if (this.currPlayer == i) this.currPlayer = -1;
			if (typeof this.players[i].id == "undefined" || parseInt(this.players[i].id) == -1) {
				this.players[i].username = "";
				this.players[i].flag = "";
				this.players[i].money = 0;
			} else {
				this.players[i].username = result["p" + i].username;
				this.players[i].flag = result["p" + i].flag;
				this.players[i].money = result["p" + i].money;
				if (this.players[i].id == publics.passinfo.ident) {
					this.currPlayer = i;
					this.currPlace = i;
				}
			}
		}
	}
	if (playerChanged) {
		this.illustrate = this.currPlayer >= 0 && this.players[0].id == this.players[1].id;
		if (this.illustrate) this.currPlayer = this.echoPlace;
		this.updateMovepoints();
		this.updateChess();
	}
	
	if (result.selection) this.setSelection(parseInt(result.selection));
	if (result.inits) {
		this.inits = result.inits;
		this.doInit();
		this.updateJiang();
		this.updateMovepoints();
		this.updateChess();
	}

	if (typeof result.state != "undefined") {
		this.state = result.state;
		self.flash = true;
		clearInterval(this.timer);
		if (this.state != "waiting") {
			this.play([], this.inits);
			this.timer = setInterval(
				function() {
					self.flash = !self.flash;
					if (self.state != "waiting" && self.echoPlace == self.currPlayer) {
						if (self.flash)
							document.title = "【请执棋】" + self.documentTitle;
						else document.title = "【　　　】" + self.documentTitle;
					}
					if (self.clock > 0) {
						self.clock--;
						self.div_clock.innerHTML = ubb2Html("　　[b][size=12px]"
							+ (self.gameTime[self.echoPlace] > 0 ? "步时" : "读秒")
							+ "[/size][/b]" + formatTime(self.clock));
					}
					if (self.gameTime[self.echoPlace] > 0) {
						self.gameTime[self.echoPlace]--;
						self.players[self.echoPlace].div_gameTime.innerHTML = ubb2Html("[b][size=12px]局时[/size][/b]" + 
							formatTime(self.gameTime[self.echoPlace]));
					}
				}, 1000);
		} else self.flash = true;
	}
	if (typeof result.repentPlace != "undefined") this.repentPlace = result.repentPlace;
	if (typeof result.peacePlace != "undefined") this.peacePlace = result.peacePlace;
	if (typeof result.manualCount != "undefined") 
		this.repent(parseInt(result.manualCount) - (result.manuals ? result.manuals.length : 0));
	if (typeof result.manuals != "undefined") this.moves(result.manuals);
	if (typeof result.clock != "undefined") this.clock = result.clock;
	if (typeof result.gt0 != "undefined") this.gameTime[0] = result.gt0;
	if (typeof result.gt1 != "undefined") this.gameTime[1] = result.gt1;
	this.doChange();
}

ChineseChess.prototype.repent = function(count) {
	count = Math.max(0, count);
	if (this.manuals.length <= count) return;
	for (var i = this.manuals.length - 1; i >= count; i--) {
		var moved = this.manuals[i];
		this.types[moved[0]] = this.types[moved[1]];
		if (this.updated[i]) {
			this.types[moved[0]] = this.updated[i];
			this.updated[i] = null;
		} else this.types[moved[0]] = this.types[moved[1]];
		if (this.killed[i]) {
			this.types[moved[1]] = this.killed[i];
			this.killed[i] = null;
		} else this.types[moved[1]] = 0;
	}
	this.manuals.length = count;
	if (this.manuals.length > 0) {
		this.from = this.manuals[this.manuals.length - 1][0];
		this.to = this.manuals[this.manuals.length - 1][1];
	} else this.from = this.to = -1;
	this.echoPlace = this.manuals.length % 2;
	this.selection = -1;
	this.updateJiang();
	this.updateChess();
	this.updateMovepoints();
	this.doChange();
}

ChineseChess.prototype.setPlayers = function(p0, p1) {
	if (!this.review) return;
	this.players[0].username = p0.username;
	this.players[0].flag = p0.flag;
	this.players[0].money = p0.money;
	this.players[1].username = p1.username;
	this.players[1].flag = p1.flag;
	this.players[1].money = p1.money;
	this.doChange();
}

ChineseChess.prototype.setEnd = function(end) {
	if (!this.review) return;
	var div = document.createElement("div");
	this.end = end;
	div.innerHTML = ubb2Html("[color=#ff00ff]" + end + "[/color]");
	div.style.backgroundColor = this.manuals.length % 2 ? "#eee" : "";
	this.div_navigators[this.manuals.length] = div;
	this.div_manual.appendChild(div);
}

ChineseChess.prototype.manualText = function() {
	var result = "";
	if (this.players[0].username)
		result += "先手：" + this.players[0].username + " VS 后手：" + this.players[1].username + "\n";
	var l = this.review ? this.reviewIndex + 1 : this.manuals.length;
	for (var i = 0; i < l; i++) {
		if (i % 2 == 0)	result += (i + 2) / 2 + ". ";
		result += this.whose[i];
		if (i % 2 == 1)
			result += "\n";
		else result += " ";
	}
	if (l % 2 == 1) result += "\n";
	if (l == this.manuals.length && this.end) result += this.end + "\n";
	return result;
}

ChineseChess.prototype.manualCode = function() {
	var result = "";
	var l = this.review ? this.reviewIndex + 1 : this.manuals.length;
	for (var i = 0; i < l; i++) {
		result += String.fromCharCode("a".charCodeAt(0) + Math.floor(this.manuals[i][0] / 9)) + (this.manuals[i][0] % 9 + 1); 
		result += String.fromCharCode("a".charCodeAt(0) + Math.floor(this.manuals[i][1] / 9)) + (this.manuals[i][1] % 9 + 1);
	}
	return result;
}

ChineseChess.prototype.initCode = function() {
	var result = "";
	for (var i = 0; this.inits && i < this.inits.length; i++) {
		result += CChessConsts.chessChar.charAt(this.inits[i][0] + Math.floor(CChessConsts.chessChar.length / 2));
		result += String.fromCharCode("a".charCodeAt(0) + Math.floor(this.inits[i][1] / 9)) + (this.inits[i][1] % 9 + 1);
	}
	return result;
}

ChineseChess.prototype.currInitCode = function() {
	var result = "";
	for (var i = 0; i < 9 * 10; i++) {
		if (this.types[i] == 0) continue;
		result += CChessConsts.chessChar.charAt(this.types[i] + Math.floor(CChessConsts.chessChar.length / 2));
		result += String.fromCharCode("a".charCodeAt(0) + Math.floor(i / 9)) + (i % 9 + 1);
	}
	return result;
}

ChineseChess.prototype.doInit = function() {
	if (this.inits && this.inits.length > 0) {
		for (var i = 0; i < 10 * 9; i++)
			this.types[i] = 0;
		for (var i = 0; i < this.inits.length; i++)
			this.types[this.inits[i][1]] = this.inits[i][0];
	} else {
		for (var i = 0; i < 10 * 9; i++)
			this.types[i] = CChessConsts.initBorad[i];
	}
	this.selection = -1;
	this.from = -1;
	this.to = -1;
}

ChineseChess.prototype.initTypes = function() {
	var result = {};
	if (this.inits && this.inits.length > 0) {
		for (var i = 0; i < 10 * 9; i++)
			result[i] = 0;
		for (var i = 0; i < this.inits.length; i++)
			result[this.inits[i][1]] = this.inits[i][0];
	} else {
		for (var i = 0; i < 10 * 9; i++)
			result[i] = CChessConsts.initBorad[i];
	}
	return result;
}

ChineseChess.prototype.buildText = function(ubb, types) {
	if (!types) types = this.types;
	var result = "";
	result += ubb ? "[color=#c0c0c0]" : "";
	for (var j = 0; j < 9; j ++)
		result += CChessConsts.numbers[(this.currPlace + 1) % 2].charAt(j);
	result += ubb ? "[/color]" : "";
	result += "\n";

	var buffer = new Array(10);
	for (var i = 0; i < buffer.length; i++) {
		buffer[i] = new Array(9);
		for (var j = 0; j < buffer[i].length; j++) {
			var position = this.absPosition(i * 9 + j);
			if (types[position] == 0)
				buffer[i][j] = CChessConsts.grids[i].charAt(j);
			else if (ubb)
				buffer[i][j] = "[color=" + (types[position] > 0 ? "#800000" : "#008000") + "]" +
					CChessConsts.names[types[position]] + "[/color]";
			else buffer[i][j] = CChessConsts.names[types[position]];
		}
	}

	for (var i = 0; i < buffer.length; i++)
		result += buffer[i].join("") + "\n";

	result += ubb ? "[color=#c0c0c0]" : "";
	for (var j = 0; j < 9; j ++)
		result += CChessConsts.numbers[this.currPlace].charAt(8 - j);
	result += ubb ? "[/color]" : "";
	result += "\n";
	if (ubb)
		return compressUbb(result);
	else return result;
}

ChineseChess.prototype.layout = function(inits) {
	if (this.box) for (var i = 0; i < 9 * 10; i++)
		this.types[i] = 0;
	if (this.box) for (var i = 0; i < CChessConsts.boxCol * CChessConsts.boxRow; i++)
		this.box.types[i] = CChessConsts.initBox[i];
	for (var i = 0; inits && i < inits.length; i++) {
		this.types[inits[i][1]] = inits[i][0];
		if (this.box) this.box.remove(inits[i][0]);
	}
	this.updateChess();
	this.updateMovepoints();
	this.doChange();
	if (this.box) this.box.doChange();
}

function ChineseChessman(board, position, td) {
	this.board = board; // 棋盘
	this.position = position; // 所在坐标
	this.td = td;

	var self = this;
	this.td.ondblclick = function() {
		var position = self.board.absPosition(self.position);
		var chess = self.board.types[position]; // 棋子类型
		if (chess == 0) return;
		if (!self.board.box) {
			if (self.board.echoPlace != self.board.currPlayer) return;
			var y = Math.floor(position / 9);
			if (y != 0 && y != 9) return;
			switch (Math.abs(chess)) {
				case 10: case 11:
					var selection = self.board.selection;
					if (!self.board.move(self.board.selection, self.board.selection, true, !self.board.research)) return;
					if (typeof self.board.onaction == "function")
						self.board.onaction("move",	selection + "," + selection);
					break;
			}
			return;
		}
		self.board.box.append(chess);
		self.board.types[position] = 0;
		self.board.selection = position;
		self.board.setSelection(-1);
	}
	this.td.onclick = function() {
		if (self.board.state != "playing" && !self.board.box) return;
		var position = self.board.absPosition(self.position);
		var chess = self.board.types[position]; // 棋子类型
		if (self.board.echoPlace != self.board.currPlayer && !self.board.box) return;
		if (self.board.movepoints[position]) {
			if (self.board.box) {
				if (self.board.box.selection != -1) {
					self.board.types[position] = self.board.box.types[self.board.box.selection];
					self.board.box.types[self.board.box.selection] = 0;
					self.board.box.setSelection(-1);
					self.board.selection = 0;
					self.board.setSelection(-1);
				} else {
					if (self.board.selection < 0) return;
					self.board.types[position] = self.board.types[self.board.selection];
					self.board.types[self.board.selection] = 0;
					self.board.setSelection(-1);
				}
				return;
			}
			if (self.board.selection >= 0) {
				var from = self.board.selection;
				var to = position;
				if (!self.board.move(from, to, true, !self.board.research)) return;
				if (typeof self.board.onaction == "function")
					self.board.onaction("move",	from + "," + to);
			}
			return;
		}
		if ((self.board.box && chess != 0) || (self.board.currPlayer == 0 ? +1 : -1) == sign(chess)) {
			self.board.setSelection(position);
			if (typeof self.board.onaction == "function") self.board.onaction("select",
				self.board.selection);
		}
	}
	this.doChange();
}

ChineseChessman.prototype.doChange = function() {
	var position = this.board.absPosition(this.position);
	var chess = this.board.types[position]; // 棋子类型
	var currPlayer = this.board.currPlayer;
	var echoPlace = this.board.echoPlace;
	var moveType = this.board.movepoints[position];
	var className = "chess ";
	if (chess == (echoPlace == 0 ? +1 : -1) && this.board.hasJiang) {
		className += echoPlace == 0 ? "redJiang" : "blackJiang";
	} else className += CChessConsts.classes[chess];
	if (this.board.box || (this.board.state == "playing" && currPlayer == echoPlace)) {
		switch (moveType) {
			case "move":
				className += " cur_move";
				break;
			case "kill":
				className += " cur_kill";
				break;
			default:
				if ((this.board.box && chess != 0) || (currPlayer == 0 ? +1 : -1) == sign(chess))
					className += " cur_drag";
				break;
		}
	}
	if (className != this.td.className) this.td.className = className;
}

function ChineseBox(parent, board) {
	var self = this;
	if (!parent) parent = document.body;
	this.board = board; // 棋盘
	this.selection = -1;
	if (this.board) {
		this.board.box = this;
		for (var i = 0; i < 9 * 10; i++)
			this.board.types[i] = 0;
		this.board.selection = 0;
		this.board.setSelection(-1);
	}
	this.types = {};
	this.parent = parent;
	this.table_box = document.createElement("table");
	this.table_box.className = "table_box";
	this.table_box.cellPadding = "0px";
	this.table_box.cellSpacing = "0px";
	this.parent.appendChild(this.table_box);
	this.tds = {};
	for (var i = 0; i < CChessConsts.boxRow; i++) {
		var tr = this.table_box.insertRow(-1);
		for (var j = 0; j < CChessConsts.boxCol; j++) {
			var td = tr.insertCell(-1);
			td.position = i * CChessConsts.boxCol + j;
			this.types[td.position] = CChessConsts.initBox[td.position];
			this.tds[td.position] = td;
			td.onclick = function() {
				if (self.types[this.position] != 0)
					self.setSelection(this.position);
				else if (self.board.selection >= 0 &&
					self.board.types[self.board.selection] == CChessConsts.initBox[this.position]) {
					self.types[this.position] = CChessConsts.initBox[this.position];
					self.board.types[self.board.selection] = 0;
					self.board.box.selection = 0;
					self.board.box.setSelection(-1);
					self.board.setSelection(-1);
				}
			}
		}
	}
	this.div_selection = document.createElement("div");
	this.div_selection.className = "div_selection";

	this.doChange();
}

ChineseBox.prototype.setSelection = function(position) {
	if (this.selection == position) return;
	this.selection = position;
	if (this.board && this.selection != -1) {
		this.board.selection = 0;
		this.board.setSelection(-1);
	}
	this.doChange();
}

ChineseBox.prototype.doChange = function() {
	for (var i = 0; i < CChessConsts.boxCol * CChessConsts.boxRow; i++) {
		var className = "chess";
		if (this.types[i] != 0)
			className += " cur_drag";
		else if (this.board && this.board.selection >= 0 &&
			this.board.types[this.board.selection] == CChessConsts.initBox[i])
			className += " cur_move";

		className += " " + CChessConsts.classes[this.types[i]];
		if (className != this.tds[i].className) this.tds[i].className = className;
	}
	if (this.selection >= 0) {
		this.tds[this.selection].appendChild(this.div_selection);
		this.div_selection.style.display = "";
	} else this.div_selection.style.display = "none";
}

ChineseBox.prototype.append = function(chess) {
	if (chess == 0) return;
	for (var i = 0; i < CChessConsts.boxCol * CChessConsts.boxRow; i++) {
		if (this.types[i] == 0 && CChessConsts.initBox[i] == chess) {
			this.types[i] = chess;
			break;
		}
	}
	this.doChange();
}

ChineseBox.prototype.remove = function(chess) {
	if (chess == 0) return;
	for (var i = 0; i < CChessConsts.boxCol * CChessConsts.boxRow; i++) {
		if (this.types[i] != 0 && CChessConsts.initBox[i] == chess) {
			this.types[i] = 0;
			break;
		}
	}
	this.doChange();
}

function manualFormat(str) {
	if (/^(([0-8][0-9]){2})+$/i.test(str)) {
		return str.replace(/([0-8])([0-9])([0-8])([0-9])/gi, function($0, $1, $2, $3, $4) {
			var from = parseInt($1) + parseInt($2) * 9;
			var to = parseInt($3) + parseInt($4) * 9;
			return "[" + from + "," + to + "],";
		}).replace(/^,|,$/g, "");
	}

	if (/^(([a-j][1-9]){2})+$/i.test(str)) {
		return str.replace(/([a-j])([1-9])([a-j])([1-9])/gi, function($0, $1, $2, $3, $4) {
			var from = ($1.toLowerCase().charCodeAt(0) - "a".charCodeAt(0)) * 9 + parseInt($2) - 1;
			var to = ($3.toLowerCase().charCodeAt(0) - "a".charCodeAt(0)) * 9 + parseInt($4) - 1;
			return "[" + from + "," + to + "],";
		}).replace(/^,|,$/g, "");
	}
	return str;
}

function initFormat(str) {
	if (/^\d{64}$/.test(str)) {
		var chesses = [5, 4, 3, 2, 1, 2, 3, 4, 5, 6, 6, 7, 7, 7, 7, 7, -5, -4, -3, -2, -1, -2, -3, -4, -5, -6, -6, -7, -7, -7, -7, -7];
		var result = [];
		for (var i = 0; i < 32; i++) {
			var pos = [+str.substr(i * 2, 1), +str.substr(i * 2 + 1, 1)];
			if (pos[0] == 9 && pos[1] == 9) continue;
			result.push("[" + chesses[i], [pos[0] + pos[1] * 9] + "]");
		}
		return result.join();
    }
	if (/[\dkabnrcp]+(\/[\dkabnrcp]+){9}\s+(w|b|r)/i.test(str)) {
		var temp = str.replace(/\d/g, function(num) {
			return new Array(+num + 1).join("0");
		});
		var match = /[0kabnrcp]{9}(\/[0kabnrcp]{9}){9}\s+(w|b|r)/i.exec(temp);
		if (!match) return str;
		temp = match[0].split(/\/| /);
		var result = [];
		var black = /b/i.test(temp[10]);
		for (var i = 0; i < 10; i++) {
			var s = temp[i];
			for (var j = 0; j < 9; j++) {
				var k = s.charAt(j);
				if (k == "0") continue;
				var chess = "pcrnbak0KABNRCP".indexOf(k) - 7;
				var pos = i * 9 + j;
				if (black) {
					chess = -chess;
					pos = 9 * 10 - pos - 1;
				}
				result.push("[" + [chess, pos] + "]");
			}
		}
		return result.join();
	}
	if (/^([kgmnrcptivwxyz][a-j][1-9])+$/i.test(str)) {
		return str.replace(/([kgmnrcptivwxyz])([a-j])([1-9])/gi, function($0, $1, $2, $3) {
			var chess = CChessConsts.chessChar.indexOf($1) - Math.floor(CChessConsts.chessChar.length / 2);
			var pos = ($2.toLowerCase().charCodeAt(0) - "a".charCodeAt(0)) * 9 + parseInt($3) - 1;
			return "[" + chess + "," + pos + "],";
		}).replace(/^,|,$/g, "");
	}
	return str;
}