1 /*
  2  * Copyright (C) 2012-2013 たかはしのんき. All rights reserved.
  3  *
  4  * History:
  5  *  0.4 2013-06-17 プレイアウトの処理を実装。
  6  *  0.3 2013-06-15 JsDoc Toolkit対応。メソッド showScore の作成。
  7  *  0.2 2013-04-26 バージョンの変更。
  8  *  0.1 2013-04-07 碁盤プログラム goban06.js を元に新規作成。
  9  */
 10 
 11 /**
 12  * @fileOverview Simulator - 囲碁対局シミュレータ
 13  * @name simulator04.js
 14  * @version 0.4
 15  * @author たかはしのんき
 16  * 
 17  */
 18 
 19 // 共通に使う定数と変数
 20 var BLACK = 1;
 21 var WHITE = 2;
 22 var PX_BOWL = 134;	// 碁笥の直径 [ピクセル]
 23 var Z2_BOWL = 20;	// 碁笥の高さ / 2 [ピクセル]
 24 var PX_LID = 110;	// 碁笥の蓋の直径 [ピクセル]
 25 var Z2_LID = 8;		// 碁笥の蓋の高さ / 2 [ピクセル]
 26 var PX_STONE = 22;	// 碁石の直径 [ピクセル]
 27 var Z2_STONE = 2;	// 碁石の高さ / 2 [ピクセル]
 28 var PX_STAR = 5;	// 星の直径 [ピクセル]
 29 var PX_CHAR = 24;	// 文字の高さ [ピクセル]
 30 var Z2_BOARD = 6;	// 碁盤の高さ / 2 [ピクセル]
 31 
 32 // キャッシュにイメージを読み込んでおく
 33 var imgbb = new Image(PX_BOWL, PX_BOWL);	// 黒の碁笥 (black bowl)
 34 imgbb.src = "img/blackbowl134.png";
 35 var imgwb = new Image(PX_BOWL, PX_BOWL);	// 白の碁笥 (white bowl)
 36 imgwb.src = "img/whitebowl134.png";
 37 var imgl = new Image(PX_LID, PX_LID);		// 碁笥の蓋 (lid)
 38 imgl.src = "img/lid110.png";
 39 var imgb = new Image(PX_STONE, PX_STONE);	// 黒石 (white stone)
 40 imgb.src = "img/black22.png";
 41 var imgw = new Image(PX_STONE, PX_STONE);	// 白石 (black stone)
 42 imgw.src = "img/white22.png";
 43 var imgs = new Image(PX_STAR, PX_STAR);		// 星 (star)
 44 imgs.src = "img/star5.png";
 45 
 46 // キャッシュに効果音を読み込んでおく
 47 var ext = audioExt();
 48 var auClick = new Audio("se/button6."  + ext);
 49 var auSwitch = new Audio("se/se_sad05." + ext);
 50 var auError = new Audio("se/se_sad08." + ext);
 51 
 52 var ro = 9;				// 路数
 53 var context3;				// 碁石レイヤーのキャンバスのコンテキスト
 54 var context2;				// 影レイヤーのキャンバスのコンテキスト
 55 var passButton = new Array(2);		// [パス]ボタン
 56 var brd;				// 碁盤オブジェクト
 57 var offsetBLid = new Object();		// 黒の碁笥の蓋のオフセット
 58 var offsetWLid = new Object();		// 白の碁笥の蓋のオフセット
 59 
 60 /**
 61  * ロード時に碁盤イメージを描画します。
 62  */
 63 window.onload = function() {
 64 	// 黒石の <input id="black"> 取得
 65 	passButton[BLACK] = document.getElementById('black');
 66 	// 白石の <input id="white"> 取得
 67 	passButton[WHITE] = document.getElementById('white');
 68 
 69 	// キャンバスとコンテキスト取得
 70 	var canvas = document.querySelector('#table');
 71 	var context = canvas.getContext('2d');
 72 	var canvas3 = document.querySelector('#stones');
 73 	context3 = canvas3.getContext('2d');
 74 	var canvas2 = document.querySelector('#shadow');
 75 	context2 = canvas2.getContext('2d');
 76 	// 座標 (270, 50) にタイトルを表示
 77 	context.fillStyle = "white";
 78 	context.font = "bold 24px Arial";
 79 	var title = "Simulator 0.4";
 80 	var x = context.canvas.width / 2 - 
 81 		PX_CHAR / 1.8 * title.length / 2;
 82 	var y = PX_CHAR * 2;
 83 	context.fillText(title, x, y);
 84 	// 机の中央に 碁盤を表示
 85 	brd = new Board(ro, context);
 86 	// 碁盤の左に白の碁笥の蓋のイメージを表示
 87 	offsetWLid.x = Math.floor((brd.boardOffset.x - PX_LID) / 2);
 88 	offsetWLid.y = brd.boardOffset.y +
 89 		Math.floor((brd.boardSize.height - PX_LID) / 2);
 90 	drawObject(offsetWLid.x, offsetWLid.y, Z2_LID, imgl, context, context);
 91 	// 左上に白の碁笥のイメージを表示
 92 	var xb = offsetWLid.x - Math.floor((PX_BOWL - PX_LID) / 2);
 93 	var yb = offsetWLid.y - PX_BOWL - Z2_BOWL;
 94 	drawObject(xb, yb, Z2_BOWL, imgwb, context, context);
 95 	// 碁盤の右に黒の碁笥の蓋のイメージを表示
 96 	offsetBLid.x = offsetWLid.x + brd.boardOffset.x + brd.boardSize.width;
 97 	offsetBLid.y = offsetWLid.y;
 98 	drawObject(offsetBLid.x, offsetBLid.y, Z2_LID, imgl, context, context);
 99 	// 右下に黒の碁笥のイメージを表示
100 	xb = offsetBLid.x - Math.floor((PX_BOWL - PX_LID) / 2);
101 	yb = offsetBLid.y + PX_LID + Z2_BOWL;
102 	drawObject(xb, yb, Z2_BOWL, imgbb, context, context);
103 	// 碁盤を表示
104 	brd.drawBoard(Z2_BOARD, context);
105 	// 黒番から
106 	passButton[WHITE].disabled = true;
107 	// クリックイベント登録
108 	canvas3.addEventListener("click", onClick, false);
109 };
110 
111 /**
112  * アゲハマの表示
113  * @param {number} stone 石の色
114  * @param {number} prisoner アゲハマの数
115  * @since 0.1
116  */
117 function showPrisoner(stone, prisoner) {
118 	if (stone == BLACK) {
119 		var p = document.getElementById('pb');
120 	} else {
121 		var p = document.getElementById('pw');
122 	}
123 	p.firstChild.nodeValue = prisoner;
124 }
125 
126 /**
127  * 得点の表示
128  * @param {number} stone 石の色
129  * @param {number} score 得点
130  * @since 0.3
131  */
132 function showScore(stone, score) {
133 	if (stone == BLACK) {
134 		var p = document.getElementById('sb');
135 	} else {
136 		var p = document.getElementById('sw');
137 	}
138 	p.firstChild.nodeValue = score;
139 }
140 
141 /**
142  * マウスイベントハンドラー
143  * 碁石を置きます。
144  * @since 0.1
145  */
146 function onClick() {
147 	var event = arguments[0];
148 	var mx = event.pageX - this.parentNode.offsetLeft;
149 	var my = event.pageY - this.parentNode.offsetTop; 
150 	var mv = brd.offsetToMove(mx, my);
151 	if (mv.onBoard) {
152 		brd.move(mv.col, mv.row, context3, context2);
153 	} else {
154 		if (brd.p.turn == BLACK) 
155 			drawObject(mx - (PX_STONE / 2), my - (PX_STONE / 2), Z2_STONE, imgb, context2, context2);
156 		else
157 			drawObject(mx - (PX_STONE / 2), my - (PX_STONE / 2), Z2_STONE, imgw, context2, context2);
158 		auError.play();
159 	}
160 }
161 
162 
163 /**
164  * [パス]ボタンの処理
165  * @since 0.1
166  */
167 function pass() {
168 	// ボタンの音を鳴らす。
169 	auSwitch.play();
170 	var col = PASS(ro);
171 	var row = PASS(ro);
172 	brd.move(col, row, context3, context2);
173 }
174 
175 /**
176  * [プレイアウト]ボタンの処理
177  * @since 0.4
178  */
179 function playout() {
180 	// ボタンの音を鳴らす。
181 	auSwitch.play();
182 	// 碁石と影のレイヤーをクリア
183 	context3.clearRect(0, 0, context3.canvas.width, context3.canvas.height);
184 	context2.clearRect(0, 0, context2.canvas.width, context2.canvas.height);
185 	// アゲハマのクリア
186 	showPrisoner(BLACK, 0);
187 	showPrisoner(WHITE, 0);
188 	// 得点のクリア
189 	showScore(BLACK, 0);
190 	showScore(WHITE, 0);
191 	// 黒番から
192 	brd.clear();
193 	passButton[BLACK].disabled = false;
194 	passButton[WHITE].disabled = true;
195 	// プレイアウト
196 	var lastPass = false;
197 	var timer = window.setInterval(function() {
198 		if (0 < brd.p.c.abs()) {
199 			// 着手可能な手があればその中からランダムに1手選んで打つ
200 			var i = Math.floor(Math.random() * brd.p.order) + 1;
201 			for ( ; ; i++) {
202 				if (brd.p.order < i) 
203 					i = 1;
204 				if (brd.p.c.getValue(i) == 1)
205 					break;
206 			}
207 			var mv = brd.p.toMove(i)
208 			brd.move(mv.col, mv.row, context3, context2);
209 			lastPass = false;
210 		} else {
211 			// なければパス
212 			auSwitch.play();
213 			brd.move(PASS(brd.p.ro), PASS(brd.p.ro), context3, context2);
214 			// 両者パスならば終局
215 			if (lastPass)
216 				window.clearInterval(timer);
217 			lastPass = true;
218 		}
219 	}, 300);			// 300ms間隔で次の手を打つ
220 }
221 
222 /**
223  * ブラウザで有効なAudioの拡張子を獲得
224  * @since 0.1
225  */
226 function audioExt() {
227 	var ext = "";
228 	var audio = new Audio();
229  	if (audio.canPlayType("audio/ogg") == 'maybe')
230  		ext="ogg";
231 	else if (audio.canPlayType("audio/mp3") == 'maybe')
232     		ext="mp3";
233 	else if (audio.canPlayType("audio/wav") == 'maybe')
234 		ext="wav";
235 	return ext;
236 }
237 
238 /**
239  * 円形オブジェクトを描画します。
240  * @param x 左端座標
241  * @param y 上端座標
242  * @param s 影の長さ(高さ/2)
243  * @param img オブジェクトのイメージ
244  * @param context3 描画先のコンテキストを指定します。
245  * @param context2 影用のコンテキストを指定します。
246  * @since 0.1
247  */
248 function drawObject(x, y, s, img, context3, context2) {
249 	var r = img.width / 2;
250 	context2.beginPath();
251 	context2.arc(x + r + s, y + r + s, r, 0, 2 * Math.PI);
252 	context2.fillStyle = "black";
253 	context2.globalAlpha = 0.5;
254 	context2.fill();
255 	context3.globalAlpha = 1.0;
256 	context3.drawImage(img, x, y);
257 }
258