play.js 9.5 KB

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