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