1 /*
  2  * Copyright (c) 2012-2013 たかはしのんき. All rights reserved.
  3  *
  4  * History:
  5  *  0.7 2013-06-15 JsDoc Toolkit対応。
  6  *  0.6 2013-04-26 プレイアウトのためにアタリの石で囲まれていない自分の1目の眼への着手を禁止。
  7  *  0.5 2013-04-08 プレイアウトのために自分の1目の眼への着手を禁止。
  8  *  0.4 2013-03-29 式の整理、活路の表示。
  9  *  0.3 2013-03-18 コウの処理を追加。
 10  *  0.2 2012-05-17 SixVectors オブジェクトを外せるよう変更。メソッド clear の追加。
 11  *  0.1 2012-05-15 新規作成。Java 版より移植。
 12  *
 13  * Reference:
 14  *  [1]佐藤真史,穴田浩一,堤正義:囲碁の数理モデル化とその応用,
 15  *     第16回ゲームプログラミングワークショップ2011,Vol.2011 No.6,
 16  *     pp.100-103(2011)
 17  */
 18 
 19 /**
 20  * @fileOverview 囲碁の局面 (position) を B-W graph model で表したオブジェクトです。
 21  * このオブジェクトで扱う メソッドの引数に盤面の桁位置 col と行位置 row を 指定する
 22  * 場合は、囲碁に合わせ、col, row の順で指定します。
 23  * 行列の行 i、 列 j を指定する場合の i, j の順と逆になるので注意してください。
 24  * @author たかはしのんき
 25  * @version 0.7
 26  */
 27 var COL = 0;		// 横座標
 28 var ROW = 1;		// 縦座標
 29 var SPACE = 0;		// 空点
 30 var BLACK = 1;		// 黒石
 31 var WHITE = 2;		// 白石
 32 var P_BLACK = 0;	// 黒石(アゲハマと活路用)
 33 var P_WHITE = 1;	// 白石(アゲハマと活路用)
 34 var OPPOSITE = 3;	// 敵石計算用定数
 35 var playout = true;	// true ならプレイアウト用に着手可能の式を変更
 36 
 37 /**
 38  * n 路盤の局面を表すオブジェクトです。
 39  * @constructor
 40  * @this {Pos}
 41  * @param n 路数を指定します。
 42  * @since 0.1
 43  */
 44 Pos = function(n) {
 45 //	this.h = new BVector(order);					// アタリ
 46 //	this.A = new BMatrix(order, order);				// 追加される連接関係
 47 //	this.D = new BMatrix(order, order)				// リセットされる連接関係
 48 //	this.a = new BVector(order);					// 追加される連
 49 //	this.d = new BVector(order);					// リセットされる連
 50 
 51 	this.ro = n;									// 路数
 52 	this.order = n * n;								// ベクトルの次数
 53 	this.F0 = new BMatrix(this.order, this.order);	// 隣接関係
 54 	this.initF0();
 55 	this.prisoner = new Array(2);					// アゲハマ
 56 	this.liberty = new Array(2);					// 活路の数
 57 	try {
 58 		this.v6 = new SixVectors(this);				// 6ベクトル
 59 	} catch(e) {
 60 		this.v6 = null;
 61 	}
 62 	this.clear();
 63 };
 64 
 65 Pos.prototype = {
 66 	/**
 67 	 * 隣接関係 F0 を初期化します。
 68 	 * @since 0.1
 69 	 */
 70 	initF0 : function() {
 71 		var adj = new Array(4);
 72 		adj = [				// 隣接する(adjacent)相対座標
 73 				[0, -1],
 74 				[-1, 0], [0, 0], [1, 0],
 75 				[0, 1]
 76 		];
 77 		for (var row = 1; row <= this.ro; row++)
 78 			for (var col = 1; col <= this.ro; col++)
 79 				for (var i = 0; i < adj.length; i++) {
 80 					var ar = row + adj[i][ROW];
 81 					var ac = col + adj[i][COL];
 82 					if (ar > 0 && ar <= this.ro && ac > 0 && ac <= this.ro)
 83 						this.F0.setValue(this.toIndex(col, row), this.toIndex(ac, ar), 1);
 84 				}
 85 	},
 86 
 87 	/**
 88 	 * 局面を初期化します。
 89 	 * @since 0.3
 90 	 */
 91 	clear : function() {
 92 		this.F = this.F0.clone();						// 連接関係
 93 		this.b = new BVector(this.order, 1);			// 黒の着点∨空点
 94 		this.w = this.b.clone();						// 白の着点∨空点
 95 		this.l = this.b.and(this.w);					// 空点
 96 		this.k = new BVector(this.order);				// コウ
 97 		this.ib = new BVector(this.order);				// 黒石の活路
 98 		this.iw = new BVector(this.order);				// 白石の活路
 99 		this.calcVectors(0, 0);
100 		if (this.v6 != null)
101 			this.v6.calcSixVectors(this);
102 		this.watch = null;								// 監視する BVector
103 		this.turn = BLACK;								// 手番(次の番)
104 		this.moves = 0;									// 手数
105 		this.prisoner = [0, 0];							// アゲハマ
106 		this.liberty = [0, 0];							// 活路の数
107 	},
108 
109 	/**
110 	 * 監視する BVector を設定します。
111 	 * @param watch 監視する BVector を指定します。 null なら監視を解除します。
112 	 * @since 0.1
113 	 */
114 	setWatch : function(watch) {
115 		this.watch = watch;
116 	},
117 
118 	/**
119 	 * 監視する BVector を返します。
120 	 * @return 監視する BVector を返します。なければ null を返します。
121 	 * @since 0.1
122 	 */
123 	getWatch : function() {
124 		return this.watch;
125 	},
126 
127 	/**
128 	 * 碁盤上の点 (move) をベクトル要素の添字 (index) に変換します。
129 	 * @param col 点の横方向の座標
130 	 * @param row 点の縦方向の座標
131 	 * @return 添字を返します。(1 origin)
132 	 * @since 0.1
133 	 */
134 	toIndex : function(col, row) {
135 		return (row - 1) * this.ro + col;
136 	},
137 
138 	/**
139 	 * ベクトル要素の添字 (index) を碁盤上の点 (move) に変換します。
140 	 * @param index 添字を渡します。 (1 origin)
141 	 * @return 碁盤上の点の座標をクラス Move のオブジェクトとして返します。
142 	 * @since 0.1
143 	 */
144 	toMove : function(index) {
145 		var mv = new Move();
146 		mv.row = Math.floor((index - 1) / this.ro) + 1;
147 		mv.col = (index - 1) % this.ro + 1;
148 		return mv;
149 	},
150 
151 	/**
152 	 * turn の反対のプレイヤーを返します。
153 	 * @param turn BLACK か WHITE を指定します。
154 	 * @return turn の反対のプレイヤーを返します。
155 	 * @since 0.1
156 	 */
157 	opposite : function(turn) {
158 		return OPPOSITE - turn;
159 	},
160 
161 	/**
162 	 * 着手により局面を更新します。 col と row のいずれかを ro+1 にするとパスできます。
163 	 * @param col 着手の横座標
164 	 * @param row 着手の縦座標
165 	 * @return 着手できたら true、着手できない手なら false を返します。
166 	 * @since 0.1
167 	 */
168 	move : function(col, row) {
169 		var mv = new Move(col, row);
170 		if (!mv.isPass(this.ro)) {
171 			if (!this.isMovable(col, row))
172 				return false;
173 			this.calcVectors(col, row);
174 			if (this.v6 != null)
175 				this.v6.calcSixVectors(this);
176 		}
177 		this.turn = this.opposite(this.turn);
178 		this.moves++;
179 		return true;
180 	},
181 
182 	/**
183 	 * 与えられた交点に着手できるかどうかを調べます。
184 	 * @param col 調べる交点の桁位置
185 	 * @param row 調べる交点の行位置
186 	 * @return 着手可能なら true を返します。
187 	 * @since 0.3
188 	 */
189 	isMovable : function(col, row) {
190 		if (this.h == null) {
191 			this.h = new BVector(this.order);
192 		}
193 		var tF = this.F.tran();
194 		for (var i = 1; i <= this.order; i++) {
195 			var ei = new BVector(this.order);
196 			ei.setValue(i, 1);
197 			var tFei = tF.mul(ei); 
198 			if ((tFei.and(this.l)).abs() == 1)
199 				this.h.setValue(i, 1);
200 			else
201 				this.h.setValue(i, 0);
202 		}
203 		if (playout) {
204 			var rb = new BVector(this.order);	// プレイアウトでの黒石の着手禁止点を表すベクトル
205 			var rw = new BVector(this.order);	// プレイアウトでの白石の着手禁止点を表すベクトル
206 			var z = new BVector(this.order);	// all 0 (zero)
207 			for (var i = 1; i <= this.order; i++) {
208 				var ei = new BVector(this.order);
209 				ei.setValue(i, 1);
210 				var F0ei_ei = this.F0.mul(ei).diff(ei);
211 				if (F0ei_ei.diff(this.w.inv().diff(this.h)).equals(z))
212 					rb.setValue(i, 1);
213 				if (F0ei_ei.diff(this.b.inv().diff(this.h)).equals(z))
214 					rw.setValue(i, 1);
215 			}
216 		}
217 		if (row == 0 && col == 0)
218 			return false;
219 		var o = new BVector(this.order);		// 着手可能点を表すベクトル
220 		if (playout) {
221 			if (this.turn == BLACK)
222 				o = this.F.mul(this.b.xor(this.h)).and(this.l).diff(this.k).diff(rb);
223 			else if (this.turn == WHITE)
224 				o = this.F.mul(this.w.xor(this.h)).and(this.l).diff(this.k).diff(rw);
225 			else
226 				return false;
227 		} else {
228 			if (this.turn == BLACK)
229 				o = this.F.mul(this.b.xor(this.h)).and(this.l).diff(this.k);
230 			else if (this.turn == WHITE)
231 				o = this.F.mul(this.w.xor(this.h)).and(this.l).diff(this.k);
232 			else
233 				return false;
234 		}
235 		var i = this.toIndex(col, row);	// 調べる着点
236 		var ei = new BVector(this.order);
237 		ei.setValue(i, 1);
238 		if (o.and(ei).equals(ei))
239 			return true;
240 		return false;
241 	},
242 
243 	/**
244 	 * BWモデルに関連するベクトルと行列を計算します。計算の対象は以下のとおり。<br>
245 	 * l - 空点を表すベクトル<br>
246 	 * h - アタリの石の集合を表すベクトル<br>
247 	 * a - 着手によって形成される連接点を表すベクトル<br>
248 	 * d - 着手によって削除される石を表すベクトル<br>
249 	 * A - 着手によって追加される連接点を表す行列<br>
250 	 * D - 着手によってリセットされる連接点を表す行列<br>
251 	 * b - 着手後の黒石∨空点を表すベクトル<br>
252 	 * w - 着手後の白石∨空点を表すベクトル<br>
253 	 * F - 着手後の連接点を表す行列<br>
254 	 * k - 着手後のコウを表すベクトル<br>
255 	 * ib - 着手後の黒石の活路を表すベクトル<br>
256 	 * iw - 着手後の白石の活路を表すベクトル
257 	 * @param col 着手した点の横座標
258 	 * @param row 着手した点の縦座標
259 	 * @since 0.3
260 	 */
261 	calcVectors : function(col, row) {
262 		if (this.h == null)
263 			this.h = new BVector(this.order);
264 		var tF = this.F.tran();							// Fの転置
265 		for (var i = 1; i <= this.order; i++) {
266 			var ei = new BVector(this.order);
267 			ei.setValue(i, 1);
268 			var tFei = tF.mul(ei); 
269 			if ((tFei.and(this.l)).abs() == 1)
270 				this.h.setValue(i, 1);
271 			else
272 				this.h.setValue(i, 0);
273 		}
274 		if (row ==0 && col==0)
275 			return;
276 		var i = this.toIndex(col, row);					// 着点
277 		var ei = new BVector(this.order);
278 		ei.setValue(i, 1);
279 		var Fei = this.F.mul(ei);						// 着点を連接点とする交点
280 		var F0ei = this.F0.mul(ei);						// 着点の隣接点
281 		var z = new BVector(this.order);				// all 0 (zero)
282 		if (this.turn == BLACK) {
283 			this.a = (Fei.diff(this.w)).or(ei);			// 着点を連接点とする黒石と着点
284 			this.d = (Fei.and(this.h)).diff(this.b);	// 着点を連接点とする白石のアタリの連
285 			if (F0ei.diff(ei).and(this.b).abs() == 0 && this.d.abs() == 1)
286 				this.k = this.d;
287 			else
288 				this.k = z;
289 			this.b = this.b.or(this.d);
290 			this.w = this.w.diff(ei);
291 			this.prisoner[P_BLACK] += this.d.abs();
292 		} else if (this.turn == WHITE) {
293 			this.a = (Fei.diff(this.b)).or(ei);			// 着点を連接点とする白石と着点
294 			this.d = (Fei.and(this.h)).diff(this.w);	// 着点を連接点とする黒石のアタリの連
295 			if (F0ei.diff(ei).and(this.w).abs() == 0 && this.d.abs() == 1)
296 				this.k = this.d;
297 			else
298 				this.k = z;
299 			this.b = this.b.diff(ei);
300 			this.w = this.w.or(this.d);
301 			this.prisoner[P_WHITE] += this.d.abs();
302 		}
303 		this.A = this.a.cross(tF.mul(this.a));
304 		var o = new BVector(this.order, 1);				// all 1 (one)
305 		this.D = this.F0.or((this.d.inv()).cross(o));
306 		this.F = (this.F.or(this.A)).and(this.D);
307 		this.l = this.b.and(this.w);
308 		this.ib = (tF.mul((this.w).inv())).and(this.l);	// 黒石の活路
309 		this.liberty[P_BLACK] = this.ib.abs();			// 黒石の活路の数
310 		this.iw = (tF.mul((this.b).inv())).and(this.l);	// 白石の活路
311 		this.liberty[P_WHITE] = this.iw.abs();			// 白石の活路の数
312 	},
313 
314 	/**
315 	 * 局面の座標より、石または空点を返します。エラーのときは -1 を返します。
316 	 * @param col 横座標
317 	 * @param row 縦座標
318 	 * @return SPACE, BLACK, WHITE のいずれかを返します。 
319 	 * @since 0.1
320 	 */
321 	getStone : function(col, row) {
322 		if (row <= 0 || row > this.ro || col <=0 || col > this.ro)
323 			return -1;
324 		var b1 = this.b.getValue(this.toIndex(col, row));
325 		var w1 = this.w.getValue(this.toIndex(col, row));
326 		var stone = ((b1 & w1) == 1) ? SPACE : ((b1 == 1) ? BLACK : WHITE);
327 		return stone;
328 	},
329 
330 	/* 局面の状態を文字列に変換します。
331 	 * @return 文字列を返します。
332 	 * @since 0.3
333 	 */
334 	toString : function() {
335 		var turn = ["-", "B", "W"];
336 		var str = new String();
337 		str = "#" + this.moves + "\n";
338 		str += "F'=\n" + this.F.toString();
339 		str += "b'=" + this.b.toString() + "\n";
340 		str += "w'=" + this.w.toString() + "\n";
341 		str += "l=" + this.l.toString() + "\n";
342 		str += "h=" + this.h.toString() + "\n";
343 		str += "Σ|db|=" + this.prisoner[P_BLACK] + "\n";
344 		str += "Σ|dw|=" + this.prisoner[P_WHITE] + "\n";
345 		str += "k=" + this.k.toString() + "\n";
346 		if (this.moves > 0) {
347 			str += "a=" + this.a.toString() + "\n";
348 			str += "d=" + this.d.toString() + "\n";
349 			str += "A=\n" + this.A.toString();
350 			str += "D=\n" + this.D.toString();
351 		}
352 		str += "turn:" + turn[this.turn] + "\n";
353 		return str;
354 	},
355 
356 	/* 局面の状態を TeX 文字列に変換します。
357 	 * @return TeX 文字列を返します。
358 	 * @since 0.3
359 	 */
360 	toTeX : function() {
361 		var turn = ["-", "B", "W"];
362 		var str = new String();
363 		str = "#" + this.moves + "\n";
364 		str += "$$F'=" + this.F.toTeX() +"$$\n";
365 		str += "$$\\boldsymbol{b}'=" + this.b.toTeX() + "$$\n";
366 		str += "$$\\boldsymbol{w}'=" + this.w.toTeX() + "$$\n";
367 		str += "$$\\boldsymbol{l}=" + this.l.toTeX() + "$$\n";
368 		str += "$$\\boldsymbol{h}=" + this.h.toTeX() + "$$\n";
369 		str += "$$\\sum \\left| \\boldsymbol{d}_b \\right| =" + this.prisoner[P_BLACK] + "$$\n";
370 		str += "$$\\sum \\left| \\boldsymbol{d}_w \\right| =" + this.prisoner[P_WHITE] + "$$\n";
371 		str += "$$\\boldsymbol{k}=" + this.k.toTeX() + "$$\n";
372 		if (this.moves > 0) {
373 			str += "$$\\boldsymbol{a}=" + this.a.toTeX() + "$$\n";
374 			str += "$$\\boldsymbol{d}=" + this.d.toTeX() + "$$\n";
375 			str += "$$A=" + this.A.toTeX() + "$$\n";
376 			str += "$$D=" + this.D.toTeX() + "$$\n";
377 		}
378 		str += "turn:" + turn[this.turn] + "\n";
379 		return str;
380 	},
381 
382 	/* 局面の状態を絵文字列に変換します。
383 	 * @return 絵文字列を返します。
384 	 * @since 0.1
385 	 */
386 	toPicStr : function() {
387 		var figure = ["┼", "●", "○"];
388 		var grid = ["┏", "┯", "┓", "┠", "┼", "┨", "┗", "┷", "┛"];
389 		var str = new String();
390 		str = "";
391 		for (var row = 1; row <= this.ro; row++) {
392 			for (var col = 1; col <= this.ro; col++) {
393 				var stone = this.getStone(col, row);
394 				if (stone == SPACE) {
395 					var rx = Math.ceil((row - 1) / (this.ro - 1) * 2);
396 					var cx = Math.ceil((col - 1) / (this.ro - 1) * 2);
397 					str += grid[rx * this.ro + cx];
398 				} else
399 					str += figure[stone];
400 			}
401 			str += "\n";
402 		}
403 		return str;
404 	}
405 };
406