/*
* Copyright (C) 2012 たかはしのんき. All rights reserved.
*
* History:
* v0.15 2012/03/22 式の見直しを行いました。パスの仕様を変更しました。
* v0.14 2012/03/03 監視するBVectorを設定、6ベクトルを計算できるようにしました。
* v0.13 2012/02/29 COLとROWを入れ替え。move()でパスできるようにしました。
* v0.12 2012/02/25 toMove()のバグを修正。
* v0.11 2012/02/16 isMovable()をpublicに変更。rowとcolの順番を入れ替え。
* ベクトルa,d,行列Aの計算式を修正。
* v0.10 2012/02/16 メソッドisMovable()の追加。
* v0.6 2012/02/10 アゲハマの処理を追加。
* v0.5 2012/02/09 ベクトルdの計算のバグ(石が取れない)を解消。
* v0.4 2012/02/09 新規作成。
*
* Reference:
* [1]佐藤真史,穴田浩一,堤正義:囲碁の数理モデル化とその応用,
* 第16回ゲームプログラミングワークショップ2011,Vol.2011 No.6,
* pp.100-103(2011)
*/
package nonkit.lib;
/**
* 囲碁の局面をB-W graph modelで表したクラスです。このクラスで扱う
* メソッドの引数に盤面の桁位置colと行位置rowを指定する場合は、
* 囲碁に合わせ、col, rowの順で指定します。行列の行i、列j
* を指定する場合のi, jの順と逆になるので注意してください。
* @author たかはしのんき
* @version v0.15
*/
public class Pos {
public final static int COL = 0; // 横座標
public final static int ROW = 1; // 縦座標
public final static int SPACE = 0; // 空点
public final static int BLACK = 1; // 黒石
public final static int WHITE = 2; // 白石
public final static int P_BLACK = 0; // 黒石(アゲハマ用)
public final static int P_WHITE = 1; // 白石(アゲハマ用)
public final static int OPPOSITE = 3; // 敵石計算用定数
public BMatrix F0; // 隣接関係
public BMatrix F; // 連接関係
public BMatrix A; // 追加される連接関係
public BMatrix D; // リセットされる連接関係
public BVector b; // 黒の着点∨空点
public BVector w; // 白の着点∨空点
public BVector l; // 空点
public BVector h; // アタリ
public BVector a; // 追加される連
public BVector d; // リセットされる連
public SixVectors v6; // 6ベクトル
public int ro; // 路数
private BVector watch; // 監視するBVector
public int turn = BLACK; // 次の番
public int moves = 0; // 手数
public int[] prisoner = new int[2]; // アゲハマ
private int order; // 次数
/**
* n路盤の初期局面を生成するコンストラクタです。
* @param n 路数を指定します。
* @since v0.4
*/
public Pos(int n) {
ro = n;
order = n * n;
F0 = new BMatrix(order, order);
initF0();
F = F0.clone();
b = new BVector(order, 1);
w = b.clone();
l = b.and(w);
calcVectors(0, 0);
v6 = new SixVectors(this);
v6.calcSixVectors(this);
}
/**
* 隣接関係F0を初期化します。
* @since v0.4
*/
private void initF0() {
int[][] adj = { // 隣接する(adjacent)相対座標
{0, -1},
{-1, 0}, {0, 0}, {1, 0},
{0, 1}
};
for (int row = 1; row <= ro; row++)
for (int col = 1; col <= ro; col++)
for (int i = 0; i < adj.length; i++) {
int ar = row + adj[i][ROW];
int ac = col + adj[i][COL];
if (ar > 0 && ar <= ro && ac > 0 && ac <= ro)
F0.setValue(toIndex(col, row), toIndex(ac, ar), 1);
}
}
/**
* 監視するBVectorを設定します。
* @param watch 監視するBVectorを指定します。nullなら監視を解除します。
* @since v0.14
*/
public void setWatch(BVector watch) {
this.watch = watch;
}
/**
* 監視するBVectorを返します。
* @return 監視するBVectorを返します。なければnullを返します。
* @since v0.14
*/
public BVector getWatch() {
return watch;
}
/**
* 碁盤上の点(move)をベクトル要素の添字(index)に変換します。
* @param col 点の横方向の座標
* @param row 点の縦方向の座標
* @return 添字を返します。(1 origin)
* @since v0.4
*/
public int toIndex(int col, int row) {
return (row - 1) * ro + col;
}
/**
* ベクトル要素の添字(index)を碁盤上の点(move)に変換します。
* @param index 添字を渡します。(1 origin)
* @return 碁盤上の点の座標をクラスMoveのオブジェクトとして返します。
* @since v0.4
*/
public Move toMove(int index) {
Move move = new Move();
move.row = (index - 1) / ro + 1;
move.col = (index - 1) % ro + 1;
return move;
}
/**
* turnの反対のプレイヤーを返します。
* @param turn BLACKかWHITEを指定します。
* @return turnの反対のプレイヤーを返します。
* @since v0.4
*/
private int opposite(int turn) {
return OPPOSITE - turn;
}
/**
* 着手により局面を更新します。colとrowのいずれかをro+1にするとパスできます。
* @param col 着手の横座標
* @param row 着手の縦座標
* @return 着手できたらtrue、着手できない手ならfalseを返します。
* @since v0.4
*/
public boolean move(int col, int row) {
if (col != Move.pass(ro) && row != Move.pass(ro)) {
if (!isMovable(col, row))
return false;
calcVectors(col, row);
v6.calcSixVectors(this);
}
turn = opposite(turn);
moves++;
return true;
}
/**
* 与えられた交点に着手できるかどうかを調べます。
* @param col 調べる交点の桁位置
* @param row 調べる交点の行位置
* @return 着手可能ならtrueを返します。
* @since v0.10
*/
public boolean isMovable(int col, int row) {
if (h == null) {
h = new BVector(b.order);
}
BMatrix tF = F.tran();
for (int i = 1; i <= b.order; i++) {
BVector ei = new BVector(b.order);
ei.setValue(i, 1);
BVector tFei = tF.mul(ei);
if ((tFei.and(l)).abs() == 1)
h.setValue(i, 1);
else
h.setValue(i, 0);
}
if (row == 0 && col == 0)
return false;
BVector o = new BVector(b.order);
if (turn == BLACK)
o = F.mul(b.xor(h)).and(l);
else if (turn == WHITE)
o = F.mul(w.xor(h)).and(l);
else
return false;
int i = toIndex(col, row); // 調べる着点
BVector ei = new BVector(b.order);
ei.setValue(i, 1);
if (o.and(ei).equals(ei))
return true;
return false;
}
/**
* BWモデルに関連するベクトルと行列を計算します。計算の対象は以下のとおり。
* l - 空点を表すベクトル
* h - アタリの石の集合を表すベクトル
* a - 着手によって形成される連接点を表すベクトル
* d - 着手によって削除される石を表すベクトル
* A - 着手によって追加される連接点を表す行列
* D - 着手によってリセットされる連接点を表す行列
* b - 着手後の黒石∨空点を表すベクトル
* w - 着手後の白石∨空点を表すベクトル
* F - 着手後の連接点を表す行列
* @param col 着手した点の横座標
* @param row 着手した点の縦座標
* @since v0.4
*/
private void calcVectors(int col, int row) {
if (h == null) {
h = new BVector(b.order);
}
BMatrix tF = F.tran(); // Fの転置
for (int i = 1; i <= b.order; i++) {
BVector ei = new BVector(b.order);
ei.setValue(i, 1);
BVector tFei = tF.mul(ei);
if ((tFei.and(l)).abs() == 1)
h.setValue(i, 1);
else
h.setValue(i, 0);
}
if (row ==0 && col==0)
return;
int i = toIndex(col, row); // 着点
BVector ei = new BVector(b.order);
ei.setValue(i, 1);
BVector F0ei = F0.mul(ei); // 着点の隣接点
// adj_: adjacent 隣接する
// uap: unit adjacent points 連接点
// e: enemy 敵石
BVector adj_h = F0ei.and(h); // 着点に接するアタリの石
if (turn == BLACK) {
//original a = (tFei.and(b.diff(l))).or(ei);
BVector adj_b = (F0ei.diff(w)).or(ei); // 着点と接する黒石
BVector adj_buap = tF.mul(adj_b); // の黒石の連接点
a = (adj_buap.diff(w)).or(ei); // の黒石と着点
BVector adj_he = adj_h.diff(b); // 着点に接する敵石のアタリ
BVector adj_heuap = tF.mul(adj_he); // 着点に接する敵石のアタリの連接点
d = adj_heuap.diff(b);
b = b.or(d);
w = w.diff(ei);
prisoner[P_BLACK] += d.abs();
} else if (turn == WHITE) {
//original a = (tFei.and(w.diff(l))).or(ei);
BVector adj_w = (F0ei.diff(b)).or(ei); // 着点と接する白石
BVector adj_wuap = tF.mul(adj_w); // の白石の連接点
a = (adj_wuap.diff(b)).or(ei); // の白石と着点
BVector adj_he = adj_h.diff(w); // 着点に接する敵石のアタリ
BVector adj_heuap = tF.mul(adj_he); // 着点に接する敵石のアタリの連接点
d = adj_heuap.diff(w);
b = b.diff(ei);
w = w.or(d);
prisoner[P_WHITE] += d.abs();
}
//orig. A = (a.multran(a)).mul(tF);
A = a.multran(tF.mul(a)); // v0.15で式を変更
//orig. D = F0.mul(d.inv()); // 結果がベクトル(行列から引けず)
BVector o = new BVector(b.order, 1);// all 1(one)
D = F0.or((d.inv()).multran(o));
//orig. F = (F.or(A)).diff(D);
F = (F.or(A)).and(D);
l = b.and(w); // v0.14にてtFの計算後からココへ
}
/**
* 局面の座標より、石または空点を返します。エラーのときは-1を返します。
* @param col 横座標
* @param row 縦座標
* @return SPACE, BLACK, WHITEのいずれかを返します。
* @since v0.4
*/
public int getStone(int col, int row) {
if (row <= 0 || row > ro || col <=0 || col > ro)
return -1;
int b1 = b.getValue(toIndex(col, row));
int w1 = w.getValue(toIndex(col, row));
int stone = ((b1 & w1) == 1) ? SPACE : ((b1 == 1) ? BLACK : WHITE);
return stone;
}
/* (非 Javadoc)
* @see java.lang.Object#toString()
* @since v0.4
*/
public String toString() {
String[] figure = {"+", "@", "O"};
String[] turn = {"-", "B", "W"};
String str = new String();
str = "#" + moves + "\n";
str += "F'=\n" + F.toString();
str += "b'=(" + b.toString() + ")\n";
str += "w'=(" + w.toString() + ")\n";
str += "l=(" + l.toString() + ")\n";
str += "h=(" + h.toString() + ")\n";
str += "Σdb=" + prisoner[P_BLACK] + "\n";
str += "Σdw=" + prisoner[P_WHITE] + "\n";
if (moves > 0) {
str += "a=(" + a.toString() + ")\n";
str += "d=(" + d.toString() + ")\n";
str += "A=\n" + A.toString();
str += "D=\n" + D.toString();
}
for (int row = 1; row <= ro; row++) {
str += "'"; // for Excel
for (int col = 1; col <= ro; col++) {
int stone = getStone(col, row);
str += figure[stone];
}
str += "\n";
}
str += "turn:" + turn[this.turn] + "\n";
return str;
}
}