1 /* 2 * Copyright (c) 2012-2014 Nonki Takahashi. All rights reserved. 3 * 4 * History: 5 * 0.6 2014-01-29 Changed Move objcet interface. 6 * 0.5 2013-06-15 Supported JsDoc Toolkit and score display. 7 * 0.4 2013-04-07 Supported stone number display. 8 * 0.3 2013-03-29 Supported liberty display. 9 * 0.2 2012-05-17 Supported layer and posision object. 10 * 0.1 2012-05-15 Created. Dropped out from goban02.js. Supported star display. 11 */ 12 13 var PIXEL_COL = 22; 14 var PIXEL_ROW = 24; 15 var PIXEL_CHAR = 24; 16 17 /** 18 * @fileOverview Board - Go Board Object 19 * @version 0.6 20 * @author Nonki Takahashi 21 */ 22 23 /** 24 * Creates Go Board Object 25 * @class Represents Go Board 26 * @this {Board} 27 * @param {Number} ro number of lines 28 * @param {Context} context context of the canvas 29 * @property {Number} ro number of lines 30 * @since 0.1 31 */ 32 Board = function(ro, context) { 33 this.ro = ro; 34 this.boardSize = { 35 // size of go board [pixel] 36 width : PIXEL_COL * (ro + 1), 37 height : PIXEL_ROW * (ro + 1) 38 }; 39 this.boardOffset = { 40 // go board offset from table corner [pixel] 41 x : Math.floor((context.canvas.width - this.boardSize.width) / 2), 42 y : Math.floor((context.canvas.height - this.boardSize.height) / 2) 43 }; 44 this.gridSize = { 45 // whole grid size [pixel] 46 width : PIXEL_COL * (ro - 1) + 2, 47 height : PIXEL_ROW * (ro - 1) + 2 48 }; 49 this.gridOffset = { 50 // whole grid offset from go board [pixel] 51 x : Math.floor((this.boardSize.width - this.gridSize.width) / 2), 52 y : Math.floor((this.boardSize.height - this.gridSize.height) / 2) 53 }; 54 // game position object 55 this.p = new Pos(ro); 56 // prisoners (captured stones) 57 this.prisoner = new Array(2); 58 }; 59 60 Board.prototype = { 61 62 /** 63 * Place next stone 64 * @example 65 * brd.move(col, row, context3, context2); 66 * brd.move(PASS, context3, context2); 67 * brd.move(new Move(col, row), context3, context2); 68 * @param {Number} col stone column 69 * @param {Number} row stone row 70 * @param {Context} context3 context for drawing stone 71 * @param {Context} context2 context for drawing shadow 72 * @return {Boolean} true if the move succeeded 73 * @since 0.6 74 */ 75 move : function() { 76 var mv; 77 var context2; 78 var context3; 79 if (arguments.length == 4) { 80 mv = new Move(arguments[0], arguments[1]); 81 context3 = arguments[2]; 82 context2 = arguments[3]; 83 } else if (arguments.length == 3) { 84 if (arguments[0] instanceof Move) 85 mv = arguments[0]; 86 else 87 mv = new Move(arguments[0]); 88 context3 = arguments[1]; 89 context2 = arguments[2]; 90 } 91 // disable current turn pass button 92 passButton[this.p.turn].disabled = true; 93 var turn = this.p.turn; 94 // inhivited point becomes false 95 var moved = this.p.move(mv); 96 if (!mv.isPass) { 97 // setWatch(); // TODO 98 if (moved) { 99 this.prisoner[P_BLACK] = this.p.prisoner[P_BLACK]; 100 this.prisoner[P_WHITE] = this.p.prisoner[P_WHITE]; 101 // rec.move(col, row); // TODO 102 this.drawStone(mv.col, mv.row, turn, context3, context2); 103 auClick.play(); 104 if (this.p.d.abs()) { 105 this.removeStones(this.p.d, this.p.turn, context3, context2); 106 showPrisoner(turn, this.prisoner[turn - 1]); 107 } 108 showScore(BLACK, this.p.liberty[P_BLACK] + this.p.w.inv().abs()); 109 showScore(WHITE, this.p.liberty[P_WHITE] + this.p.b.inv().abs()); 110 } else 111 auError.play(); 112 } 113 // enable next turn pass button 114 passButton[this.p.turn].disabled = false; 115 return moved; 116 }, 117 118 /** 119 * Convert from canvas offset to move 120 * @param {Number} x X co-ordinate on canvas 121 * @param {Number} y Y co-ordinate on canvas 122 * @return {Move} positon to move 123 * @since 0.2 124 */ 125 offsetToMove : function(x, y) { 126 var col = Math.floor((x - this.boardOffset.x - this.gridOffset.x / 2) / PIXEL_COL) + 1; 127 var row = Math.floor((y - this.boardOffset.y - this.gridOffset.y / 2) / PIXEL_ROW) + 1; 128 var mv = new Move(col, row); 129 if (1 <= col && col <= this.ro && 1 <= row && row <= this.ro) 130 mv.onBoard = true; 131 else 132 mv.onBoard = false; 133 return mv; 134 }, 135 136 /** 137 * Restore go board as initial state 138 * @since 0.2 139 */ 140 clear : function() { 141 // game position object 142 this.p.clear(); 143 // prisoners (captured stones) 144 this.prisoner = [0, 0]; 145 }, 146 147 /** 148 * Draw go board 149 * @param {Number} s length of shadow(height / 2) 150 * @param {Context} context context for drawing 151 * @since 0.1 152 */ 153 drawBoard : function(s, context) { 154 var x = this.boardOffset.x; 155 var y = this.boardOffset.y; 156 var ro = this.ro; 157 var dx = PIXEL_COL; 158 var dy = PIXEL_ROW; 159 var width = this.boardSize.width; 160 var height = this.boardSize.height; 161 var gwidth = this.gridSize.width; 162 var gheight = this.gridSize.height; 163 var gx = x + this.gridOffset.x; 164 var gy = y + this.gridOffset.y; 165 // shadow of go board 166 context.beginPath(); 167 context.moveTo(x, y); 168 context.lineTo(x + width, y); 169 context.lineTo(x + width + s, y + s); 170 context.lineTo(x + width + s, y + height + s); 171 context.lineTo(x + s, y + height + s); 172 context.lineTo(x, y + height); 173 context.closePath(); 174 context.fillStyle = "black"; 175 context.globalAlpha = 0.5; 176 context.fill(); 177 // go board 178 context.beginPath(); 179 context.rect(x, y, width, height); 180 context.fillStyle = "burlywood"; 181 context.globalAlpha = 1.0; 182 context.fill(); 183 // wood grain 184 this.drawWoodGrain(x, y, width, height, context); 185 // grid 186 context.fillStyle = "black"; 187 var x1, y1, lwidth; 188 // (horizontal grid) 189 x1 = gx; 190 for (var row = 1; row <= ro; row++) { 191 if (row == 1) 192 y1 = gy + (row - 1) * dy; 193 else 194 y1 = gy + 1 + (row - 1) * dy; 195 if (row == 1 || row == ro) 196 lwidth = 2; 197 else 198 lwidth = 1; 199 context.beginPath(); 200 context.rect(x1, y1, gwidth, lwidth); 201 context.fill(); 202 } 203 // (vertical grid) 204 y1 = gy; 205 for (var col = 1; col <= ro; col++) { 206 if (col == 1) 207 x1 = gx + (col - 1) * dx; 208 else 209 x1 = gx + 1 + (col - 1) * dx; 210 if (col == 1 || col == ro) 211 lwidth = 2; 212 else 213 lwidth = 1; 214 context.beginPath(); 215 context.rect(x1, y1, lwidth, gheight); 216 context.fill(); 217 } 218 if (ro % 2 == 1) { 219 // tengen 220 this.drawStar(Math.floor(ro / 2) + 1, Math.floor(ro / 2) + 1, context); 221 } 222 if (ro == 9) { 223 // stars on 3rd line corner 224 this.drawStar(3, 3, context); 225 this.drawStar(ro - 2, 3, context); 226 this.drawStar(3, ro - 2, context); 227 this.drawStar(ro - 2, ro - 2, context); 228 } 229 if (ro == 13 || ro == 19) { 230 // stars on 4th line corner 231 this.drawStar(4, 4, context); 232 this.drawStar(ro - 3, 4, context); 233 this.drawStar(4, ro - 3, context); 234 this.drawStar(ro - 3, ro - 3, context); 235 } 236 if (ro == 19) { 237 // stars of 4th line edge 238 this.drawStar(Math.floor(ro / 2) + 1, 4, context); 239 this.drawStar(4, Math.floor(ro / 2) + 1, context); 240 this.drawStar(ro - 3, Math.floor(ro / 2) + 1, context); 241 this.drawStar(Math.floor(ro / 2) + 1, ro - 3, context); 242 } 243 }, 244 245 /** 246 * Draw a stone 247 * @param {Number} col column position of the stone 248 * @param {Number} row row posetion of the stone 249 * @param {Number} stone stone color (BLACK or WHITE) 250 * @param {Context} context3 context for drawing stone 251 * @param {Context} context2 context for drawing shadow 252 * @since 0.2 253 */ 254 drawStone : function(col, row, stone, context3, context2) { 255 var x = this.boardOffset.x + this.gridOffset.x + 1 + (col - 1) * PIXEL_COL; 256 var y = this.boardOffset.y + this.gridOffset.y + 1 + (row - 1) * PIXEL_ROW; 257 if (stone == BLACK) 258 drawObject(x - (PX_STONE / 2), y - (PX_STONE / 2), Z2_STONE, imgb, context3, context2); 259 else if (stone == WHITE) 260 drawObject(x - (PX_STONE / 2), y - (PX_STONE / 2), Z2_STONE, imgw, context3, context2); 261 }, 262 263 /** 264 * Remove stones 265 * @param {BVector} d BVecter for captured stones 266 * @param {Number} stone captured stone color (BLACK or WHITE) 267 * @param {Context} context3 context for stone drawn 268 * @param {Context} context2 context for shadow drawn 269 * @since 0.2 270 */ 271 removeStones : function(d, stone, context3, context2) { 272 for (var i = 1; i <= d.order; i++) 273 if (d.getValue(i) == 1) { 274 var mv = this.p.toMove(i); 275 var x = this.boardOffset.x + this.gridOffset.x + 1 + (mv.col - 1) * PIXEL_COL; 276 var y = this.boardOffset.y + this.gridOffset.y + 1 + (mv.row - 1) * PIXEL_ROW; 277 context3.clearRect(x - (PX_STONE / 2), y - (PX_STONE / 2), PIXEL_COL, PIXEL_ROW); 278 context2.clearRect(x - (PX_STONE / 2) + Z2_STONE, y - (PX_STONE / 2) + Z2_STONE, PIXEL_COL, PIXEL_ROW); 279 // draw prisoner on lid of bowl 280 var r = Math.random() * PX_LID / 3; 281 var a = Math.random() * 2 * Math.PI; 282 if (stone == BLACK) { 283 var xs = offsetWLid.x + Math.floor((PX_LID / 2) + r * Math.cos(a) - (PX_STONE / 2)); 284 var ys = offsetWLid.y + Math.floor((PX_LID / 2) + r * Math.sin(a) - (PX_STONE / 2)); 285 drawObject(xs, ys, Z2_STONE, imgb, context2, context2); 286 } else { 287 var xs = offsetBLid.x + Math.floor((PX_LID / 2) + r * Math.cos(a) - (PX_STONE / 2)); 288 var ys = offsetBLid.y + Math.floor((PX_LID / 2) + r * Math.sin(a) - (PX_STONE / 2)); 289 drawObject(xs, ys, Z2_STONE, imgw, context2, context2); 290 } 291 } 292 }, 293 /** 294 * Draw a star 295 * @param {Number} col column position for the star 296 * @param {Number} row row position for the star 297 * @param {Context} context context for drawing star 298 * @since 0.1 299 */ 300 drawStar : function(col, row, context) { 301 var x = this.boardOffset.x + this.gridOffset.x + 1 + (col - 1) * PIXEL_COL - 2; 302 var y = this.boardOffset.y + this.gridOffset.y + 1 + (row - 1) * PIXEL_ROW - 2; 303 context.drawImage(imgs, x, y); 304 }, 305 306 /** 307 * Draw wood grain 308 * @param {Number} x left co-ordinate 309 * @param {Number} y top co-ordinate 310 * @param {Number} width width to draw 311 * @param {Number} height height to draw 312 * @param {Context} context context to draw 313 * @since 0.1 314 */ 315 drawWoodGrain : function(x, y, width, height, context) { 316 // wood grain color 317 context.strokeStyle = "rgb(192, 153, 86)"; 318 var xb0 = x; 319 var xb1 = x + width; 320 var yb0 = y; 321 var yb1 = y + height; 322 var xo, yo, xw, yw; 323 // center of wood grain 324 var xc = xb0 + (xb1 - xb0) * 5 / 7; 325 var yc = yb0 + (yb1 - yb0) * 5 / 4; 326 for (var rw = 5; rw <= 460; rw += 5) { 327 xo = xc + rw; 328 yo = yc; 329 for (var theta = 0; theta <= 2 * Math.PI; theta += 2 * Math.PI / 19) { 330 xw = xc + Math.floor(rw * Math.cos(theta)); 331 yw = yc + 13 * Math.floor(rw * Math.sin(theta)); 332 var xw0 = xo, xw1 = xw, yw0 = yo, yw1 = yw; 333 if (yw1 < yb0 && yb0 < yw0) { 334 // clipping on top edge 335 xw1 = xw0 + (xw1 - xw0) * (yb0 - yw0) / (yw1 - yw0); 336 yw1 = yb0; 337 } else if (yw0 < yb0 && yb0 < yw1) { 338 // clipping on top edge 339 xw0 = xw1 + (xw0 - xw1) * (yb0 - yw1) / (yw0 - yw1); 340 yw0 = yb0; 341 } 342 if (yw1 < yb1 && yb1 < yw0) { 343 // clipping on bottom edge 344 xw0 = xw1 + (xw0 - xw1) * (yb1 - yw1) / (yw0 - yw1); 345 yw0 = yb1; 346 } else if (yw0 < yb1 && yb1 < yw1) { 347 // clipping on bottom edge 348 xw1 = xw0 + (xw1 - xw0) * (yb1 - yw0) / (yw1 - yw0); 349 yw1 = yb1; 350 } 351 if (xw1 < xb0 && xb0 < xw0) { 352 // clipping on left edge 353 xw1 = xb0; 354 yw1 = yw0 + (yw1 - yw0) * (xb0 - xw0) / (xw1 - xw0); 355 } else if (xw0 < xb0 && xb0 < xw1) { 356 // clipping on left edge 357 xw0 = xb0; 358 yw0 = yw1 + (yw0 - yw1) * (xb0 - xw1) / (xw0 - xw1); 359 } 360 if (xw1 < xb1 && xb1 < xw0) { 361 // clipping on right edge 362 xw0 = xb1; 363 yw0 = yw1 + (yw0 - yw1) * (xb1 - xw1) / (xw0 - xw1); 364 } else if (xw0 < xb1 && xb1 < xw1) { 365 // clipping on right edge 366 xw1 = xb1; 367 yw1 = yw0 + (yw1 - yw0) * (xb1 - xw0) / (xw1 - xw0); 368 } 369 if (xb0 <= xw0 && xw0 <= xb1 && xb0 <= xw1 && xw1 <= xb1 && yb0 <= yw0 && yw0 <= yb1 && yb0 <= yw1 && yw1 <= yb1) { 370 context.beginPath(); 371 context.moveTo(xw0, yw0); 372 context.lineTo(xw1, yw1); 373 context.stroke(); 374 } 375 xo = xw; 376 yo = yw; 377 } 378 } 379 } 380 }; 381