Goban 0.5

マウスをクリックすると碁石を自由に置けます。待ったはできません。[パス Pass] ボタンでパスすることができます。石を囲うと取ることができます。コウの処理を追加しました。

実行結果

Goban 0.5 には HTML5 Canvas が必要です。 Goban 0.5 には HTML5 Canvas が必要です。 Goban 0.5 には HTML5 Canvas が必要です。
0 0

ソース

一部のソースはリンクを張ってあります。ソースは UTF-8 でエンコードしてあります。

goban05.html

スタイルシートの部分です。Canvas をレイヤーのように重ねるための工夫をしています。前回(goban04.html)から変更ありません。
<style>

</style>
実行結果を表示する部分です。構成は前回(goban04.html)から変更ありません。
<h2>実行結果</h2>
<div style="height: 510px; width: 640; position: relative">
<canvas width="640" height="480" id="table">Goban 0.5 には HTML5 Canvas が必要です。</canvas>
<canvas width="640" height="480" id="shadow">Goban 0.5 には HTML5 Canvas が必要です。</canvas>
<canvas width="640" height="480" id="stones">Goban 0.5 には HTML5 Canvas が必要です。</canvas>
<div id="control">
<img style="vertical-align: bottom; padding-left: 10px" src="img/white22.png">
<input id="white" type="button" value="パス Pass" onclick="pass()">
<span id="pw">0</span>
<img style="vertical-align: bottom; padding-left: 10px" src="img/black22.png">
<input id="black" type="button" value="パス Pass" onclick="pass()">
<span id="pb">0</span>
<input style="margin-left: 40px" type="button" value="はじめから Restart" onclick="restart()">
</div><!-- end of control -->
</div><!-- end of 実行結果 -->

goban05.js

バージョンを変更しました。
リンク先をご覧下さい。

board02.js

今回は変更ありません。碁盤を描くレイヤー、碁石を描くレイヤー、碁石の影を描くレイヤーの3つのコンテキストを使い分けています。
リンク先をご覧下さい。

pos03.js

コウの処理を追加しました。BVector k がコウを表すベクトルです。
/*
 * Copyright (C) 2012-2013 たかはしのんき. All rights reserved.
 *
 * History:
 *  0.3 2013/03/18 コウの処理を追加。
 *  0.2 2012/05/17 SixVectors オブジェクトを外せるよう変更。メソッド clear の追加。
 *  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.3
 */
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.prisoner = new Array(2);					// アゲハマ
	try {
		this.v6 = new SixVectors(this);				// 6ベクトル
	} catch(e) {
		this.v6 = null;
	}
	this.clear();
};

Pos.prototype = {
	/**
	 * 隣接関係 F0 を初期化します。
	 * @since 0.1
	 */
	initF0 : function() {
		var adj = new Array(4);
		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);
				}
	},

	/**
	 * 局面を初期化します。
	 * @since 0.3
	 */
	clear : function() {
		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.k = new BVector(this.order);				// コウ
		this.calcVectors(0, 0);
		if (this.v6 != null)
			this.v6.calcSixVectors(this);
		this.watch = null;								// 監視する BVector
		this.turn = BLACK;								// 手番(次の番)
		this.moves = 0;									// 手数
		this.prisoner = [0, 0];
	},

	/**
	 * 監視する 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 = Math.floor((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(col, row);
		if (!mv.isPass(this.ro)) {
			if (!this.isMovable(col, row))
				return false;
			this.calcVectors(col, row);
			if (this.v6 != null)
				this.v6.calcSixVectors(this);
		}
		this.turn = this.opposite(this.turn);
		this.moves++;
		return true;
	},

	/**
	 * 与えられた交点に着手できるかどうかを調べます。
	 * @param col 調べる交点の桁位置
	 * @param row 調べる交点の行位置
	 * @return 着手可能なら true を返します。
	 * @since 0.3
	 */
	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).diff(this.k);
		else if (this.turn == WHITE)
			o = this.F.mul(this.w.xor(this.h)).and(this.l).diff(this.k);
		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 - 着手後の連接点を表す行列<br>
	 * k - 着手後のコウを表すベクトル
	 * @param col 着手した点の横座標
	 * @param row 着手した点の縦座標
	 * @since 0.3
	 */
	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);					// 着点に接するアタリの石
		var z = new BVector(this.b.order);				// all 0(zero)
		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);
			if (F0ei.diff(ei).and(this.b).abs() == 0 && this.d.abs() == 1)
				this.k = this.d;
			else
				this.k = z;
			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);
			if (F0ei.diff(ei).and(this.w).abs() == 0 && this.d.abs() == 1)
				this.k = this.d;
			else
				this.k = z;
			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.3
	 */
	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";
		str += "k=" + this.k.toString() + "\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.3
	 */
	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";
		str += "$$\\boldsymbol{k}=" + this.k.toTeX() + "$$\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;
	}
};

move02.js

今回は変更ありません。
リンク先をご覧下さい。

bvector05.js

先日、行ベクトルの機能を追加しました。

bmatrix04.js

先日、行ベクトルの機能を追加しました。