Pos 0.1

囲碁の局面を表すオブジェクト Pos で3路盤の局面を作成し、テキストと $\TeX$ で内容を表示するようにしました。これらの式の意味は「作って遊ぼう Java」連載時に作成した数式一覧をご覧下さい。

実行結果

テキストでの表示

$\TeX$での表示

ソース

"expand source" を押すと展開されます。元に戻すには、このページを再ロードしてください。

pos01.html

この HTML 内の JavaScript で書かれた Pos オブジェクトのテストプログラムです。
/**
 * 結果表示用の関数です。HTMLとして展開します。
 * @since 0.1
 */
function writeln(str) {
	var s = 0;
	var e;
	while ((e = str.indexOf("\n", s)) > 0) {
		document.writeln(str.substring(s, e) + "
\n"); s = e + 1; } document.writeln(str.substr(s) + "
\n"); } /** * メインです。 * @since 0.1 */ document.writeln("\n"); writeln("pos = new Pos(3);"); pos = new Pos(3); writeln("pos.move(2, 2);"); pos.move(2, 2); writeln("pos.move(1, 2);"); pos.move(1, 2); writeln(pos.toPicStr()); document.writeln("\n"); writeln(pos.toTeX()); writeln(pos.v6.toTeX());

move01.js

着手を表す Move オブジェクトの定義です。
/*
 * Copyright (C) 2012 たかはしのんき. All rights reserved.
 *
 * History:
 *  0.1 2012/05/15 新規作成。Java 版より移植。
 */

/**
 * 盤面の着手位置を表すクラスです。
 * @author たかはしのんき
 * @version 0.1
 */
var RESIGN = 0;
/**
 * 着手のコンストラクタです。盤面の桁位置と行位置を指定します。
 * @param col 桁位置
 * @param row 行位置
 * @since 0.1
 */
Move = function(col, row) {
	this.col = col;
	this.row = row;
};

Move.prototype = {
	/**
	 * 路数からパスの値を計算します。定数にできなかったので...。
	 * @param ro 路数を与えます。
	 * @return パスの値を返します。
	 * @since 0.1
	 */
	pass : function(ro) {
		return ro + 1;
	},

	/**
	 * 着手がパスかどうかを返します。
	 * @param ro 路数を与えます。
	 * @return パスなら true を返します。
	 * @since 0.1
	 */
	isPass : function(ro) {
		if ((this.col == pass(ro)) && (this.row == pass(ro)))
			return true;
		else
			return false;
	},

	/**
	 * 着手が投了かどうかを返します。
	 * @return 投了なら true を返します。
	 * @since 0.1
	 */
	isResign : function() {
		if ((this.col == RESIGN) && (this.row == RESIGN))
			return true;
		else
			return false;
	}
};

pos01.js

囲碁の局面 (position) を表す Pos オブジェクトの定義です。
/*
 * Copyright (C) 2012 たかはしのんき. All rights reserved.
 *
 * History:
 *  0.1 2012/05/15 新規作成。Java 版より移植。
 *
 * Reference:
 *  [1]佐藤真史,穴田浩一,堤正義:囲碁の数理モデル化とその応用,
 *     第16回ゲームプログラミングワークショップ2011,Vol.2011 No.6,
 *     pp.100-103(2011)
 */

/**
 * 囲碁の局面 (position) を B-W graph model で表したオブジェクト
 * です。 このオブジェクトで扱う メソッドの引数に盤面の桁位置 col と行位置
 * row を 指定する場合は、囲碁に合わせ、col, row の順で指定します。
 * 行列の行 i、 列 j を指定する場合の i, j の順と逆になるので注意して
 * ください。
 * @author たかはしのんき
 * @version 0.1
 */
var COL = 0;		// 横座標
var ROW = 1;		// 縦座標
var	SPACE = 0;		// 空点
var BLACK = 1;		// 黒石
var WHITE = 2;		// 白石
var P_BLACK = 0;	// 黒石(アゲハマ用)
var P_WHITE = 1;	// 白石(アゲハマ用)
var OPPOSITE = 3;	// 敵石計算用定数

/**
 * n 路盤の局面を表すオブジェクトです。
 * @param n 路数を指定します。
 * @since 0.1
 */
Pos = function(n) {
//	this.h = new BVector(order);					// アタリ
//	this.A = new BMatrix(order, order);				// 追加される連接関係
//	this.D = new BMatrix(order, order)				// リセットされる連接関係
//	this.a = new BVector(order);					// 追加される連
//	this.d = new BVector(order);					// リセットされる連

	this.ro = n;									// 路数
	this.order = n * n;								// ベクトルの次数
	this.F0 = new BMatrix(this.order, this.order);	// 隣接関係
	this.initF0();
	this.F = this.F0.clone();						// 連接関係
	this.b = new BVector(this.order, 1);			// 黒の着点∨空点
	this.w = this.b.clone();						// 白の着点∨空点
	this.l = this.b.and(this.w);					// 空点
	this.calcVectors(0, 0);
	this.v6 = new SixVectors(this);					// 6ベクトル
	this.v6.calcSixVectors(this);
	this.watch = null;								// 監視する BVector
	this.turn = BLACK;								// 次の番
	this.moves = 0;									// 手数
	this.prisoner = new Array(2);					// アゲハマ
	this.prisoner[P_BLACK] = 0;
	this.prisoner[P_WHITE] = 0;
};

Pos.prototype = {
	/**
	 * 隣接関係 F0 を初期化します。
	 * @since 0.1
	 */
	initF0 : function() {
		var adj = [			// 隣接する(adjacent)相対座標
				[0, -1],
				[-1, 0], [0, 0], [1, 0],
				[0, 1]
		];
		for (var row = 1; row <= this.ro; row++)
			for (var col = 1; col <= this.ro; col++)
				for (var i = 0; i < adj.length; i++) {
					var ar = row + adj[i][ROW];
					var ac = col + adj[i][COL];
					if (ar > 0 && ar <= this.ro && ac > 0 && ac <= this.ro)
						this.F0.setValue(this.toIndex(col, row), this.toIndex(ac, ar), 1);
				}
	},

	/**
	 * 監視する BVector を設定します。
	 * @param watch 監視する BVector を指定します。 null なら監視を解除します。
	 * @since 0.1
	 */
	setWatch : function(watch) {
		this.watch = watch;
	},

	/**
	 * 監視する BVector を返します。
	 * @return 監視する BVector を返します。なければ null を返します。
	 * @since 0.1
	 */
	getWatch : function() {
		return this.watch;
	},

	/**
	 * 碁盤上の点 (move) をベクトル要素の添字 (index) に変換します。
	 * @param col 点の横方向の座標
	 * @param row 点の縦方向の座標
	 * @return 添字を返します。(1 origin)
	 * @since 0.1
	 */
	toIndex : function(col, row) {
		return (row - 1) * this.ro + col;
	},

	/**
	 * ベクトル要素の添字 (index) を碁盤上の点 (move) に変換します。
	 * @param index 添字を渡します。 (1 origin)
	 * @return 碁盤上の点の座標をクラス Move のオブジェクトとして返します。
	 * @since 0.1
	 */
	toMove : function(index) {
		var mv = new Move();
		mv.row = (index - 1) / this.ro + 1;
		mv.col = (index - 1) % this.ro + 1;
		return mv;
	},

	/**
	 * turn の反対のプレイヤーを返します。
	 * @param turn BLACK か WHITE を指定します。
	 * @return turn の反対のプレイヤーを返します。
	 * @since 0.1
	 */
	opposite : function(turn) {
		return OPPOSITE - turn;
	},

	/**
	 * 着手により局面を更新します。 col と row のいずれかを ro+1 にするとパスできます。
	 * @param col 着手の横座標
	 * @param row 着手の縦座標
	 * @return 着手できたら true、着手できない手なら false を返します。
	 * @since 0.1
	 */
	move : function(col, row) {
		var mv = new Move(0, 0);
		if (col != mv.pass(this.ro) && row != mv.pass(this.ro)) {
			if (!this.isMovable(col, row))
				return false;
			this.calcVectors(col, row);
			this.v6.calcSixVectors(this);
		}
		this.turn = this.opposite(this.turn);
		this.moves++;
		return true;
	},

	/**
	 * 与えられた交点に着手できるかどうかを調べます。
	 * @param col 調べる交点の桁位置
	 * @param row 調べる交点の行位置
	 * @return 着手可能なら true を返します。
	 * @since 0.1
	 */
	isMovable : function(col, row) {
		if (this.h == null) {
			this.h = new BVector(this.order);
		}
		var tF = this.F.tran();
		for (var i = 1; i <= this.order; i++) {
			var ei = new BVector(this.order);
			ei.setValue(i, 1);
			var tFei = tF.mul(ei); 
			if ((tFei.and(this.l)).abs() == 1)
				this.h.setValue(i, 1);
			else
				this.h.setValue(i, 0);
		}
		if (row == 0 && col == 0)
			return false;
		var o = new BVector(this.order);
		if (this.turn == BLACK)
			o = this.F.mul(this.b.xor(this.h)).and(this.l);
		else if (this.turn == WHITE)
			o = this.F.mul(this.w.xor(this.h)).and(this.l);
		else
			return false;
		var i = this.toIndex(col, row);	// 調べる着点
		var ei = new BVector(this.order);
		ei.setValue(i, 1);
		if (o.and(ei).equals(ei))
			return true;
		return false;
	},

	/**
	 * BWモデルに関連するベクトルと行列を計算します。計算の対象は以下のとおり。<br>
	 * l - 空点を表すベクトル<br>
	 * h - アタリの石の集合を表すベクトル<br>
	 * a - 着手によって形成される連接点を表すベクトル<br>
	 * d - 着手によって削除される石を表すベクトル<br>
	 * A - 着手によって追加される連接点を表す行列<br>
	 * D - 着手によってリセットされる連接点を表す行列<br>
	 * b - 着手後の黒石∨空点を表すベクトル<br>
	 * w - 着手後の白石∨空点を表すベクトル<br>
	 * F - 着手後の連接点を表す行列
	 * @param col 着手した点の横座標
	 * @param row 着手した点の縦座標
	 * @since 0.1
	 */
	calcVectors : function(col, row) {
		if (this.h == null) {
			this.h = new BVector(this.order);
		}
		var tF = this.F.tran();					// Fの転置
		for (var i = 1; i <= this.order; i++) {
			var ei = new BVector(this.order);
			ei.setValue(i, 1);
			var tFei = tF.mul(ei); 
			if ((tFei.and(this.l)).abs() == 1)
				this.h.setValue(i, 1);
			else
				this.h.setValue(i, 0);
		}
		if (row ==0 && col==0)
			return;
		var i = this.toIndex(col, row);				// 着点
		var ei = new BVector(this.b.order);
		ei.setValue(i, 1);
		var F0ei = this.F0.mul(ei);					// 着点の隣接点
		// adj_: adjacent 隣接する
		// uap: unit adjacent points 連接点
		// e: enemy 敵石
		var adj_h = F0ei.and(this.h);				// 着点に接するアタリの石
		if (this.turn == BLACK) {
			var adj_b = (F0ei.diff(this.w)).or(ei);		// 着点と接する黒石
			var adj_buap = tF.mul(adj_b);				// の黒石の連接点
			this.a = (adj_buap.diff(this.w)).or(ei);	// の黒石と着点
			var adj_he = adj_h.diff(this.b);			// 着点に接する敵石のアタリ
			var adj_heuap = tF.mul(adj_he);				// 着点に接する敵石のアタリの連接点
			this.d = adj_heuap.diff(this.b);
			this.b = this.b.or(this.d);
			this.w = this.w.diff(ei);
			this.prisoner[P_BLACK] += this.d.abs();
		} else if (this.turn == WHITE) {
			var adj_w = (F0ei.diff(this.b)).or(ei);		// 着点と接する白石
			var adj_wuap = tF.mul(adj_w);				// の白石の連接点
			this.a = (adj_wuap.diff(this.b)).or(ei);	// の白石と着点
			var adj_he = adj_h.diff(this.w);			// 着点に接する敵石のアタリ
			var adj_heuap = tF.mul(adj_he);				// 着点に接する敵石のアタリの連接点
			this.d = adj_heuap.diff(this.w);
			this.b = this.b.diff(ei);
			this.w = this.w.or(this.d);
			this.prisoner[P_WHITE] += this.d.abs();
		}
		this.A = this.a.cross(tF.mul(this.a));
		var o = new BVector(this.b.order, 1);			// all 1(one)
		this.D = this.F0.or((this.d.inv()).cross(o));
		this.F = (this.F.or(this.A)).and(this.D);
		this.l = this.b.and(this.w);
	},

	/**
	 * 局面の座標より、石または空点を返します。エラーのときは -1 を返します。
	 * @param col 横座標
	 * @param row 縦座標
	 * @return SPACE, BLACK, WHITE のいずれかを返します。 
	 * @since 0.1
	 */
	getStone : function(col, row) {
		if (row <= 0 || row > this.ro || col <=0 || col > this.ro)
			return -1;
		var b1 = this.b.getValue(this.toIndex(col, row));
		var w1 = this.w.getValue(this.toIndex(col, row));
		var stone = ((b1 & w1) == 1) ? SPACE : ((b1 == 1) ? BLACK : WHITE);
		return stone;
	},

	/* 局面の状態を文字列に変換します。
	 * @return 文字列を返します。
	 * @since 0.1
	 */
	toString : function() {
		var turn = ["-", "B", "W"];
		var str = new String();
		str = "#" + this.moves + "\n";
		str += "F'=\n" + this.F.toString();
		str += "b'=" + this.b.toString() + "\n";
		str += "w'=" + this.w.toString() + "\n";
		str += "l=" + this.l.toString() + "\n";
		str += "h=" + this.h.toString() + "\n";
		str += "Σ|db|=" + this.prisoner[P_BLACK] + "\n";
		str += "Σ|dw|=" + this.prisoner[P_WHITE] + "\n";
		if (this.moves > 0) {
			str += "a=" + this.a.toString() + "\n";
			str += "d=" + this.d.toString() + "\n";
			str += "A=\n" + this.A.toString();
			str += "D=\n" + this.D.toString();
		}
		str += "turn:" + turn[this.turn] + "\n";
		return str;
	},

	/* 局面の状態を TeX 文字列に変換します。
	 * @return TeX 文字列を返します。
	 * @since 0.1
	 */
	toTeX : function() {
		var turn = ["-", "B", "W"];
		var str = new String();
		str = "#" + this.moves + "\n";
		str += "$$F'=" + this.F.toTeX() +"$$\n";
		str += "$$\\boldsymbol{b}'=" + this.b.toTeX() + "$$\n";
		str += "$$\\boldsymbol{w}'=" + this.w.toTeX() + "$$\n";
		str += "$$\\boldsymbol{l}=" + this.l.toTeX() + "$$\n";
		str += "$$\\boldsymbol{h}=" + this.h.toTeX() + "$$\n";
		str += "$$\\sum \\left| \\boldsymbol{d}_b \\right| =" + this.prisoner[P_BLACK] + "$$\n";
		str += "$$\\sum \\left| \\boldsymbol{d}_w \\right| =" + this.prisoner[P_WHITE] + "$$\n";
		if (this.moves > 0) {
			str += "$$\\boldsymbol{a}=" + this.a.toTeX() + "$$\n";
			str += "$$\\boldsymbol{d}=" + this.d.toTeX() + "$$\n";
			str += "$$A=" + this.A.toTeX() + "$$\n";
			str += "$$D=" + this.D.toTeX() + "$$\n";
		}
		str += "turn:" + turn[this.turn] + "\n";
		return str;
	},

	/* 局面の状態を絵文字列に変換します。
	 * @return 絵文字列を返します。
	 * @since 0.1
	 */
	toPicStr : function() {
		var figure = ["┼", "●", "○"];
		var grid = ["┏", "┯", "┓", "┠", "┼", "┨", "┗", "┷", "┛"];
		var str = new String();
		str = "";
		for (var row = 1; row <= this.ro; row++) {
			for (var col = 1; col <= this.ro; col++) {
				var stone = this.getStone(col, row);
				if (stone == SPACE) {
					var rx = Math.ceil((row - 1) / (this.ro - 1) * 2);
					var cx = Math.ceil((col - 1) / (this.ro - 1) * 2);
					str += grid[rx * this.ro + cx];
				} else
					str += figure[stone];
			}
			str += "\n";
		}
		return str;
	}
};

sixvectors01.js

囲碁のルールに関する6ベクトルを表す SixVectors オブジェクトの定義です。
/*
 * Copyright (C) 2012 たかはしのんき. All rights reserved.
 *
 * History:
 *  0.1 2012/05/15 新規作成。Java 版より移植。
 *  
 * Reference:
 *  [2]日本棋院:日本囲碁規約,日本棋院(1989)
 */

/**
 * 6ベクトルの計算をするクラスです。活き石以外は『日本囲碁規約』の定義にしたがって計算します。
 * @author たかはしのんき
 * @version 0.1
 */
var adjH = new Array(2);	// 水平方向に (horizontal) 隣接する (adjacent) 相対座標
adjH = [ [-1, 0], [1, 0] ];
var adjV = new Array(2);	// 垂直方向に (vertical) 隣接する (adjacent) 相対座標
adjV = [ [0, -1],
		 [0, 1] ];

/**
 * 6ベクトル計算クラスのコンストラクタです。局面によらないベクトルを予め計算します。
 * @param p 局面を指定します。ベクトルの次数と、隣接点F0を利用します。
 * @since 0.1
 */
SixVectors = function(p) {
	this.order = p.order;					// ベクトルの次数
	this.F0 = p.F0.clone();					// 隣接点
	this.zero = new BVector(this.order);	// 0ベクトル
	this.one = this.zero.inv();				// 1ベクトル
	this.e = new Array(this.order + 1);		// 単位ベクトル ei の配列
	for (var i = 1; i <= this.order; i++) {
		this.e[i] = new BVector(this.order);
		this.e[i].setValue(i, 1);
	}
	this.H1 = this.calcJx(p.ro, adjH);		// 水平方向のツケ
	this.V1 = this.calcJx(p.ro, adjV);		// 垂直方向のツケ
	this.J1 = this.H1.or(this.V1);			// ツケ
	this.D2 = this.H1.mul(this.V1);			// コスミ
};
	
SixVectors.prototype = {
	/**
	 * 現在の局面における6ベクトルを計算します。
	 * @param p 現在の局面を指定します。
	 * @since 0.1
	 */
	calcSixVectors : function(p) {
		var l = p.b.and(p.w);
		this.v = this.calcV(p.F, l, p.b, p.w);						// (1)活き石
		this.x = this.calcX(l, this.v);								// (2)死に石
		this.Sx = this.calcSx(l, this.x);							// 空接点
		this.y = this.calcY(this.Sx, l, this.v, p.b, p.w);			// (3)目
		this.m = this.calcM(l, this.y);								// (4)ダメ
		this.s = this.calcS(p.F, this.v, this.Sx, this.m, l);		// (5)セキ石
		this.t = this.calcT(this.Sx, l, this.v, p.b, p.w, this.s);	// (6)地
		this.cb = this.eyeCan(p.b, p.w, this.x, BLACK);				// 黒石の眼の候補地
		this.cw = this.eyeCan(p.b, p.w, this.x, WHITE);				// 白石の眼の候補地
	},

	/**
	 * 与えられた方向の隣接関係 Jx (同一点を含まず)を計算します。
	 * @param ro 路数を与えます。
	 * @param adj 隣接する方向の相対座標を与えます。
	 * @return 隣接関係 Jx を返します。
	 * @since v0.16
	 */
	calcJx : function(ro, adj) {
		var Jx = new BMatrix(ro * ro, ro * ro);
		for (var row = 1; row <= ro; row++)
			for (var col = 1; col <= ro; col++)
				for (var i = 0; i < adj.length; i++) {
					var ar = row + adj[i][ROW];
					var ac = col + adj[i][COL];
					if (ar > 0 && ar <= ro && ac > 0 && ac <= ro)
						Jx.setValue(this.toIndex(col, row, ro), this.toIndex(ac, ar, ro), 1);
				}
		return Jx;
	},

	/**
	 * 盤面の桁と行からベクトルの要素番号を計算します。
	 * @param col 桁
	 * @param row 行
	 * @param ro 路数
	 * @return 要素番号を返します。
	 * @since 0.1
	 */
	toIndex : function(col, row, ro) {
		return (row - 1) * ro + col;
	},

	/**
	 * (1)活き石を計算します。
	 * 「相手方の着手により取られない石、又は取られても新たに相手方に取られない石を生じうる石」
	 * @param F 連接点行列を与えます。
	 * @param l 空点を与えます。
	 * @param b 黒石を与えます。
	 * @param w 白石を与えます。
	 * @return 活き石を返します。
	 * @since v0.14
	 */
	calcV : function(F, l, b, w) {
		// とりあえず全ての石を活き石としておく。
		var v = l.inv();										// (1)活き石
		// 拡張しても出口(活路)が増えない石を死に石と仮定して差し引きます。
		// 拡張の際に眼をつぶさないようにしたい。
		// 最初に眼の候補地を計算しておく。
		var cb = this.eyeCan(b, w, this.zero, BLACK);
		var cw = this.eyeCan(b, w, this.zero, WHITE);
		// TODO 1次の拡張しかしていないが、2次以上も要検討。
		// まず黒石について計算する。
		var done = new BVector(this.order);
		for (var i = 1; i <= this.order; i++) {
			if (w.getValue(i) == 1 || done.getValue(i) == 1)
				continue;						// 黒石でない  または すでに計算済み
			var b0 = ((F.tran()).mul(this.e[i])).diff(w);	// 黒石の連
			done = done.or(b0);								// 計算済み
			var l0 = ((this.F0.mul(b0)).and(l)).abs();		// 黒石の出口の数
			var b1 = (this.F0.mul(b0)).diff(w);				// 黒石の連の1次拡張
			var b2 = (this.F0.mul(b1)).diff(w);				// 黒石の連の2次拡張
			if ((b1.diff(cb)).equals(b0) &&		// 1次拡張では眼がつぶれも、
				!(b2.diff(cb)).equals(b0))		// 2次拡張で2眼確保可なら、
					b1 = b2.diff(cb);			// それを拡張とする。
			// このときに一間先の黒石と連結する可能性あるので、そのときはその連も取り込む。
			var sb1 = ((this.F0.mul(b1)).diff(b1)).diff(w);	// 拡張した連の隣に
			if (sb1.abs() > 0)								// 黒石があるとき、
				b1 = b1.or(((F.tran()).mul(sb1)).diff(w));	// その連も加える。
			// TODO この拡張により敵石(白石)が取れてしまっても、とりあえず放置しておく。
			var l1 = ((this.F0.mul(b1)).diff(b1.and(w))).abs();	// 拡張後の出口の数
			if (l1 <= l0)									// 出口が増えないとき、
				v = v.diff(b0);								// 死に石とする。
		}
		// 白石についても計算する。
		done.clear();
		for (var i = 1; i <= this.order; i++) {
			if (b.getValue(i) == 1 || done.getValue(i) == 1)
				continue;						// 白石でない  または すでに計算済み
			var w0 = ((F.tran()).mul(this.e[i])).diff(b);	// 白石の連
			done = done.or(w0);								// 計算済み
			var l0 = ((this.F0.mul(w0)).and(l)).abs();		// 白石の出口の数
			var w1 = (this.F0.mul(w0)).diff(b);				// 白石の連の拡張
			var w2 = (this.F0.mul(w1)).diff(b);				// 白石の連の2次拡張
			if ((w1.diff(cw)).equals(w0) &&		// 1次拡張では眼がつぶれも、
				!(w2.diff(cw)).equals(w0))		// 2次拡張で2眼確保可なら、
					w1 = w2.diff(cw);			// それを拡張とする。
			// このときに一間先の白石と連結する可能性あるので、そのときはその連も取り込む。
			var sw1 = ((this.F0.mul(w1)).diff(w1)).diff(b);	// 拡張した連の隣に
			if (sw1.abs() > 0)								// 白石があるとき、
				w1 = w1.or(((F.tran()).mul(sw1)).diff(b));	// その連も加える。
			// TODO この拡張により敵石(黒石)が取れてしまっても、とりあえず放置しておく。
			var l1 = ((this.F0.mul(w1)).diff(w1.and(b))).abs();	// 拡張後の出口
			if (l1 <= l0)									// 出口が増えないとき、
				v = v.diff(w0);								// 死に石とする。
		}
		var lb0 = (this.F0.mul(w.inv())).and(l);	// 黒石の活路(出口)
		var lw0 = (this.F0.mul(b.inv())).and(l);	// 白石の活路(出口)
		var lc0 = lb0.and(lw0); 					// 共通の活路
		if (lc0.abs() >= 2) {						// 白石と黒石の共通の活路が2以上
			var tempSx = this.calcSx(l, this.zero);
			var s = ((tempSx.tran()).mul(lc0)).diff(l);
			v = v.or(s);				// その活路を囲む石はセキなので、復活する。
		}
		return v;
	},

	/**
	 * (2)死に石を計算します。
	 * 「活き石以外の石」
	 * @param l 空点を与えます。
	 * @param v 活き石を与えます。
	 * @return 死に石を返します。
	 * @since 0.1
	 */
	calcX : function(l, v) {
		var x = (l.inv()).diff(v);			// (2)死に石
		return x;
	},

	/**
	 * 空点(含む死に石)の空接点を計算します。
	 * @param l 空点を与えます。
	 * @param x 死に石を与えます。
	 * @return 空点(含む死に石)の空接点を返します。
	 * @since 0.1
	 */
	calcSx : function(l, x) {
		var sp = l.or(x);								// 死に石を加えた空点
		var Sx = new BMatrix(this.order, this.order);	// 求める空接点
		for (var i = 1; i <= this.order; i++) {			// すべての交点に対して、
			if (sp.getValue(i) == 0) {					// 交点に石があれば隣接点を設定
				for (var j = 1; j <= this.order; j++)
					Sx.setValue(i, j, this.F0.getValue(i, j));
			} else {									// 交点が空白なら、
				var adj = this.e[i].clone();			// 求める点(まず初期化)
				var adjx = (this.F0.mul(adj)).and(sp);	// 交点に隣接する空点
				while (!adj.equals(adjx)) {				// 同じになるまで
					adj = adjx;
					adjx = (this.F0.mul(adj)).and(sp);	// 空領域を拡張
				}
				adj = adjx;								// 空領域
				adjx = this.F0.mul(adj);				// その隣接点
				for (var j = 1; j <= this.order; j++)
					Sx.setValue(i, j, adjx.getValue(j));
			}
		}		
		return Sx;
	},

	/**
	 * (3)目を計算します。
	 * 「一方のみの活き石で囲んだ空点」
	 * @param Sx 空点(含む死に石)の空接点を与えます。
	 * @param l 空点を与えます。
	 * @param v 活き石を与えます。
	 * @param b 黒石∨空点を与えます。
	 * @param w 白石∨空点を与えます。
	 * @return 目を返します。
	 * @since 0.1
	 */
	calcY: function(Sx, l, v, b, w) {
		var y = new BVector(this.order);	// (3)目
		var sap = (Sx.tran()).mul(l);		// 空接点 space adjacent point
		var L = Sx.and(l.cross(this.one));
		var R = (l.inv()).cross(sap);
		if (L.equals(R))
			return y;						// 石に囲われていない: 目は無い
		for (var i = 1; i <= this.order; i++) {
			if (l.getValue(i) == 0)
				continue;					// 空点でない
			var enc;						// その空点を囲う(enclosing)石
			enc = ((Sx.tran()).mul(this.e[i])).diff(l);
			if ((enc.diff(v.diff(w)).equals(this.zero)) ||
				(enc.diff(v.diff(b)).equals(this.zero)))
				// 囲う石が黒または白の活き石のみなら、
				y.setValue(i, 1);		// 目である
		}
		return y;
	},

	/**
	 * (4)ダメを計算します。
	 * 「目以外の空点」
	 * @param l 空点を与えます。
	 * @param y 目を与えます。
	 * @return ダメを返します。
	 * @since 0.1
	 */
	calcM: function(l, y) {
		var m = l.diff(y);					// (4)ダメ
		return m;
	},

	/**
	 * (5)セキ石を計算します。
	 * 「ダメを有する活き石」
	 * @param F 連接点行列を与えます。
	 * @param v 活き石を与えます。
	 * @param Sx 空点(含む死に石)の空接点を与えます。
	 * @param m ダメを与えます。
	 * @param l 空点を与えます。
	 * @return セキ石を返します。
	 * @since 0.1
	 */
	calcS : function(F, v, Sx, m, l) {
		var tF = F.tran();
		var tSx = Sx.tran();
		var s = v.and(tF.mul(tSx.mul(m)).diff(l));	// (5)セキ石
		return s;
	},

	/**
	 * (6)地を計算します。
	 * 「セキ石以外の活き石の目」
	 * @param Sx 空点(含む死に石)の連接点を与えます。
	 * @param l 空点を与えます。
	 * @param v 活き石を与えます。
	 * @param b 黒石∨空点を与えます。
	 * @param w 白石∨空点を与えます。
	 * @param s セキ石を与えます。
	 * @return 地を返します。
	 * @since 0.1
	 */
	calcT : function(Sx, l, v, b, w, s) {
		var t = new BVector(this.order);	// (6)地
		var sap = (Sx.tran()).mul(l);		// 空接点 space adjacent point
		var L = Sx.and(l.cross(this.one));
		var R = (l.inv()).cross(sap);
		if (L.equals(R))
			return t;						// 石に囲われていない: 地は無い
		for (var i = 1; i <= this.order; i++) {
			if (l.getValue(i) == 0)
				continue;					// 空点でない
			var enc;						// その空点を囲う(enclosing)石
			enc = ((Sx.tran()).mul(this.e[i])).diff(l);
			if ((enc.diff((v.diff(w)).diff(s)).equals(this.zero)) ||
				(enc.diff((v.diff(b)).diff(s)).equals(this.zero)))
				// 囲う石が黒または白のセキ石以外の活き石のみなら、
				t.setValue(i, 1);			// 地である
		}
		return t;
	},

	/**
	 * 黒石または白石の眼(eye)の候補地(candidate)を計算します。
	 * @param b 黒石∨空点を表すベクトルを与えます。
	 * @param w 白石∨空点を表すベクトルを与えます。
	 * @param x 死に石を表すベクトルを与えます。未定の場合、zeroを指定可。
	 * @param bw Pos.BLACK または Pos.WHITEを与えます。
	 * @return 与えられた石の眼の候補地を表すベクトルを返します。
	 * @since 0.1
	 */
	eyeCan : function(b, w, x, bw) {
		// FIXME バグ:欠け眼も眼の候補になってしまう。
		var c = new BVector(this.order, 1);
		var l = b.and(w);
		for (var i = 1; i <= this.order; i++) {
			if (l.getValue(i) == 0) {
				c.setValue(i, 0);
				continue;
			};
			var enemy;
			if (bw == BLACK)
				enemy = (b.inv()).diff(x);
			else
				enemy = (w.inv()).diff(x);
			if (((this.J1.mul(this.e[i])).and(enemy)).abs() > 0 ||
					((this.D2.mul(this.e[i])).and(enemy)).abs() > 1)
				c.setValue(i, 0);
		}
		return c;
	},
	
	/* 6ベクトルの状態を文字列に変換します。
	 * @return 文字列を返します。
	 * @since 0.1
	 */
	toString : function() {
		var str = new String();
		str += "v=" + this.v.toString() + "\n";
		str += "x=" + this.x.toString() + "\n";
		str += "Sx=\n" + this.Sx.toString();
		str += "y=" + this.y.toString() + "\n";
		str += "m=" + this.m.toString() + "\n";
		str += "s=" + this.s.toString() + "\n";
		str += "t=" + this.t.toString() + "\n";
		str += "cb=" + this.cb.toString() + "\n";
		str += "cw=" + this.cw.toString() + "\n";
		return str;
	},

	/* 6ベクトルの状態を TeX 文字列に変換します。
	 * @return TeX 文字列を返します。
	 * @since 0.1
	 */
	toTeX : function() {
		var str = new String();
		str += "$$\\boldsymbol{v}=" + this.v.toTeX() + "$$\n";
		str += "$$\\boldsymbol{x}=" + this.x.toTeX() + "$$\n";
		str += "$$Sx=" + this.Sx.toTeX() + "$$\n";
		str += "$$\\boldsymbol{y}=" + this.y.toTeX() + "$$\n";
		str += "$$\\boldsymbol{m}=" + this.m.toTeX() + "$$\n";
		str += "$$\\boldsymbol{s}=" + this.s.toTeX() + "$$\n";
		str += "$$\\boldsymbol{t}=" + this.t.toTeX() + "$$\n";
		str += "$$\\boldsymbol{c}_b=" + this.cb.toTeX() + "$$\n";
		str += "$$\\boldsymbol{c}_w=" + this.cw.toTeX() + "$$\n";
		return str;
	}
};

Powered by
SyntaxHighlighter MathJax W3C HTML5