Goban 0.3
マウスをクリックすると碁石を自由に置けます。待ったはできません。[パス Pass] ボタンでパスすることができます。石を囲ってもまだ取れません。
実行結果
ソース
"expand source" を押すと展開されます。元に戻すには、このページを再ロードしてください。
goban03.html
<div style="height: 510px; background-color: #26130D; margin 0">
<canvas style="margin: 0" width="640" height="480" id="table">Goban 0.3 には HTML5 Canvas が必要です。</canvas>
<img style="vertical-align: bottom; padding-left: 10px" src="img/white22.png">
<input id="white" type="button" value="パス Pass" onclick="pass()">
<img style="vertical-align: bottom; padding-left: 10px" src="img/black22.png">
<input id="black" type="button" value="パス Pass" onclick="pass()">
<input style="margin-left: 40px" type="button" value="はじめから Restart" onclick="restart()">
</div>
goban03.js
goban02.js からの差分は以下のとおりです。
(1) 碁盤や碁笥の位置を計算により求め、きれいに配置しました。
(2) Firefox で石が置けなかったため、マウスイベントの処理を改良しました。
(3) 6路盤を9路盤に変更し、星の描画処理を追加しました。
(4) 碁盤の処理を Board オブジェクトとして board01.js に分離しました。
(5) [パス]、[はじめから]ボタンの処理を追加しました。
/*
* Copyright (C) 2012 たかはしのんき. All rights reserved.
*
* History:
* 0.3 2012/05/15 マウスイベントで Firefox に対応。星の表示に対応。ボタンに対応。
* 0.2 2012/05/08 マウスイベントハンドラーを追加。効果音に対応。
* 0.1 2012/05/07 新規作成。Java 版より移植。
*/
/**
* Goban - 碁盤イメージの描画
* @version 0.3
* @author たかはしのんき
*
*/
// 共通に使う定数と変数
var BLACK = 1;
var WHITE = 2;
var PX_BOWL = 134; // 碁笥の直径 [ピクセル]
var Z2_BOWL = 20; // 碁笥の高さ / 2 [ピクセル]
var PX_LID = 110; // 碁笥の蓋の直径 [ピクセル]
var Z2_LID = 8; // 碁笥の蓋の高さ / 2 [ピクセル]
var PX_STONE = 22; // 碁石の直径 [ピクセル]
var Z2_STONE = 2; // 碁石の高さ / 2 [ピクセル]
var PX_STAR = 5; // 星の直径 [ピクセル]
var PX_CHAR = 24; // 文字の高さ [ピクセル]
var Z2_BOARD = 6; // 碁盤の高さ / 2 [ピクセル]
// キャッシュにイメージを読み込んでおく
var imgbb = new Image(PX_BOWL, PX_BOWL); // 黒の碁笥 (black bowl)
imgbb.src = "img/blackbowl134.png";
var imgwb = new Image(PX_BOWL, PX_BOWL); // 白の碁笥 (white bowl)
imgwb.src = "img/whitebowl134.png";
var imgl = new Image(PX_LID, PX_LID); // 碁笥の蓋 (lid)
imgl.src = "img/lid110.png";
var imgb = new Image(PX_STONE, PX_STONE); // 黒石 (white stone)
imgb.src = "img/black22.png";
var imgw = new Image(PX_STONE, PX_STONE); // 白石 (black stone)
imgw.src = "img/white22.png";
var imgs = new Image(PX_STAR, PX_STAR); // 星 (star)
imgs.src = "img/star5.png";
// キャッシュに効果音を読み込んでおく
var ext = audioExt();
var auClick = new Audio("se/button6." + ext);
var auSwitch = new Audio("se/se_sad05." + ext);
var auError = new Audio("se/se_sad08." + ext);
var turn; // 手番
var context; // キャンバスのコンテキスト
var passButton = new Array(2); // [パス]ボタン
var brd; // 碁盤オブジェクト
/**
* ロード時に碁盤イメージを描画します。
*/
window.onload = function() {
// 黒石の <img id="black"> 取得
passButton[BLACK] = document.getElementById('black');
// 白石の <img id="white"> 取得
passButton[WHITE] = document.getElementById('white');
// キャンバスとコンテキスト取得
var canvas = document.querySelector('#table');
context = canvas.getContext('2d');
// 座標 (270, 50) にタイトルを表示
context.fillStyle = "white";
context.font = "bold 24px Arial";
var title = "Goban 0.3";
var x = context.canvas.width / 2 -
PX_CHAR / 1.8 * title.length / 2;
var y = PX_CHAR * 2;
context.fillText(title, x, y);
// 机の中央に碁盤を表示
brd = new Board(9);
// 碁盤の左に白の碁笥の蓋のイメージを表示
x = Math.floor((brd.boardOffset.x - PX_LID) / 2);
y = brd.boardOffset.y +
Math.floor((brd.boardSize.height - PX_LID) / 2);
drawObject(x, y, Z2_LID, imgl, context);
// 白の碁笥の蓋に黒石のイメージを表示
var r = Math.random() * PX_LID / 3;
var a = Math.random() * 2 * Math.PI;
var xs = x + Math.floor((PX_LID / 2) + r * Math.cos(a) - (PX_STONE / 2));
var ys = y + Math.floor((PX_LID / 2) + r * Math.sin(a) - (PX_STONE / 2));
drawObject(xs, ys, Z2_STONE, imgb, context);
// 左上に白の碁笥のイメージを表示
var xb = x - Math.floor((PX_BOWL - PX_LID) / 2);
var yb = y - PX_BOWL - Z2_BOWL;
drawObject(xb, yb, Z2_BOWL, imgwb, context);
// 碁盤の右に黒の碁笥の蓋のイメージを表示
x = x + brd.boardOffset.x + brd.boardSize.width;
drawObject(x, y, Z2_LID, imgl, context);
// 黒の碁笥の蓋に白石のイメージを表示
r = Math.random() * PX_LID / 3;
a = Math.random() * 2 * Math.PI;
xs = x + Math.floor((PX_LID / 2) + r * Math.cos(a) - (PX_STONE / 2));
ys = y + Math.floor((PX_LID / 2) + r * Math.sin(a) - (PX_STONE / 2));
drawObject(xs, ys, Z2_STONE, imgw, context);
// 右下に黒の碁笥のイメージを表示
xb = x - Math.floor((PX_BOWL - PX_LID) / 2);
yb = y + PX_LID + Z2_BOWL;
drawObject(xb, yb, Z2_BOWL, imgbb, context);
// 碁盤を表示
brd.drawBoard(Z2_BOARD);
// 黒番から
turn = BLACK;
passButton[WHITE].disabled = true;
// クリックイベント登録
canvas.addEventListener("click", onClick, false);
};
/**
* マウスイベントハンドラー
* とりあえず碁石を置きます。
* @since 0.2
*/
function onClick() {
var mx, my;
if (arguments.length == 1) { // Firefox 対応
event = arguments[0];
mx = event.pageX - this.offsetLeft;
my = event.pageY - this.offsetTop;
} else {
mx = event.offsetX;
my = event.offsetY;
}
if (turn == BLACK)
drawObject(mx - (PX_STONE / 2), my - (PX_STONE / 2), Z2_STONE, imgb, context);
else
drawObject(mx - (PX_STONE / 2), my - (PX_STONE / 2), Z2_STONE, imgw, context);
auClick.play();
passButton[turn].disabled = true;
turn = 3 - turn;
passButton[turn].disabled = false;
}
/**
* [パス]ボタンの処理
* @since 0.3
*/
function pass() {
// ボタンの音を鳴らす。
auSwitch.play();
passButton[turn].disabled = true;
turn = 3 - turn;
passButton[turn].disabled = false;
}
/**
* [はじめから]ボタンの処理
* @since 0.3
*/
function restart() {
// ボタンの音を鳴らす。
auSwitch.play();
// 碁盤を表示
brd.drawBoard(Z2_BOARD);
// 黒番から
turn = BLACK;
passButton[WHITE].disabled = true;
passButton[BLACK].disabled = false;
}
/**
* ブラウザで有効なAudioの拡張子を獲得
* @since 0.2
*/
function audioExt() {
var ext = "";
var audio = new Audio();
if (audio.canPlayType("audio/ogg") == 'maybe')
ext="ogg";
else if (audio.canPlayType("audio/mp3") == 'maybe')
ext="mp3";
else if (audio.canPlayType("audio/wav") == 'maybe')
ext="wav";
return ext;
}
/**
* 円形オブジェクトを描画します。
* @param x 左端座標
* @param y 上端座標
* @param s 影の長さ(高さ/2)
* @param img オブジェクトのイメージ
* @param context 描画先のコンテキストを指定します。
* @since 0.1
*/
function drawObject(x, y, s, img, context) {
var r = img.width / 2;
context.beginPath();
context.arc(x + r + s, y + r + s, r, 0, 2 * Math.PI);
context.fillStyle = "black";
context.globalAlpha = 0.5;
context.fill();
context.globalAlpha = 1.0;
context.drawImage(img, x, y);
}
board01.js
goban02.js より碁盤の描画処理をオブジェクト Board として分離しました。
/*
* Copyright (C) 2012 たかはしのんき. All rights reserved.
*
* History:
* 0.1 2012/05/15 新規作成。 goban02.js より独立。
*/
var PIXEL_COL = 22;
var PIXEL_ROW = 24;
var PIXEL_CHAR = 24;
/**
* Board - 碁盤オブジェクト
* @version 0.1
* @author たかはしのんき
*
*/
Board = function(ro) {
this.ro = ro;
this.boardSize = { // 碁盤のサイズ[ピクセル]
width : PIXEL_COL * (ro + 1),
height : PIXEL_ROW * (ro + 1)
};
this.boardOffset = { // 碁盤のテーブルからのオフセット[ピクセル]
x : Math.floor((context.canvas.width - this.boardSize.width) / 2),
y : Math.floor((context.canvas.height
+ PIXEL_CHAR * 2 - this.boardSize.height) / 2)
};
this.gridSize = { // 格子全体のサイズ[ピクセル]
width : PIXEL_COL * (ro - 1) + 2,
height : PIXEL_ROW * (ro - 1) + 2
};
this.gridOffset = { // 格子全体の碁盤からのオフセット[ピクセル]
x : Math.floor((this.boardSize.width - this.gridSize.width) / 2),
y : Math.floor((this.boardSize.height - this.gridSize.height) / 2)
};
};
Board.prototype = {
/**
* 碁盤を描画します。
* @param s 影の長さ(高さ/2)
* @since 0.1
*/
drawBoard : function(s) {
var x = this.boardOffset.x;
var y = this.boardOffset.y;
var ro = this.ro;
var dx = PIXEL_COL;
var dy = PIXEL_ROW;
var width = this.boardSize.width;
var height = this.boardSize.height;
var gwidth = this.gridSize.width;
var gheight = this.gridSize.height;
var gx = x + this.gridOffset.x;
var gy = y + this.gridOffset.y;
// 碁盤の影
context.beginPath();
context.moveTo(x, y);
context.lineTo(x + width, y);
context.lineTo(x + width + s, y + s);
context.lineTo(x + width + s, y + height + s);
context.lineTo(x + s, y + height + s);
context.lineTo(x, y + height);
context.closePath();
context.fillStyle = "black";
context.globalAlpha = 0.5;
context.fill();
// 碁盤
context.beginPath();
context.rect(x, y, width, height);
context.fillStyle = "burlywood";
context.globalAlpha = 1.0;
context.fill();
// 木目
this.drawWoodGrain(x, y, width, height, context);
// 格子
context.fillStyle = "black";
var x1, y1, lwidth;
// (横の格子線)
x1 = gx;
for (var row = 1; row <= ro; row++) {
if (row == 1)
y1 = gy + (row - 1) * dy;
else
y1 = gy + 1 + (row - 1) * dy;
if (row == 1 || row == ro)
lwidth = 2;
else
lwidth = 1;
context.beginPath();
context.rect(x1, y1, gwidth, lwidth);
context.fill();
}
// 縦の格子線
y1 = gy;
for (var col = 1; col <= ro; col++) {
if (col == 1)
x1 = gx + (col - 1) * dx;
else
x1 = gx + 1 + (col - 1) * dx;
if (col == 1 || col == ro)
lwidth = 2;
else
lwidth = 1;
context.beginPath();
context.rect(x1, y1, lwidth, gheight);
context.fill();
}
if (ro % 2 == 1) { // 星
// 天元
this.drawStar(Math.floor(ro / 2) + 1, Math.floor(ro / 2) + 1);
if (ro == 9) { // 3線隅の星
this.drawStar(3, 3);
this.drawStar(ro - 2, 3);
this.drawStar(3, ro - 2);
this.drawStar(ro - 2, ro - 2);
}
if (ro == 13 || ro == 19) { // 4線隅の星
this.drawStar(4, 4);
this.drawStar(ro - 3, 4);
this.drawStar(4, ro - 3);
this.drawStar(ro - 3, ro - 3);
}
if (ro == 19) { // 4線辺の星
this.drawStar(ro / 2 + 1, 4);
this.drawStar(4, ro / 2 + 1);
this.drawStar(ro - 3, ro / 2 + 1);
this.drawStar(ro / 2 + 1, ro - 3);
}
}
},
/**
* 星を描画します。
* @param col 星の桁位置を指定します。
* @param row 星の行位置を指定します。
* @since 0.1
*/
drawStar : function(col, row) {
var x = this.boardOffset.x + this.gridOffset.x + 1 +
(col - 1) * PIXEL_COL - 2;
var y = this.boardOffset.y + this.gridOffset.y + 1 +
(row - 1) * PIXEL_ROW - 2;
context.drawImage(imgs, x, y);
},
/**
* 木目を描画します。
* @param x 左端座標
* @param y 上端座標
* @param width 描画する幅
* @param height 描画する高さ
* @param context 描画先のコンテキストを指定します。
* @since 0.1
*/
drawWoodGrain : function(x, y, width, height, context) {
context.strokeStyle = "rgb(192, 153, 86)"; // 木目の色
var xb0 = x;
var xb1 = x + width;
var yb0 = y;
var yb1 = y + height;
var xo, yo, xw, yw;
var xc = xb0 + (xb1 - xb0) * 5 / 7; // 木目の中心
var yc = yb0 + (yb1 - yb0) * 5 / 4;
for (var rw = 5; rw <= 460; rw += 5) {
xo = xc + rw;
yo = yc;
for (var theta = 0; theta <= 2 * Math.PI;
theta += 2 * Math.PI / 19) {
xw = xc + Math.floor(rw * Math.cos(theta));
yw = yc + 13 * Math.floor(rw * Math.sin(theta));
var xw0 = xo, xw1 = xw, yw0 = yo, yw1 = yw;
if (yw1<yb0 && yb0<yw0) { //上辺でクリッピング
xw1 = xw0+(xw1-xw0)*(yb0-yw0)/(yw1-yw0);
yw1 = yb0;
} else if (yw0<yb0 && yb0<yw1) { //上辺でクリッピング
xw0 = xw1+(xw0-xw1)*(yb0-yw1)/(yw0-yw1);
yw0 = yb0;
}
if (yw1<yb1 && yb1<yw0) { //下辺でクリッピング
xw0 = xw1+(xw0-xw1)*(yb1-yw1)/(yw0-yw1);
yw0 = yb1;
} else if (yw0<yb1 && yb1<yw1) { //下辺でクリッピング
xw1 = xw0+(xw1-xw0)*(yb1-yw0)/(yw1-yw0);
yw1 = yb1;
}
if (xw1<xb0 && xb0<xw0) { //左辺でクリッピング
xw1 = xb0;
yw1 = yw0+(yw1-yw0)*(xb0-xw0)/(xw1-xw0);
} else if (xw0<xb0 && xb0<xw1) { //左辺でクリッピング
xw0 = xb0;
yw0 = yw1+(yw0-yw1)*(xb0-xw1)/(xw0-xw1);
}
if (xw1<xb1 && xb1<xw0) { //右辺でクリッピング
xw0 = xb1;
yw0 = yw1+(yw0-yw1)*(xb1-xw1)/(xw0-xw1);
} else if (xw0<xb1 && xb1<xw1) { //右辺でクリッピング
xw1 = xb1;
yw1 = yw0+(yw1-yw0)*(xb1-xw0)/(xw1-xw0);
}
if (xb0<=xw0 && xw0<=xb1 && xb0<=xw1 && xw1<=xb1 &&
yb0<=yw0 && yw0<=yb1 && yb0<=yw1 && yw1<=yb1) {
context.beginPath();
context.moveTo(xw0, yw0);
context.lineTo(xw1, yw1);
context.stroke();
}
xo = xw;
yo = yw;
}
}
}
};