backgammon.html 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Backgammon</title>
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <script src="../js/snap.svg-min.js"></script>
  7. <style>
  8. /*
  9. .board-container {
  10. display: inline-block;
  11. position: relative;
  12. width: 80%;
  13. padding-bottom: 47%;
  14. vertical-align: middle;
  15. overflow: hidden;
  16. }
  17. */
  18. body {
  19. margin: 0px;
  20. box-sizing: border-box;
  21. max-height: 100vh;
  22. }
  23. .game-ui {
  24. display: flex;
  25. flex-direction: column;
  26. justify-content: space-between;
  27. box-sizing: border-box;
  28. max-height: 100vh;
  29. flex-basis: auto;
  30. padding: 20px;
  31. }
  32. .toolbar {
  33. display: flex;
  34. flex-direction: row;
  35. flex-grow: 2;
  36. justify-content: space-between;
  37. align-items: baseline;
  38. }
  39. .toolbar-button {
  40. flex: 1 1 100px;
  41. margin: 2px;
  42. height: 40px;
  43. width: 50px;
  44. }
  45. .player-info {
  46. display: flex;
  47. flex-direction: column;
  48. justify-content: space-between;
  49. }
  50. .match-info {
  51. display: flex;
  52. box-sizing: border-box;
  53. flex-direction: row;
  54. justify-content: space-between;
  55. margin-right: 50px;
  56. }
  57. .svg-board {
  58. flex: 1 2 auto;
  59. padding: 5px;
  60. }
  61. .hint {
  62. height: 1em;
  63. }
  64. </style>
  65. </head>
  66. <body>
  67. <div class="game-ui">
  68. <div id="toolbar" class="toolbar" style="padding-bottom: 10px;">
  69. <!-- <svg id="player" width=20 height=20> </svg> -->
  70. <span id="player"></span>
  71. <button id="double" type="button" class="toolbar-button" onclick="graphicToolbar.onDouble()" disabled="true">Double</button>
  72. <button id="accept" type="button" class="toolbar-button" onclick="graphicToolbar.onAccept()" disabled="true">Accept</button>
  73. <button id="refuse" type="button" class="toolbar-button" onclick="graphicToolbar.onRefuse()" disabled="true">Refuse</button>
  74. <button id="undo" type="button" class="toolbar-button" onclick="graphicToolbar.onUndo()" disabled="false">Undo</button>
  75. <button id="direction" type="button" class="toolbar-button" onclick="graphicToolbar.onChangeDirection()">Direction</button>
  76. </div>
  77. <svg id="board" viewBox="0 0 520 300" class="svg-board"></svg>
  78. <p id="hint" class="hint"></p>
  79. <div class="match-info">
  80. <div class="player-info">
  81. <div class="player-name">White <span id="white-name"></span></div>
  82. <div class="score">Score: <span id="white-score"></span> <span id="white-away"></span></div>
  83. <div class="pips">Pips: <span id="white-pips"></span> (<span id="white-diff"></span>)</div>
  84. </div>
  85. <div class="player-info">
  86. <div class="player-name">Black <span id="black-name"></span></div>
  87. <div class="score">Score: <span id="black-score"></span> <span id="black-away"></span></div>
  88. <div class="pips">Pips: <span id="black-pips"></span> (<span id="black-diff"></span>)</div>
  89. </div>
  90. <div class "match-options">
  91. <div class="match-limit">
  92. Match: <span id="match-limit"></span>
  93. </div>
  94. <div class="match-misc">
  95. <span id="match-misc"></span>
  96. </div>
  97. </div>
  98. </div>
  99. </div>
  100. <div id="status" hidden="true"></div>
  101. <script>
  102. "use strict";
  103. function Board(game, ui, boardPic) {
  104. this.pic = boardPic;
  105. this.game = game;
  106. this.ui = ui;
  107. var self = this;
  108. this.clockwise = true;
  109. this.orientation = "white";
  110. this.onChangeDirection = function() {
  111. self.clockwise = !self.clockwise;
  112. self.refresh();
  113. }
  114. this.onPointClick = function(p) {
  115. var a = physicalToAbsolute(p, self.clockwise, self.orientation);
  116. if (ui.checkersEnabled) {
  117. let canMove = game.canMoveFrom(a);
  118. if (canMove === "swap") game.swapDice();
  119. if (canMove) ui.putEvent(moveFromEvent(a, game));
  120. }
  121. };
  122. this.onBarClick = function() {
  123. if (ui.checkersEnabled) {
  124. if (ui.checkersEnabled) {
  125. let canMove = game.canMoveFrom("bar");
  126. if (canMove === "swap") game.swapDice();
  127. if (canMove) ui.putEvent(moveFromBarEvent(game));
  128. }
  129. }
  130. };
  131. this.onRightPaneClick = function() {
  132. if (ui.moveCompletionEnabled) {
  133. ui.putEvent(finishMoveEvent());
  134. } else if (ui.offerDoubleEnabled) {
  135. ui.putEvent(offerDoubleEvent("no"));
  136. } else if (game.canSwapDice()) {
  137. game.swapDice();
  138. self.refresh();
  139. }
  140. }
  141. this.onTrayClick = function(ID) {
  142. if (ui.doublingEnabled && ID === self.cubeTrayID[PLAYER]()) {
  143. ui.putEvent(offerDoubleEvent("yes"));
  144. }
  145. }
  146. this.plug = function() {
  147. var foo = function() {};
  148. this.pic.onPointClick = this.onPointClick; // phys_i
  149. this.pic.onBarClick = this.onBarClick; // ()
  150. this.pic.onRightPaneClick = this.onRightPaneClick; // ()
  151. this.pic.onTrayClick = this.onTrayClick; // phys_i
  152. this.setCheckersBar = this.pic.setCheckersBar || foo; // k_white, k_black
  153. this.setCheckersPoint = this.pic.setCheckersPoint || foo; // phys_i, n, player
  154. this.setCheckersTray = this.pic.setCheckersTray || foo; // phys_i, n, player
  155. this.setCubeLeft = this.pic.setCubeLeft || foo; // val
  156. this.setCubeRight = this.pic.setCubeRight || foo; // val
  157. this.setCubeLeftPane = this.pic.setCubeLeftPane || foo; // val
  158. this.setCubeRightPane = this.pic.setCubeRightPane || foo; // val
  159. this.setCubeTray = this.pic.setCubeTray || foo; // phys_i, val
  160. this.hideCube = this.pic.hideCube || foo; // ()
  161. this.setDiceLeft = this.pic.setDiceLeft || foo; // d1, opacity1, d2, opacity2; opacity: "max", "half", "min"
  162. this.setDiceRight = this.pic.setDiceRight || foo;
  163. this.hideDice = this.pic.hideDice || foo; // ()
  164. }
  165. this.refresh = function() {
  166. // refresh checkers on the points
  167. for (let i = 0; i < 24; ++i) {
  168. let a = physicalToAbsolute(i, this.clockwise, this.orientation);
  169. this.setCheckersPoint(i, game.board.points[a].checkers, game.board.points[a].player);
  170. }
  171. // refresh checkers on the bar
  172. this.setCheckersBar(game.board.bar.white, game.board.bar.black);
  173. this.hideDice();
  174. this.hideCube();
  175. // refresh the trays
  176. var offWhite = this.bearoffTrayID.white();
  177. var offBlack = this.bearoffTrayID.black();
  178. var cubeLocation = this.cubeLocation();
  179. for (let i = 0; i < 4; ++i) {
  180. if (i === offWhite) {
  181. this.setCheckersTray(i, game.board.off.white, "white");
  182. } else if (i === offBlack) {
  183. this.setCheckersTray(i, game.board.off.black, "black");
  184. } else if (i === cubeLocation) {
  185. this.setCubeTray(i, game.cube);
  186. } else {
  187. this.setCheckersTray(i, 0, null);
  188. }
  189. }
  190. // Dice go before cube so that we could hide them without hiding the cube
  191. // It turns out this.hideDice() removes the cube as well.
  192. // Is this a bug or not?
  193. if (!game.dice) this.hideDice()
  194. else if (!game.turn) {
  195. if (this.orientation === "white") {
  196. this.setDiceRight(game.dice[0], "max");
  197. this.setDiceLeft(game.dice[1], "max");
  198. } else {
  199. this.setDiceRight(game.dice[1], "max");
  200. this.setDiceLeft(game.dice[0], "max");
  201. }
  202. } else {
  203. let op0 = "max", op1 = "max";
  204. if (game.turn === PLAYER) {
  205. if (game.dice[0] != game.dice[1]) {
  206. if (!game.restDice.includes(game.dice[0])) op0 = "min";
  207. if (!game.restDice.includes(game.dice[1])) op1 = "min";
  208. } else {
  209. let len = game.restDice.length;
  210. if (len === 3) op0 = "half"
  211. else if (len === 2) op0 = "min"
  212. else if (len === 1) {
  213. op0 = "min";
  214. op1 = "half";
  215. } else if (len === 0) op0 = op1 = "min";
  216. }
  217. }
  218. if (game.turn === this.orientation) this.setDiceRight(game.dice[0], op0, game.dice[1], op1)
  219. else this.setDiceLeft(game.dice[0], op0, game.dice[1], op1);
  220. }
  221. if (cubeLocation === "left") this.setCubeLeft(game.cube)
  222. else if (cubeLocation === "right") this.setCubeRight(game.cube)
  223. else if (cubeLocation === "leftPane") this.setCubeLeftPane(2*game.cube)
  224. else if (cubeLocation === "rightPane") this.setCubeRightPane(2*game.cube)
  225. else if (cubeLocation === null) this.hideCube();
  226. }
  227. this.bearoffTrayID = {
  228. white: function() {
  229. return (self.orientation === "white") ? (self.clockwise ? 3 : 2) : (self.clockwise ? 0 : 1);
  230. },
  231. black: function() {
  232. return (self.orientation === "black") ? (self.clockwise ? 3 : 2) : (self.clockwise ? 0 : 1);
  233. }
  234. }
  235. // Returns the storage number, "left", "right", or null.
  236. this.cubeLocation = function() {
  237. if (!game.cube) return null
  238. else if (game.isDoubling === PLAYER) return "leftPane"
  239. else if (game.isDoubling) return "rightPane"
  240. else if (!game.cubeOwner) {
  241. if (this.clockwise) return "right"
  242. else return "left";
  243. } else if (game.cubeOwner === this.orientation) {
  244. if (this.clockwise) return 2
  245. else return 3;
  246. } else {
  247. if (this.clockwise) return 1
  248. else return 0;
  249. }
  250. }
  251. this.cubeTrayID = {
  252. white: function() {
  253. return (self.orientation === "white") ? (self.clockwise ? 2 : 3) : (self.clockwise ? 1 : 0);
  254. },
  255. black: function() {
  256. return (self.orientation === "black") ? (self.clockwise ? 2 : 3) : (self.clockwise ? 1 : 0);
  257. }
  258. }
  259. }
  260. function normalizeCube(value) {
  261. return (value === 1) ? 64 : value;
  262. }
  263. function die(pic, x, y, a, r, alpha, value, faceColour, pipColour) {
  264. var g = pic.g();
  265. var face = pic.rect(x - 0.5*a, y - 0.5*a, a, a, 5).attr({
  266. fill: faceColour
  267. }).appendTo(g);
  268. var pip = pic.circle(x, y, r).attr({
  269. fill: pipColour
  270. }).appendTo(g);
  271. var shift = alpha*a* 0.5;
  272. if (value === 1) {
  273. } else if (value === 2) {
  274. pip.clone().transform(Snap.matrix().translate(shift, shift));
  275. pip.transform(Snap.matrix().translate(-shift, -shift));
  276. } else if (value === 3) {
  277. pip.clone().transform(Snap.matrix().translate(shift, shift));
  278. pip.clone().transform(Snap.matrix().translate(-shift, -shift));
  279. } else if (value === 4) {
  280. pip.clone().transform(Snap.matrix().translate(shift, shift));
  281. pip.clone().transform(Snap.matrix().translate(-shift, -shift));
  282. pip.clone().transform(Snap.matrix().translate(-shift, shift));
  283. pip.transform(Snap.matrix().translate(shift, -shift));
  284. } else if (value === 5) {
  285. pip.clone().transform(Snap.matrix().translate(shift, shift));
  286. pip.clone().transform(Snap.matrix().translate(-shift, -shift));
  287. pip.clone().transform(Snap.matrix().translate(-shift, shift));
  288. pip.clone().transform(Snap.matrix().translate(shift, -shift));
  289. } else if (value === 6) {
  290. pip.clone().transform(Snap.matrix().translate(shift, shift));
  291. pip.clone().transform(Snap.matrix().translate(-shift, -shift));
  292. pip.clone().transform(Snap.matrix().translate(-shift, shift));
  293. pip.clone().transform(Snap.matrix().translate(shift, -shift));
  294. pip.clone().transform(Snap.matrix().translate(shift, 0));
  295. pip.transform(Snap.matrix().translate(-shift, 0));
  296. }
  297. return g;
  298. };
  299. function cube(pic, x, y, a, value, bgColour, fgColour, orientation) {
  300. a = a * 0.9;
  301. var g = pic.g();
  302. pic.rect(x - 0.5*a, y - 0.5*a, a, a, 5).attr({
  303. fill: bgColour
  304. }).appendTo(g);
  305. var label = pic.text(x, y, value).attr({
  306. textAnchor: "middle",
  307. alignmentBaseline: "central",
  308. fill: "beige"
  309. });
  310. var t = Snap.matrix();
  311. if (orientation === "W") t.rotate(-90, x, y)
  312. else if (orientation === "S") t.rotate(180, x, y)
  313. else if (orientation === "E") t.rotate(90, x, y)
  314. label.transform(t).appendTo(g);
  315. return g;
  316. };
  317. function moveFromEvent(p, game) {
  318. return moveEvent(p + 1, game.restDice[0]);
  319. }
  320. function moveFromBarEvent(game) {
  321. return moveEvent("bar", game.restDice[0]);
  322. }
  323. function finishMoveEvent() {
  324. return ["f"];
  325. }
  326. function undoEvent() {
  327. return ["u"];
  328. }
  329. function offerDoubleEvent(yesNo) {
  330. return ["offer-double", yesNo];
  331. }
  332. function acceptDoubleEvent(yesNo) {
  333. return ["accept-double", yesNo];
  334. }
  335. function SVGPoint(board, ID, checkerCount, player) {
  336. var self = this;
  337. var g = board.paper.g();
  338. g.rect(pointX(board, ID) - 1/2*board.params.pointWidth,
  339. (ID < 12) ? 0 : board.params.height - board.params.pointHeight,
  340. 0.99*board.params.pointWidth, board.params.pointHeight)
  341. .attr({
  342. fillOpacity: 0
  343. })
  344. pointPic(board, ID).appendTo(g);
  345. var checkersToDraw = Math.min(checkerCount, maxCirclesOnPoint);
  346. var label = (checkerCount > maxCirclesOnPoint) ? checkerCount : null;
  347. var direction = pointDirection(ID);
  348. var baseX = pointX(board, ID);
  349. var baseY = pointBaseY(board, ID);
  350. for (let i = 0; i < checkersToDraw; ++i) {
  351. checkerPic(board, player, baseX,
  352. baseY + direction * (2*i+1) * board.params.checkerRadius).appendTo(g);
  353. }
  354. if (checkersToDraw > 0 && label) {
  355. textLabel(board, baseX, baseY + direction * (2 * checkersToDraw - 1) * board.params.checkerRadius, label).appendTo(g);
  356. }
  357. g.click(function () { board.onPointClick(ID) });
  358. return g;
  359. }
  360. function checkerPic(board, player, x, y) {
  361. return board.paper.circle(x, y, board.params.checkerRadius).attr({
  362. fill: board.params.checkerColour[player]
  363. });
  364. }
  365. function textLabel(board, x, y, text) {
  366. return board.paper.text(x, y, text).attr({
  367. textAnchor: "middle",
  368. alignmentBaseline: "central"
  369. });
  370. };
  371. function SVGBar(board, white, black) {
  372. var g = board.paper.g();
  373. // background
  374. board.paper.rect(7 * board.params.pointWidth + 2 * board.params.borderWidth, 0, board.params.barWidth, board.params.height)
  375. .attr({
  376. fill: board.params.borderColour
  377. }).appendTo(g);
  378. // TODO text labels
  379. var setCheckers = function(k, player) {
  380. var x = 7 * board.params.pointWidth + 2 * board.params.borderWidth + 0.5 * board.params.barWidth;
  381. var topY = (player == board.orientation) ? 0.33 * board.params.height : 0.67 * board.params.height;
  382. var dir = (player == board.orientation) ? -1 : 1;
  383. for (let i = 0; i < k; ++i) {
  384. checkerPic(board, player, x,
  385. topY + dir * (2*i+1) * board.params.checkerRadius).appendTo(g);
  386. }
  387. };
  388. setCheckers(white, "white");
  389. setCheckers(black, "black");
  390. g.click(board.onBarClick);
  391. return g;
  392. }
  393. function Pane(board, which, d0, op0, d1, op1) {
  394. var g = board.paper.g();
  395. if (which === "right") g.click(board.onRightPaneClick);
  396. var x0 = (which === "left") ? board.params.pointWidth + 2 * board.params.borderWidth : 7 * board.params.pointWidth + 2 * board.params.borderWidth + board.params.barWidth;
  397. var y0 = board.params.borderWidth + board.params.pointHeight;
  398. // background
  399. board.paper.rect(x0, y0,
  400. 6 * board.params.pointWidth, board.params.height - 2*board.params.pointHeight)
  401. .attr({
  402. fillOpacity: 0
  403. }).appendTo(g);
  404. // two dice
  405. if (d0 && d1) {
  406. let a = 30;
  407. let r = 3;
  408. let alpha = 0.6;
  409. let faceColour = "red";
  410. let pipColour = "white";
  411. let x1 = x0 + 3 * board.params.pointWidth;
  412. let y1 = 0.5 * board.params.height;
  413. let delta = 0.75*a;
  414. let pic0 = die(board.paper, x1 - delta, y1, a, r, alpha, d0, faceColour, pipColour);
  415. let pic1 = die(board.paper, x1 + delta, y1, a, r, alpha, d1, faceColour, pipColour);
  416. if (op0 === "min") {
  417. pic0.attr({
  418. fillOpacity: 0.33
  419. });
  420. } else if (op0 === "half") {
  421. pic0.attr({
  422. fillOpacity: 0.67
  423. });
  424. }
  425. if (op1 === "min") {
  426. pic1.attr({
  427. fillOpacity: 0.33
  428. });
  429. } else if (op1 === "half") {
  430. pic1.attr({
  431. fillOpacity: 0.67
  432. });
  433. }
  434. pic0.appendTo(g);
  435. pic1.appendTo(g);
  436. }
  437. // one die
  438. // I've copied & pasted the above. :blush: I promise to refactor it some day.
  439. if (d0 && !d1) {
  440. let a = 30;
  441. let r = 3;
  442. let alpha = 0.6;
  443. let faceColour = "red";
  444. let pipColour = "white";
  445. let x1 = x0 + 3 * board.params.pointWidth;
  446. let y1 = 0.5 * board.params.height;
  447. let pic = die(board.paper, x1, y1, a, r, alpha, d0, faceColour, pipColour);
  448. pic.appendTo(g);
  449. }
  450. return g;
  451. }
  452. function Tray(board, ID, checkerCount, player) {
  453. var g = board.paper.g();
  454. g.click(function() {board.onTrayClick(ID);});
  455. var x = (ID === 0 || ID === 3) ? board.params.borderWidth : 13*board.params.pointWidth + board.params.barWidth + 3 * board.params.borderWidth;
  456. var y = (ID === 0 || ID === 1) ? board.params.borderWidth : board.params.height - board.params.borderWidth - board.params.pointHeight;
  457. // background
  458. g.rect(x, y, board.params.pointWidth, board.params.pointHeight)
  459. .attr({
  460. fill: board.params.backgroundColour
  461. });
  462. // checkers
  463. var checkersToDraw = Math.min(checkerCount, maxCirclesOnPoint);
  464. var label = (checkerCount > maxCirclesOnPoint) ? checkerCount : null;
  465. var direction = (ID < 2) ? 1 : -1;
  466. var baseX = x + 0.5 * board.params.pointWidth;
  467. var baseY = (ID === 0 || ID === 1) ? board.params.borderWidth : board.params.height - board.params.borderWidth;
  468. for (let i = 0; i < checkersToDraw; ++i) {
  469. checkerPic(board, player, baseX,
  470. baseY + direction * (2*i+1) * board.params.checkerRadius).appendTo(g);
  471. }
  472. if (checkersToDraw > 0 && label) {
  473. textLabel(board, baseX, baseY + direction * (2 * checkersToDraw - 1) * board.params.checkerRadius, label).appendTo(g);
  474. }
  475. return g;
  476. }
  477. function SVGBoard(paper) {
  478. this.params = {
  479. width: 520,
  480. height: 300,
  481. pointWidth: 30,
  482. pointHeight: 120,
  483. barWidth: 60,
  484. blackCheckerColour: "#753421",
  485. checkerColour: {
  486. black: "#753421",
  487. white: "plum"
  488. },
  489. checkerRadius: 10,
  490. maxCheckersOnPoint: 6,
  491. evenColour: "#669900",
  492. oddColour: "#6699ff",
  493. borderColour: "#ffcc00",
  494. borderWidth: 10,
  495. backgroundColour: "beige"
  496. };
  497. var self = this;
  498. this.paper = paper;
  499. this.onBarClick = function() {};
  500. this.onPointClick = function() {};
  501. this.onTrayClick = function() {};
  502. this.onRightPaneClick = function() {};
  503. // draw the board
  504. // TODO check that we don't draw the same things twice
  505. paper.rect(0, 0, 4 * this.params.borderWidth + 14 * this.params.pointWidth + this.params.barWidth,
  506. this.params.height).attr({
  507. fill: this.params.backgroundColour
  508. });
  509. paper.rect(0, 0, 2 * this.params.borderWidth + this.params.pointWidth,
  510. this.params.height).attr({
  511. fill: this.params.borderColour
  512. });
  513. paper.rect(13 * this.params.pointWidth + 2 * this.params.borderWidth + this.params.barWidth, 0, 2 * this.params.borderWidth + this.params.pointWidth,
  514. this.params.height).attr({
  515. fill: this.params.borderColour
  516. });
  517. paper.rect(0, 0, 14 * this.params.pointWidth + this.params.barWidth + 4 * this.params.borderWidth, this.params.borderWidth).attr({
  518. fill: this.params.borderColour
  519. });
  520. paper.rect(0, this.params.height - this.params.borderWidth, 14 * this.params.pointWidth + this.params.barWidth + 4 * this.params.borderWidth, this.params.borderWidth).attr({
  521. fill: this.params.borderColour
  522. });
  523. this.point = function(x, y, direction, colour) {
  524. var triangle = paper.polyline([x, y, x + this.params.pointWidth, y, x + 0.5 * this.params.pointWidth, y + direction * this.params.pointHeight, x, y]);
  525. triangle.attr({
  526. fill: colour
  527. });
  528. return triangle;
  529. }
  530. // draw the points, etc.
  531. var bar = SVGBar(this, 0, 0);
  532. var trays = [];
  533. for (let i = 0; i < 4; ++i) trays.push(Tray(this, i, 0, null));
  534. var cubePic = null;
  535. var points = [];
  536. for (let i = 0; i < 24; ++i) points.push(SVGPoint(this, i, 0, null));
  537. var leftPane = null;
  538. var rightPane = null;
  539. this.setCubeLeft = function(c) {
  540. var x = self.params.borderWidth + 0.5 * self.params.pointWidth;
  541. var y = 0.5 * self.params.height;
  542. if (cubePic) cubePic.remove();
  543. cubePic = cube(paper, x, y, self.params.pointWidth,
  544. normalizeCube(c), "brown", "beige", "W");
  545. }
  546. this.setCubeRight = function(c) {
  547. var x = 3 * self.params.borderWidth + 13.5 * self.params.pointWidth + self.params.barWidth;
  548. var y = 1/2 * self.params.height;
  549. if (cubePic) cubePic.remove();
  550. cubePic = cube(paper, x, y, self.params.pointWidth,
  551. normalizeCube(c), "brown", "beige", "W");
  552. }
  553. this.setCubeRightPane = function(value) {
  554. var x = 2 * self.params.borderWidth + 7 * self.params.pointWidth + self.params.barWidth + 3 * self.params.pointWidth;
  555. var y = 0.5 * self.params.height;
  556. if (rightPane) rightPane.remove();
  557. rightPane = Pane(self, "right");
  558. if (cubePic) cubePic.remove();
  559. cubePic = cube(paper, x, y, self.params.pointWidth,
  560. normalizeCube(value), "brown", "beige", "N");
  561. cubePic.appendTo(rightPane);
  562. }
  563. this.setCubeLeftPane = function(value) {
  564. var x = 2 * self.params.borderWidth + 4 * self.params.pointWidth;
  565. var y = 0.5 * self.params.height;
  566. if (leftPane) leftPane.remove();
  567. leftPane = Pane(self, "left");
  568. if (cubePic) cubePic.remove();
  569. cubePic = cube(paper, x, y, self.params.pointWidth,
  570. normalizeCube(value), "brown", "beige", "N");
  571. cubePic.appendTo(leftPane);
  572. }
  573. this.hideCube = function() {
  574. if (cubePic) cubePic.remove();
  575. }
  576. this.setCubeTray = function(ID, value) {
  577. var baseX = (ID === 0 || ID === 3) ? self.params.borderWidth + 1/2 * self.params.pointWidth :
  578. 13.5 * self.params.pointWidth + 3 * self.params.borderWidth + self.params.barWidth;
  579. var baseY = (ID === 0 || ID === 1) ? self.params.borderWidth :
  580. self.params.height - self.params.borderWidth;
  581. var direction = (ID === 0 || ID === 1) ? 1 : -1;
  582. if (cubePic) cubePic.remove();
  583. cubePic = cube(paper, baseX, baseY + direction * 0.5 * self.params.pointWidth, self.params.pointWidth,
  584. normalizeCube(value), "brown", "beige", (ID < 2) ? "S" : "N");
  585. }
  586. this.bar = SVGBar(this);
  587. this.leftPane = Pane(this, "left");
  588. this.rightPane = Pane(this, "right");
  589. this.setDiceLeft = function(d0, op0, d1, op1) {
  590. if (leftPane) leftPane.remove();
  591. leftPane = Pane(self, "left", d0, op0, d1, op1);
  592. }
  593. this.setDiceRight = function(d0, op0, d1, op1) {
  594. if (rightPane) rightPane.remove();
  595. rightPane = Pane(self, "right", d0, op0, d1, op1);
  596. }
  597. this.hideDice = function() {
  598. if (leftPane) leftPane.remove();
  599. leftPane = Pane(self, "left");
  600. if (rightPane) rightPane.remove();
  601. rightPane = Pane(self, "right");
  602. }
  603. this.setCheckersPoint = function(id, k, player) {
  604. if (points[id]) points[id].remove();
  605. points[id] = SVGPoint(self, id, k, player);
  606. }
  607. this.setCheckersBar = function(white, black) {
  608. if (bar) bar.remove();
  609. bar = SVGBar(self, white, black);
  610. }
  611. this.setCheckersTray = function(id, k, player) {
  612. if (trays[id]) trays[id].remove();
  613. trays[id] = Tray(self, id, k, player);
  614. }
  615. }
  616. function Game() {
  617. this.dice = null;
  618. this.restDice = null;
  619. this.turn = null;
  620. this.moves = null;
  621. this.diceNo = -1;
  622. this.gameNo = -1;
  623. this.canMoveFrom = function(p) {
  624. var restDice = this.restDice;
  625. var from = (p === "bar") ? "bar" : p + 1;
  626. if (restDice.length === 0) return false;
  627. if (this.moves.some(function(move) {
  628. return (move[0] === restDice[0] && move[1] === from);
  629. })) {
  630. return "no-swap";
  631. }
  632. if (restDice.length === 1) return "swap";
  633. if (this.moves.some(function(move) {
  634. return (move[0] === restDice[1] && move[1] === from);
  635. })) {
  636. return "swap";
  637. }
  638. return false;
  639. }
  640. this.canSwapDice = function() {
  641. var dice = this.restDice;
  642. return (game.turn === PLAYER) && (dice.length == 2) && (dice[0] != dice[1]);
  643. }
  644. this.swapDice = function() {
  645. var tmp = this.restDice[0];
  646. this.restDice[0] = this.restDice[1];
  647. this.restDice[1] = tmp;
  648. tmp = this.dice[0];
  649. this.dice[0] = this.dice[1];
  650. this.dice[1] = tmp;
  651. }
  652. this.pips = function(player) {
  653. var pips = 0;
  654. var points = this.board.points;
  655. for (let i = 0; i < 24; ++i) {
  656. if (points[i].player === player) {
  657. pips += points[i].checkers * ((player === "white") ? (i + 1) : (24 - i));
  658. }
  659. }
  660. pips += this.board.bar[player] * 25;
  661. return pips;
  662. }
  663. }
  664. function UI() {
  665. this.putEvent = function(evt) {
  666. sendEvent(evt);
  667. } ;
  668. }
  669. function Toolbar(game, ui, board, graphicToolbar) {
  670. this.pic = graphicToolbar;
  671. this.game = game;
  672. this.ui = ui;
  673. this.board = board;
  674. var self = this;
  675. this.plug = function() {
  676. var foo = function() {};
  677. this.pic.onDouble = this.onDouble;
  678. this.pic.onAccept = this.onAccept;
  679. this.pic.onRefuse = this.onRefuse;
  680. this.pic.onUndo = this.onUndo;
  681. this.pic.onChangeDirection = this.board.onChangeDirection;
  682. this.enableDouble = this.pic.enableDouble || foo;
  683. this.enableAccept = this.pic.enableAccept || foo;
  684. this.enableRefuse = this.pic.enableRefuse || foo;
  685. this.enableUndo = this.pic.enableUndo || foo;
  686. }
  687. // TODO We assume that disabling works properly, but still it would be nice
  688. // to check ui & make sure we are allowed to do that.
  689. this.onDouble = function() {
  690. ui.putEvent(offerDoubleEvent("yes"));
  691. }
  692. this.onAccept = function() {
  693. ui.putEvent(acceptDoubleEvent("yes"));
  694. }
  695. this.onRefuse = function() {
  696. ui.putEvent(acceptDoubleEvent("no"));
  697. }
  698. this.onUndo = function() {
  699. if (ui.undoEnabled) ui.putEvent(undoEvent());
  700. }
  701. this.refresh = function() {
  702. this.enableDouble(ui.offerDoubleEnabled);
  703. this.enableAccept(ui.acceptDoubleEnabled);
  704. this.enableRefuse(ui.acceptDoubleEnabled);
  705. this.enableUndo(ui.undoEnabled);
  706. }
  707. }
  708. function UserInterface(game, ui, graphicBoard, graphicToolbar, matchInfo, graphicHint) {
  709. this.game = game;
  710. this.ui = ui;
  711. this.board = new Board(game, ui, graphicBoard);
  712. this.toolbar = new Toolbar(game, ui, this.board, graphicToolbar);
  713. this.matchinfo = new MatchInfo(game, matchInfo);
  714. this.hint = new Hint(graphicHint);
  715. this.board.plug();
  716. this.toolbar.plug();
  717. this.matchinfo.plug();
  718. this.hint.plug()
  719. this.refresh = function() {
  720. this.board.refresh();
  721. this.toolbar.refresh();
  722. this.matchinfo.refresh();
  723. if (game.matchLimit && game.matchLimit <= game.matchScore[0]) {
  724. this.hint.setHint("White wins the match.");
  725. } else if (game.matchLimit && game.matchLimit <= game.matchScore[1]) {
  726. this.hint.setHint("Black wins the match.");
  727. } else if (game.winner) {
  728. this.hint.setHint(game.winner + " wins the game.");
  729. } else if (!game.turn) {
  730. this.hint.setHint("Starting the game.");
  731. } else if (ui.checkersEnabled) {
  732. this.hint.setHint("Move your checkers and click on the right to complete the move.");
  733. } else if (ui.offerDoubleEnabled) {
  734. this.hint.setHint("Double or click on the right to roll the dice.");
  735. } else if (ui.acceptDoubleEnabled) {
  736. this.hint.setHint("Accept or refuse the double.");
  737. } else if (game.turn === PLAYER && game.dice && ! game.moves) {
  738. this.hint.setHint("NO MOVES");
  739. } else if (game.turn !== PLAYER) {
  740. this.hint.setHint("Your opponent’s turn.");
  741. } else {
  742. this.hint.setHint(null);
  743. }
  744. }
  745. }
  746. function GraphicToolbar() {
  747. var self = this;
  748. this.doubleButton = document.getElementById("double")
  749. this.acceptButton = document.getElementById("accept")
  750. this.refuseButton = document.getElementById("refuse")
  751. this.undoButton = document.getElementById("undo");
  752. this.directionButton = document.getElementById("direction")
  753. this.enableDouble = function(b) {
  754. self.doubleButton.disabled = !b;
  755. }
  756. this.enableAccept = function(b) {
  757. self.acceptButton.disabled = !b;
  758. }
  759. this.enableRefuse = function(b) {
  760. self.refuseButton.disabled = !b;
  761. }
  762. this.enableUndo = function(b) {
  763. self.undoButton.disabled = !b;
  764. }
  765. }
  766. function MatchInfo(game, graphicMatchInfo) {
  767. this.pic = graphicMatchInfo;
  768. this.plug = function() {
  769. var foo = function() {};
  770. this.setNames = this.pic.setNames || foo; // white-name black-name
  771. this.setScore = this.pic.setScore || foo; // player score away
  772. this.setPips = this.pic.setPips || foo; // player pips diff
  773. this.setMatchLimit = this.pic.setMatchLimit || foo; // limit/null
  774. this.setCrawford = this.pic.setCrawford || foo; // bool
  775. this.setJacoby = this.pic.setJacoby || foo; // bool
  776. }
  777. this.refresh = function() {
  778. // TODO names
  779. //this.setName("white", game.names.white);
  780. this.setNames(ui.whiteName, ui.blackName);
  781. this.setScore("white", game.matchScore[0], game.matchLimit && (game.matchLimit - game.matchScore[0]));
  782. this.setScore("black", game.matchScore[1], game.matchLimit && (game.matchLimit - game.matchScore[1]));
  783. var whitePips = game.pips("white");
  784. var blackPips = game.pips("black");
  785. this.setPips("white", whitePips, whitePips - blackPips);
  786. this.setPips("black", blackPips, blackPips - whitePips);
  787. this.setMatchLimit(game.matchLimit);
  788. if (game.matchLimit) {
  789. this.setCrawford(game.isCrawford);
  790. } else {
  791. this.setJacoby(game.withJacoby);
  792. }
  793. }
  794. }
  795. function GraphicMatchInfo() {
  796. this.setNames = function(whiteName, blackName) {
  797. document.getElementById("white-name").innerHTML = whiteName || "";
  798. document.getElementById("black-name").innerHTML = blackName || "";
  799. }
  800. this.setScore = function(player, score, away) {
  801. document.getElementById( player === "white" ? "white-score" : "black-score").innerHTML = score;
  802. if (!isNaN(away)) {
  803. if (away > 0) {
  804. document.getElementById( player === "white" ? "white-away" : "black-away")
  805. .innerHTML = "(" + away + "-away)";
  806. } else {
  807. document.getElementById( player === "white" ? "white-away" : "black-away")
  808. .innerHTML = "(won match)";
  809. }
  810. }
  811. }
  812. this.setPips = function(player, pips, diff) {
  813. document.getElementById( player === "white" ? "white-pips" : "black-pips").innerHTML = pips;
  814. document.getElementById( player === "white" ? "white-diff" : "black-diff").innerHTML
  815. = (diff >= 0) ? "+" + diff : diff;
  816. }
  817. this.setMatchLimit = function(limit) {
  818. document.getElementById("match-limit").innerHTML = (limit || "unlimited");
  819. }
  820. this.setCrawford = function(isCrawford) {
  821. document.getElementById("match-misc").innerHTML = isCrawford ? "Crawford game" : "";
  822. }
  823. this.setJacoby = function(withJacoby) {
  824. document.getElementById("match-misc").innerHTML = withJacoby ? "Jacoby rule" : "";
  825. }
  826. }
  827. function Hint(graphicHint) {
  828. this.pic = graphicHint;
  829. this.plug = function () {
  830. // set to null to clear
  831. this.setHint = this.pic.setHint;
  832. }
  833. }
  834. function GraphicHint() {
  835. var self = this;
  836. this.setHint = function(text) {
  837. document.getElementById("hint").innerHTML = text || "";
  838. }
  839. }
  840. var score = [0, 0];
  841. var game = new Game();
  842. var ui = new UI();
  843. //var boardPic = Snap(520, 300);
  844. var boardPaper = Snap("#board");
  845. var boardPic = new SVGBoard(boardPaper);
  846. var maxCirclesOnPoint = 6;
  847. //var board = new Board(game, ui, boardPic)
  848. var graphicToolbar = new GraphicToolbar();
  849. var userIntereface = new UserInterface(game, ui, boardPic, graphicToolbar, new GraphicMatchInfo(), new GraphicHint());
  850. var PLAYER = "watching";
  851. document.getElementById("player").innerHTML = PLAYER;
  852. function statusBar() {
  853. return document.getElementById("status");
  854. }
  855. function point(x, y, direction, colour) {
  856. var triangle = boardPic.polyline([x, y, x + board.pointWidth, y, x + 0.5 * board.pointWidth, y + direction * board.pointHeight, x, y]);
  857. triangle.attr({
  858. fill: colour
  859. });
  860. return triangle;
  861. }
  862. function checkPointID(ID) {
  863. if (!(0 <= ID && ID < 24))
  864. throw "Invalid point ID";
  865. }
  866. function checkPlayer(player) {
  867. if (!(player == "black" || player == "white" || player === null))
  868. throw "Invalid player";
  869. }
  870. function pointPic(board, ID) {
  871. checkPointID(ID);
  872. var direction = pointDirection(ID);
  873. var colour = (ID % 2 == 0) ? board.params.evenColour : board.params.oddColour;
  874. return board.point(pointX(board, ID) - 0.5 * board.params.pointWidth, pointBaseY(board, ID), direction, colour);
  875. }
  876. function pointX(board, ID) {
  877. checkPointID(ID);
  878. var base = (ID < 12) ? ID * board.params.pointWidth : (23 - ID) * board.params.pointWidth;
  879. base += 0.5 * board.params.pointWidth + 2 * board.params.borderWidth + board.params.pointWidth;
  880. if (6 <= ID && ID < 18) base += board.params.barWidth;
  881. return base;
  882. }
  883. function pointBaseY(board, ID) {
  884. return (ID < 12) ? board.params.borderWidth : board.params.height - board.params.borderWidth;
  885. }
  886. function pointTopY(board, ID) {
  887. checkPointID(ID);
  888. return (ID < 12) ? board.params.pointHeight : board.params.height - board.params.pointHeight;
  889. }
  890. function pointDirection(ID) {
  891. checkPointID(ID);
  892. return (ID < 12) ? 1 : -1;
  893. }
  894. function moveEvent(from, pips) {
  895. return ["m", from, pips];
  896. }
  897. function sendEvent(evt) {
  898. console.log("sending " + JSON.stringify(evt));
  899. websocket.send(JSON.stringify(evt));
  900. statusBar().innerHTML = (JSON.stringify(evt));
  901. }
  902. function continueEvent(decision) {
  903. return ["c", decision ? "yes" : "no"];
  904. }
  905. function proposalDecisionEvent(decision) {
  906. return ["proposal-decision", decision ? "yes" : "no"];
  907. }
  908. function dispatchCommand(command) {
  909. console.log(command);
  910. if (command[0] == "player") {
  911. PLAYER = command[1];
  912. userIntereface.board.orientation = PLAYER;
  913. document.getElementById("player").innerHTML = PLAYER;
  914. //checkerPic(Snap("#player"), PLAYER, 10, 10);
  915. } else if (command[0] == "update") {
  916. setGame(game, ui, command[1]);
  917. userIntereface.refresh();
  918. } else if (command[0] == "continue?") {
  919. sendEvent(continueEvent(confirm("One more?")));
  920. } else if (command[0] == "message") {
  921. statusBar().innerHTML = command[1];
  922. } else if (command[0] == "proposal") {
  923. ui.putEvent(proposalDecisionEvent(confirm("Do you want to play with " + command[1] + "?")));
  924. } else if (command[0] == "cancelled") {
  925. alert("Cancelled by " + command[1]);
  926. }
  927. }
  928. function physicalToAbsolute(p, clockwise, orientation) {
  929. if (clockwise && (orientation === "black")) return p;
  930. if (!clockwise && (orientation === "black")) return (p < 12) ? 11 - p : 35 - p;
  931. if (clockwise && (orientation === "white")) return 23 - p;
  932. if (!clockwise && (orientation === "white")) return (p < 12) ? p + 12 : p - 12;}
  933. function setGame(game, ui, obj) {
  934. game.turn = obj.game.turn;
  935. if (!game.turn) {
  936. game.dice = obj.game["dice"];
  937. } else if (obj.game["dice"].length === 0 || obj.game["dice-no"] !== game.diceNo ||
  938. obj.game["game-no"] !== game.gameNo) {
  939. game.diceNo = obj.game["dice-no"];
  940. game.dice = obj.game.dice.sort().reverse();
  941. game.restDice = obj.game["rest-dice"].sort().reverse();
  942. } else {
  943. /* If the move is the same, we've got two dice and we receive two dice
  944. * from the server, they must be the same two dice as we have, so we
  945. * copy game.dice to game.restDice in order to save the custom dice
  946. * order. Otherwise we either have a single die or identical dice,
  947. * either way we don't care about the order.
  948. */
  949. game.restDice = (game.dice.length === 2 && obj.game["rest-dice"].length === 2)
  950. ? game.dice.slice() : obj.game["rest-dice"]
  951. }
  952. game.gameNo = obj.game["game-no"];
  953. game.cube = obj.game["cube"];
  954. game.cubeOwner = obj.game["cube-owner"];
  955. game.isDoubling = obj.game["is-doubling"];
  956. game.isCrawford = obj.game["crawford?"];
  957. game.withJacoby = obj.game["jacoby?"];
  958. game.matchLimit = obj.game["match-limit"];
  959. game.result = obj.game.result;
  960. game.moves = obj.game.moves;
  961. game.winner = obj.game["winner"];
  962. game.score = obj.game["score"];
  963. game.matchScore = obj.game["match-score"];
  964. game.board = obj.game.board;
  965. if (obj.ui) {
  966. ui.checkersEnabled = obj.ui["checkers-enabled"];
  967. ui.undoEnabled = obj.ui["undo-enabled"];
  968. ui.moveCompletionEnabled = obj.ui["move-completion-enabled"];
  969. ui.offerDoubleEnabled = obj.ui["offer-double-enabled"];
  970. ui.acceptDoubleEnabled = obj.ui["accept-double-enabled"];
  971. ui.whiteName = obj.ui["white-name"];
  972. ui.blackName = obj.ui["black-name"];
  973. }
  974. }
  975. var wsUri = "ws://" + window.location.host + window.location.pathname;
  976. var websocket = new WebSocket(wsUri);
  977. function setupWebSocket()
  978. {
  979. websocket.onopen = function(evt) { onOpen(evt) };
  980. websocket.onclose = function(evt) { onClose(evt) };
  981. websocket.onmessage = function(evt) { onMessage(evt) };
  982. websocket.onerror = function(evt) { onError(evt) };
  983. }
  984. function onOpen(evt)
  985. {
  986. statusBar().innerHTML = "connected";
  987. }
  988. function onClose(evt)
  989. {
  990. statusBar().innerHTML = "disconnected";
  991. }
  992. function onMessage(evt)
  993. {
  994. //setGameFromJSON(board, evt.data.toLowerCase());
  995. dispatchCommand(JSON.parse(evt.data.toLowerCase()));
  996. //alert(evt.data.toLowerCase());
  997. }
  998. function onError(evt)
  999. {
  1000. }
  1001. function doSend(message)
  1002. {
  1003. }
  1004. function writeToScreen(message)
  1005. {
  1006. }
  1007. //window.addEventListener("load", setupWebSocket, false);
  1008. setupWebSocket();
  1009. //console.log(new checkerPic("black", pointX(board, 3), pointTopY(board, 3) - board.checkerRadius, "15"));
  1010. //var st = new SVGStorage(board, 1);
  1011. //st.setCheckers(4, "white");
  1012. //
  1013. /*
  1014. {
  1015. this.points.forEach(function(p, i) {
  1016. let a = physicalToAbsolute(i, board.clockwise, board.orientation);
  1017. p.setCheckers(game.board.points[a].checkers, game.board.points[a].player);
  1018. });
  1019. this.bar.setCheckers(game.board.bar.white, "white");
  1020. this.bar.setCheckers(game.board.bar.black, "black");
  1021. this.undoButton.disabled = !ui.undoEnabled;
  1022. this.leftPane.refresh();
  1023. document.getElementById("accept").disabled = !ui.acceptDoubleEnabled;
  1024. document.getElementById("refuse").disabled = !ui.acceptDoubleEnabled;
  1025. document.getElementById("double").disabled = !ui.offerDoubleEnabled;
  1026. var offWhite = this.bearoffStorageID.white();
  1027. var offBlack = this.bearoffStorageID.black();
  1028. var cubeLocation = this.cubeLocation();
  1029. for (let i = 0; i < 4; ++i) {
  1030. if (i === offWhite) {
  1031. this.storages[i].setCheckers(game.board.off.white, "white");
  1032. } else if (i === offBlack) {
  1033. this.storages[i].setCheckers(game.board.off.black, "black");
  1034. } else if (i === cubeLocation) {
  1035. this.storages[i].setCube(game.cube);
  1036. } else {
  1037. this.storages[i].setCheckers(0, null);
  1038. }
  1039. }
  1040. if (cubeLocation === "left") this.setCubeLeft(game.cube)
  1041. else if (cubeLocation === "right") this.setCubeRight(game.cube)
  1042. else if (cubeLocation === "leftPane") this.setCubeLeftPane(2*game.cube);
  1043. else if (cubeLocation === "rightPane") this.setCubeRightPane(2*game.cube)
  1044. else if (cubeLocation === null && this.cubePic) this.cubePic.remove();
  1045. document.getElementById("pips").innerHTML = "Pips: white " + game.pips("white") + " black " + game.pips("black");
  1046. if (!game.winner) statusBar().innerHTML = game.turn + " " + game.restDice;
  1047. else {
  1048. let winner = game.winner;
  1049. let res = game.score;
  1050. let text = (res === 1) ? winner + " wins 1 point." : winner + " wins " + res + " points."
  1051. alert(text);
  1052. statusBar().innerHTML = text;
  1053. }
  1054. }
  1055. */
  1056. function showStatus() {
  1057. document.getElementById("status").hidden = false;
  1058. }
  1059. </script>
  1060. </body>