1 /* 2 * Copyright (c) 2014 Nonki Takahashi. All rights reserved. 3 * 4 * History: 5 * 0.1 2014-01-21 Created from Simulator 0.4. 6 */ 7 8 /** 9 * @fileOverview Viewer - SGF Viewer 10 * @name viewer01.js 11 * @version 0.1 12 * @author Nonki Takahashi 13 */ 14 15 // common constants 16 var BLACK = 1; 17 var WHITE = 2; 18 // diameter of bowl [pixel] 19 var PX_BOWL = 134; 20 // height of bowl / 2 [pixel] 21 var Z2_BOWL = 20; 22 // diameter of lid [pixel] 23 var PX_LID = 110; 24 // height of lid / 2 [pixel] 25 var Z2_LID = 8; 26 // diameter of stone [pixel] 27 var PX_STONE = 22; 28 // height of stone / 2 [pixel] 29 var Z2_STONE = 2; 30 // diameter of star [pixel] 31 var PX_STAR = 5; 32 // height of character [pixel] 33 var PX_CHAR = 24; 34 // height of board / 2 [pixel] 35 var Z2_BOARD = 6; 36 37 // preload images into cache 38 // black bowl 39 var imgbb = new Image(PX_BOWL, PX_BOWL); 40 imgbb.src = "img/blackbowl134.png"; 41 // white bowl 42 var imgwb = new Image(PX_BOWL, PX_BOWL); 43 imgwb.src = "img/whitebowl134.png"; 44 // lid 45 var imgl = new Image(PX_LID, PX_LID); 46 imgl.src = "img/lid110.png"; 47 // white stone 48 var imgb = new Image(PX_STONE, PX_STONE); 49 imgb.src = "img/black22.png"; 50 // black stone 51 var imgw = new Image(PX_STONE, PX_STONE); 52 imgw.src = "img/white22.png"; 53 // star 54 var imgs = new Image(PX_STAR, PX_STAR); 55 imgs.src = "img/star5.png"; 56 57 // preload sound effect into cache 58 var ext = audioExt(); 59 var auClick = new Audio("se/button6." + ext); 60 var auSwitch = new Audio("se/se_sad05." + ext); 61 var auError = new Audio("se/se_sad08." + ext); 62 63 // number of lines (ro) 64 var ro = 9; 65 // context for campus of stone layer 66 var context3; 67 // context for campus of shade layer 68 var context2; 69 // context for campus of board 70 var context; 71 // [pass] button 72 var passButton = new Array(2); 73 // board object 74 var brd; 75 // black lid offset 76 var offsetBLid = new Object(); 77 // white lid offset 78 var offsetWLid = new Object(); 79 // interval 80 var ms = 500; 81 var outInterval; 82 var lastPass = false; 83 var inGame = true; 84 var rec; 85 // program information 86 var programName = "Viewer"; 87 var programVer = "0.1"; 88 89 /** 90 * Draw board image at HTML page load 91 */ 92 window.onload = function() { 93 // get <input id="black"> for black stone 94 passButton[BLACK] = document.getElementById("black"); 95 // get <input id="white"> for white stone 96 passButton[WHITE] = document.getElementById("white"); 97 98 // get canvases and contexts 99 var canvas = document.querySelector("#table"); 100 context = canvas.getContext("2d"); 101 var canvas3 = document.querySelector("#stones"); 102 context3 = canvas3.getContext("2d"); 103 var canvas2 = document.querySelector("#shadow"); 104 context2 = canvas2.getContext("2d"); 105 // draw a Go set and program title 106 drawNewGoSet(); 107 // from black (so disable white pass button) 108 passButton[WHITE].disabled = true; 109 // register a click event 110 canvas3.addEventListener("click", onClick, false); 111 // select input element described as for attribute 112 var inInterval = document.querySelector("#interval"); 113 // get output element 114 outInterval = document.querySelector("output"); 115 // set onhange event handler 116 inInterval.onchange = function(e) { 117 // assign input element value to output element 118 ms = e.target.value; 119 outInterval.value = ms; 120 }; 121 }; 122 123 /** 124 * Draw board, bowls and title  125 */ 126 function drawNewGoSet() { 127 // display title 128 context.fillStyle = "white"; 129 context.font = "bold 24px Arial"; 130 var title = programName + " " + programVer; 131 var x = context.canvas.width / 2 - PX_CHAR / 1.8 * title.length / 2; 132 var y = PX_CHAR * 2; 133 context.fillText(title, x, y); 134 // draw a Go board at the center of the desk 135 brd = new Board(ro, context); 136 // draw a white lid of bowl on the left of the board 137 offsetWLid.x = Math.floor((brd.boardOffset.x - PX_LID) / 2); 138 offsetWLid.y = brd.boardOffset.y + Math.floor((brd.boardSize.height - PX_LID) / 2); 139 drawObject(offsetWLid.x, offsetWLid.y, Z2_LID, imgl, context, context); 140 // draw a white bowl on the top left corner 141 var xb = offsetWLid.x - Math.floor((PX_BOWL - PX_LID) / 2); 142 var yb = offsetWLid.y - PX_BOWL - Z2_BOWL; 143 drawObject(xb, yb, Z2_BOWL, imgwb, context, context); 144 // draw a black lid of bowl on the right of the board 145 offsetBLid.x = offsetWLid.x + brd.boardOffset.x + brd.boardSize.width; 146 offsetBLid.y = offsetWLid.y; 147 drawObject(offsetBLid.x, offsetBLid.y, Z2_LID, imgl, context, context); 148 // draw a black bowl on the bottom right corner 149 xb = offsetBLid.x - Math.floor((PX_BOWL - PX_LID) / 2); 150 yb = offsetBLid.y + PX_LID + Z2_BOWL; 151 drawObject(xb, yb, Z2_BOWL, imgbb, context, context); 152 // draw board 153 brd.drawBoard(Z2_BOARD, context); 154 } 155 156 /** 157 * Show number of prisoners 158 * @param {Number} stone color of stone (BLACK or WHITE) 159 * @param {Number} prisoner number of prisoners 160 * @since 0.1 161 */ 162 function showPrisoner(stone, prisoner) { 163 if (stone == BLACK) { 164 var p = document.getElementById("pb"); 165 } else { 166 var p = document.getElementById("pw"); 167 } 168 p.firstChild.nodeValue = prisoner; 169 } 170 171 /** 172 * Show score 173 * @param {Number} stone color of stone (BLACK or WHITE) 174 * @param {Number} score score 175 * @since 0.1 176 */ 177 function showScore(stone, score) { 178 if (stone == BLACK) { 179 var p = document.getElementById("sb"); 180 } else { 181 var p = document.getElementById("sw"); 182 } 183 p.firstChild.nodeValue = score; 184 } 185 186 /** 187 * Mouse event handler 188 * for place a stone 189 * @since 0.1 190 */ 191 function onClick() { 192 var event = arguments[0]; 193 var mx = event.pageX - this.parentNode.offsetLeft; 194 var my = event.pageY - this.parentNode.offsetTop; 195 var mv = brd.offsetToMove(mx, my); 196 if (mv.onBoard) { 197 brd.move(mv.col, mv.row, context3, context2); 198 } else { 199 if (brd.p.turn == BLACK) 200 drawObject(mx - (PX_STONE / 2), my - (PX_STONE / 2), Z2_STONE, imgb, context2, context2); 201 else 202 drawObject(mx - (PX_STONE / 2), my - (PX_STONE / 2), Z2_STONE, imgw, context2, context2); 203 auError.play(); 204 } 205 } 206 207 /** 208 * [Pass] button event handler 209 * @since 0.1 210 */ 211 function onPass() { 212 // sound of button 213 auSwitch.play(); 214 brd.move(PASS, context3, context2); 215 } 216 217 /** 218 * Playout timer event handler 219 * @since 0.1 220 */ 221 function playout() { 222 if (0 < brd.p.c.abs()) { 223 // if there are possible moves, select one of them randomly 224 // TODO check whether this random number works correctly 225 var i = Math.floor(Math.random() * brd.p.order) + 1; 226 for (; ; i++) { 227 if (brd.p.order < i) 228 i = 1; 229 if (brd.p.c.getValue(i) == 1) 230 break; 231 } 232 var mv = brd.p.toMove(i); 233 brd.move(mv, context3, context2); 234 // rec.move(mv); // TODO involve if needed 235 lastPass = false; 236 } else { 237 // if there are no possible moves, pass 238 auSwitch.play(); 239 brd.move(PASS, context3, context2); 240 // rec.move(MOVE_PASS); // TODO involve if needed 241 // both pass becomes game end 242 if (lastPass) 243 inGame = false; 244 lastPass = true; 245 } 246 if (inGame) 247 window.setTimeout(playout, ms); 248 } 249 250 /** 251 * Replay timer event handler 252 * @since 0.1 253 */ 254 function replay() { 255 if (!rec.eod()) { 256 var mv = rec.replay(); 257 brd.move(mv, context3, context2); 258 window.setTimeout(replay, ms); 259 } 260 } 261 262 /** 263 * [Play] button event handler 264 * @since 0.1 265 */ 266 function onPlay() { 267 // sound of button 268 auSwitch.play(); 269 // clear stone and shadow layer 270 context3.clearRect(0, 0, context3.canvas.width, context3.canvas.height); 271 context2.clearRect(0, 0, context2.canvas.width, context2.canvas.height); 272 // clear prisoners 273 showPrisoner(BLACK, 0); 274 showPrisoner(WHITE, 0); 275 // clear scores 276 showScore(BLACK, 0); 277 showScore(WHITE, 0); 278 // clear board 279 context.clearRect(0, 0, context.canvas.width, context.canvas.height); 280 // ro change test // TODO 281 // ro = Math.round(Math.random()) * 10 + 9; 282 drawNewGoSet(); 283 // from black 284 passButton[BLACK].disabled = false; 285 passButton[WHITE].disabled = true; 286 // get game record from SGF 287 textbox = document.querySelector("#sgf"); 288 rec = new Rec(textbox.innerHTML); 289 rec.rewind(); 290 // play game record 291 lastPass = false; 292 inGame = true; 293 window.setTimeout(replay, ms); 294 } 295 296 /** 297 * Get available Audio extension for current browser 298 * @returns {String} available Audio extension 299 * @since 0.1 300 */ 301 function audioExt() { 302 var ext = ""; 303 var audio = new Audio(); 304 if (audio.canPlayType("audio/ogg") == "maybe") 305 ext = "ogg"; 306 else if (audio.canPlayType("audio/mp3") == "maybe") 307 ext = "mp3"; 308 else if (audio.canPlayType("audio/wav") == "maybe") 309 ext = "wav"; 310 return ext; 311 } 312 313 /** 314 * Draw circlar object 315 * @param {Number} x left co-ordiinate 316 * @param {Number} y top co-ordinate 317 * @param {Number} s length of shadow (height / 2) 318 * @param {Image} img object image 319 * @param {Context} context3 context for object 320 * @param {Context} context2 context for shadow 321 * @since 0.1 322 */ 323 function drawObject(x, y, s, img, context3, context2) { 324 var r = img.width / 2; 325 context2.beginPath(); 326 context2.arc(x + r + s, y + r + s, r, 0, 2 * Math.PI); 327 context2.fillStyle = "black"; 328 context2.globalAlpha = 0.5; 329 context2.fill(); 330 context3.globalAlpha = 1.0; 331 context3.drawImage(img, x, y); 332 } 333