play.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. var $side = 'w';
  2. var $piece = null;
  3. var $chess = new Chess();
  4. function selectPiece(el) {
  5. el.addClass('selected');
  6. }
  7. function unselectPiece(el) {
  8. el.removeClass('selected');
  9. }
  10. function isSelected(el) {
  11. return el ? el.hasClass('selected') : false;
  12. }
  13. function movePiece(from, to, promotion, rcvd) {
  14. var move = $chess.move({
  15. 'from': from,
  16. 'to': to,
  17. promotion: promotion
  18. });
  19. if (move) {
  20. var tdFrom = $('td.' + from.toUpperCase());
  21. var tdTo = $('td.' + to.toUpperCase());
  22. //highlight moves
  23. if ($('td').hasClass('last-target')){
  24. $('td').removeClass('last-target last-origin');
  25. }
  26. tdFrom.addClass('last-origin');
  27. tdTo.addClass('last-target');
  28. var piece = tdFrom.find('a'); // piece being moved
  29. var moveSnd = $("#moveSnd")[0];
  30. unselectPiece(piece.parent());
  31. if (tdTo.html() !== '') { //place captured piece next to the chessboard
  32. $('#captured-pieces')
  33. .find($chess.turn() === 'b' ? '.b' : '.w')
  34. .append('<li>' + tdTo.find('a').html() + '</li>');
  35. }
  36. tdTo.html(piece);
  37. $piece = null;
  38. // en passant move
  39. if (move.flags === 'e'){
  40. var enpassant = move.to.charAt(0) + move.from.charAt(1);
  41. $('td.' + enpassant.toUpperCase()).html('');
  42. }
  43. //kingside castling
  44. var rook;
  45. if (move.flags === 'k'){
  46. if (move.to === 'g1'){
  47. rook = $('td.H1').find('a');
  48. $('td.F1').html(rook);
  49. }
  50. else if (move.to === 'g8'){
  51. rook = $('td.H8').find('a');
  52. $('td.F8').html(rook);
  53. }
  54. }
  55. //queenside castling
  56. if (move.flags === 'q'){
  57. if (move.to === 'c1'){
  58. rook = $('td.A1').find('a');
  59. $('td.D1').html(rook);
  60. }
  61. else if (move.to === 'c8'){
  62. rook = $('td.A8').find('a');
  63. $('td.D8').html(rook);
  64. }
  65. }
  66. //promotion
  67. if (move.flags === 'np' || move.flags === 'cp'){
  68. var square = $('td.' + move.to.toUpperCase()).find('a');
  69. var option = move.promotion;
  70. var promotion_w = {
  71. 'q': '&#9813;',
  72. 'r': '&#9814;',
  73. 'n': '&#9816;',
  74. 'b': '&#9815;'
  75. };
  76. var promotion_b = {
  77. 'q': '&#9819;',
  78. 'r': '&#9820;',
  79. 'n': '&#9822;',
  80. 'b': '&#9821;'
  81. };
  82. if (square.hasClass('white')){
  83. square.html(promotion_w[option]);
  84. } else {
  85. square.html(promotion_b[option]);
  86. }
  87. }
  88. if ($('#sounds').is(':checked')) {
  89. moveSnd.play();
  90. }
  91. //feedback
  92. var fm = $('.feedback-move');
  93. var fs = $('.feedback-status');
  94. $chess.turn() === 'b' ? fm.text('Black to move.') : fm.text('White to move.');
  95. fm.parent().toggleClass('blackfeedback whitefeedback');
  96. $chess.in_check() ? fs.text(' Check.') : fs.text('');
  97. //game over
  98. if ($chess.game_over()) {
  99. fm.text('');
  100. var result = "";
  101. if ($chess.in_checkmate())
  102. result = $chess.turn() === 'b' ? 'Checkmate. White wins!' : 'Checkmate. Black wins!'
  103. else if ($chess.in_draw())
  104. result = "Draw.";
  105. else if ($chess.in_stalemate())
  106. result = "Stalemate.";
  107. else if ($chess.in_threefold_repetition())
  108. result = "Draw. (Threefold Repetition)";
  109. else if ($chess.insufficient_material())
  110. result = "Draw. (Insufficient Material)";
  111. fs.text(result);
  112. }
  113. /* Add all moves to the table */
  114. var pgn = $chess.pgn({ max_width: 5, newline_char: ',' });
  115. var moves = pgn.split(',');
  116. var last_move = moves.pop().split('.');
  117. var move_number = last_move[0];
  118. var move_pgn = $.trim(last_move[1]);
  119. if (move_pgn.indexOf(' ') != -1) {
  120. var moves = move_pgn.split(' ');
  121. move_pgn = moves[1];
  122. }
  123. $('#moves tbody tr').append('<td><strong>' + move_number + '</strong>. ' + move_pgn + '</td>');
  124. if (rcvd === undefined) {
  125. $socket.emit('new-move', {
  126. 'token': $token,
  127. 'move': move
  128. });
  129. }
  130. if ($chess.game_over()) {
  131. $('.resign').hide();
  132. alert(result);
  133. } else {
  134. if ($chess.turn() === 'b') {
  135. $socket.emit('timer-black', {
  136. 'token': $token
  137. });
  138. } else {
  139. $socket.emit('timer-white', {
  140. 'token': $token
  141. });
  142. }
  143. }
  144. }
  145. }
  146. /* socket.io */
  147. $(document).ready(function () {
  148. $socket.emit('join', {
  149. 'token': $token,
  150. 'time': $time * 60,
  151. 'increment': $increment
  152. });
  153. $socket.on('joined', function (data) {
  154. if (data.color === 'white') {
  155. $side = 'w';
  156. $('.chess_board.black').remove();
  157. $socket.emit('timer-white', {
  158. 'token': $token
  159. });
  160. } else {
  161. $side = 'b';
  162. $('.chess_board.white').remove();
  163. $('.chess_board.black').show();
  164. }
  165. $('#sendMessage').find('input').addClass($side === 'b' ? 'black' : 'white');
  166. });
  167. $socket.on('move', function (data) {
  168. movePiece(from=data.move.from, to=data.move.to, promotion=data.move.promotion, rcvd=true);
  169. });
  170. $socket.on('opponent-disconnected', function (data) {
  171. alert("Your opponent has disconnected.");
  172. window.location = '/';
  173. });
  174. $socket.on('opponent-resigned', function (data) {
  175. alert("Your opponent has resigned. You won!");
  176. window.location = '/';
  177. });
  178. $socket.on('full', function (data) {
  179. alert("This game already has two players. You have to create a new one.");
  180. window.location = '/';
  181. });
  182. $socket.on('receive-message', function (data) {
  183. var chat = $('ul#chat');
  184. var chat_node = $('ul#chat')[0];
  185. var messageSnd = $("#messageSnd")[0];
  186. chat.append('<li class="' + data.color + ' left" >' + data.message + '</li>');
  187. if (chat.is(':visible') && chat_node.scrollHeight > 300) {
  188. setTimeout(function() { chat_node.scrollTop = chat_node.scrollHeight; }, 50);
  189. } else if (!chat.is(':visible') && !$('.new-message').is(':visible')) {
  190. $('#bubble').before('<span class="new-message">You have a new message!</span>');
  191. }
  192. if ($('#sounds').is(':checked')) {
  193. messageSnd.play();
  194. }
  195. });
  196. $socket.on('countdown', function (data) {
  197. var color = data.color;
  198. var opp_color = color === 'black' ? 'white' : 'black';
  199. var min = Math.floor(data.time / 60);
  200. var sec = data.time % 60;
  201. if (sec.toString().length === 1) {
  202. sec = '0' + sec;
  203. }
  204. $('#clock li.' + opp_color).removeClass('ticking');
  205. $('#clock li.' + color).addClass('ticking').text(min + ':' + sec);
  206. });
  207. $socket.on('countdown-gameover', function (data) {
  208. var loser = data.color === 'black' ? 'Black' : 'White';
  209. var winner = data.color === 'black' ? 'White' : 'Black';
  210. $('.resign').hide();
  211. alert(loser + "'s time is out. " + winner + " won.");
  212. window.location = '/';
  213. });
  214. });
  215. /* gameplay */
  216. $(document).ready(function () {
  217. $('#clock li').each(function() {
  218. $(this).text($time + ':00');
  219. });
  220. $('#game-type').text($time + '|' + $increment);
  221. $('.chess_board a').click(function (e) {
  222. var piece = $(this);
  223. if ((piece.hasClass('white') && $side != 'w') ||
  224. (piece.hasClass('black') && $side != 'b')) {
  225. if ($piece) {
  226. movePiece(
  227. from=$piece.parent().data('id').toLowerCase(),
  228. to=$(this).parent().data('id').toLowerCase(),
  229. promotion=$('#promotion option:selected').val()
  230. )
  231. }
  232. } else {
  233. if ($chess.turn() != $side) {
  234. return false;
  235. }
  236. if ($piece && isSelected($(this).parent())) {
  237. unselectPiece($piece.parent());
  238. $piece = null;
  239. } else {
  240. if ($piece) {
  241. unselectPiece($piece.parent());
  242. $piece = null;
  243. }
  244. $piece = $(this);
  245. selectPiece($piece.parent());
  246. }
  247. }
  248. e.stopImmediatePropagation();
  249. e.preventDefault();
  250. });
  251. $('.chess_board td').click(function (e) {
  252. if ($piece) {
  253. movePiece(
  254. from=$piece.parent().data('id').toLowerCase(),
  255. to=$(this).data('id').toLowerCase(),
  256. promotion=$('#promotion option:selected').val()
  257. )
  258. }
  259. });
  260. $('.resign').click(function (e) {
  261. $socket.emit('resign', {
  262. 'token': $token
  263. });
  264. alert('You resigned.');
  265. window.location = '/';
  266. });
  267. $('a.chat').click(function (e) {
  268. $('#chat-wrapper').toggle();
  269. $('.new-message').remove();
  270. var chat_node = $('ul#chat')[0];
  271. if (chat_node.scrollHeight > 300) {
  272. setTimeout(function() { chat_node.scrollTop = chat_node.scrollHeight; }, 50);
  273. }
  274. });
  275. $('#chat-wrapper .close').click(function (e) {
  276. $('#chat-wrapper').hide();
  277. });
  278. $('#sendMessage').submit(function (e) {
  279. e.preventDefault();
  280. var input = $(this).find('input');
  281. var message = input.val();
  282. var color = $side === 'b' ? 'black' : 'white';
  283. if (!/^\W*$/.test(message)) {
  284. input.val('');
  285. $('ul#chat').append('<li class="' + color + ' right" >' + message + '</li>');
  286. var chat_node = $('ul#chat')[0];
  287. if (chat_node.scrollHeight > 300) {
  288. setTimeout(function() { chat_node.scrollTop = chat_node.scrollHeight; }, 50);
  289. }
  290. $socket.emit('send-message', {
  291. 'message': message,
  292. 'color': color,
  293. 'token': $token
  294. });
  295. }
  296. });
  297. });