diff --git a/css/chess.css b/css/chess.css index 1364450..363fb77 100644 --- a/css/chess.css +++ b/css/chess.css @@ -240,6 +240,11 @@ button:disabled .silhouette { user-select: none; } +.cb-piece.cb-selected { + filter: drop-shadow(0 0 5px blue); + -webkit-filter: drop-shadow(0 0 5px blue); +} + #cb_board .cb-piece.ui-draggable { cursor: grab; } diff --git a/js/pacosako.js b/js/pacosako.js index a5014d0..364649e 100644 --- a/js/pacosako.js +++ b/js/pacosako.js @@ -339,6 +339,14 @@ class Game { return JSON.parse(JSON.stringify(this._redo)); } + canCapture(side, from) { + if (from === PHANTOM) { + return true; + } else { + return this._board.getPiece(otherSide(side), from) === EMPTY; + } + } + legalMoves(side, from, canCapture) { const board = this._board; const type = board.getPiece(side, from); @@ -347,6 +355,10 @@ class Game { return []; } + if (canCapture === undefined) { + canCapture = this.canCapture(side, from); + } + if (from === PHANTOM) { /* this is valid because board.getPiece(side, PHANTOM) !== EMPTY */ from = board.phantom.from; diff --git a/js/pacosako_ui.js b/js/pacosako_ui.js index 45ee75e..345048d 100644 --- a/js/pacosako_ui.js +++ b/js/pacosako_ui.js @@ -57,31 +57,76 @@ $(function (){ } } - function pieceStartDrag(ev, ui) { - const dragged = $(this); - const side = dragged.data('side'); - const type = dragged.data('type'); - const from = dragged.data('location'); - const board = currentGame.board; - const where = (from === PS.PHANTOM) ? board.phantom.from : from; - const canCapture = (from === PS.PHANTOM) || - board.getPiece(PS.Util.otherSide(side), where) === PS.EMPTY; - const legals = currentGame.legalMoves(side, from, canCapture); + function squareClickDestination(ev, ui) { + let selected = $('#cb_board .cb-selected'); + if (selected.length !== 1) { + renderBoard(); + return; + } + + let from = selected.data('location'); + let to = this.id.replace(/^cb_/, ''); + selected.appendTo('#cb_hidden'); + + try { + const meta = { timestamp: new Date(Gun.state()).getTime() }; + currentGame.move(from, to, meta); + } catch (err) { + debug('unable to move', err); + } + + $('#cb_board').data('last_state', currentGame.moves); + setCurrentBoard(currentGame); + putState(); + } + + function pieceStartMove(piece, event) { + const side = piece.data('side'); + const type = piece.data('type'); + const from = piece.data('location'); + const legals = currentGame.legalMoves(side, from); for (const there of legals) { - cbSquare(there).addClass('cb-legal').droppable('enable'); + const square = cbSquare(there); + square.addClass('cb-legal') + if (event === 'drag') { + square.droppable('enable'); + } else if (event === 'click') { + square.on('click.destination', squareClickDestination); + } } } + function pieceClickUnselect(ev, ui) { + renderBoard(); + } + + function pieceClickSelect(ev, ui) { + const clicked = $(this); + clicked.addClass('cb-selected'); + $('#cb_board .cb-piece').off('click.select'); + clicked.on('click.unselect', pieceClickUnselect); + pieceStartMove(clicked, 'click'); + } + + function pieceStartDrag(ev, ui) { + $('#cb_board .cb-selected').removeClass('cb-selected'); + $('#cb_board .cb-square').off('click.destination'); + pieceStartMove($(this), 'drag'); + } + function pieceStopDrag(ev, ui) { - $('#cb_board .cb-legal').removeClass('cb-legal').droppable('disable'); + renderBoard(); } function placePiece(where, side, type, suffix) { const code = pieceCode(side, type); const piece_id = 'cb_piece_' + code + '_' + suffix; const piece = $($('#' + piece_id)[0] || $('#cb_piece_' + code + ' img').clone()); - piece.attr('style', ''); piece.attr('id', piece_id); + piece.removeClass('cb-selected'); + piece.removeAttr('style'); + piece.off('click.select'); + piece.off('click.unselect'); piece.data({ side: side, type: type, location: where }); piece.appendTo(cbSquare(where)); piece.draggable({ @@ -96,8 +141,11 @@ $(function (){ } function renderBoard() { - $('#cb_board .cb-piece .ui-draggable').draggable('destroy'); - $('#cb_board .cb-piece').attr('style', '').appendTo('#cb_hidden'); + $('#cb_board .cb-piece').off('click.select'); + $('#cb_board .cb-piece').off('click.unselect'); + $('#cb_board .cb-square').off('click.destination'); + $('#cb_board .cb-piece.cb-selected').removeClass('cb-selected'); + $('#cb_board .cb-piece').removeAttr('style').appendTo('#cb_hidden'); $('#cb_board .cb-start').removeClass('cb-start'); $('#cb_board .cb-end').removeClass('cb-end'); $('#cb_board .cb-legal').removeClass('cb-legal'); @@ -122,17 +170,6 @@ $(function (){ } } - let clss = game.player === PS.LIGHT ? '.cb-lt-piece' : '.cb-dk-piece'; - - if (board.phantom) { - let where = board.phantom.from; - placePiece(PS.PHANTOM, board.phantom.side, board.phantom.type, 'ph'); - cbSquare(PS.PHANTOM).appendTo(cbSquare(where)); - $('#cb_phantom .ui-draggable-disabled').filter(clss).draggable('enable'); - } else if (game.status === PS.PLAYING) { - $('#cb_board .ui-draggable-disabled').filter(clss).draggable('enable'); - } - const lastMove = game.lastMove; if (lastMove) { if (lastMove.from) { @@ -144,6 +181,26 @@ $(function (){ } } + const liveView = !game.canRedo; + const playing = game.status === PS.PLAYING; + + /* only enable selection / drag-and-drop in the live view */ + if (liveView && playing) { + const phantom = board.phantom; + if (phantom) { + const piece = placePiece(PS.PHANTOM, phantom.side, phantom.type, 'ph'); + cbSquare(PS.PHANTOM).appendTo(cbSquare(phantom.from)); + piece.draggable('enable'); + piece.addClass('cb-selected'); + pieceStartMove(piece, 'click'); + } else if (game.status === PS.PLAYING) { + const clss = game.player === PS.LIGHT ? '.cb-lt-piece' : '.cb-dk-piece'; + const pieces = $('#cb_board ' + clss) + pieces.on('click.select', pieceClickSelect); + pieces.draggable('enable'); + } + } + let msg = ''; let winner = game.winner; if (winner) { @@ -151,7 +208,7 @@ $(function (){ msg += (lastMove.side === PS.LIGHT ? 'Light' : 'Dark') + ' player resigned. '; } msg += (winner === PS.LIGHT ? 'Light' : 'Dark') + ' player won!'; - } else if (game.status === PS.PLAYING) { + } else if (playing) { msg += (game.player === PS.LIGHT ? 'Light' : 'Dark') + ' player\'s turn.'; } else { msg += 'Game ended in a draw.'; @@ -167,25 +224,14 @@ $(function (){ $('#cb_nav_next_turn').attr('disabled', !game.canRedo); $('#cb_nav_last').attr('disabled', !game.canRedo); - if (!game.canRedo) { - $('#cb_undo').attr('disabled', !currentGame.canUndo); - $('#cb_redo').attr('disabled', !currentGame.canRedo); - if (currentGame.status === PS.PLAYING) { - $('#cb_pass').attr('disabled', currentGame.board.phantom ? true : false); - } else { - $('#cb_pass').attr('disabled', true); - } - $('#cb_resign').attr('disabled', currentGame.status !== PS.PLAYING); - $('#cb_board').addClass('cb-live'); - $('#cb_board').removeClass('cb-archive'); + $('#cb_undo').attr('disabled', !liveView || !currentGame.canUndo); + $('#cb_redo').attr('disabled', !liveView || !currentGame.canRedo); + $('#cb_resign').attr('disabled', !liveView || !playing); + + if (liveView) { + $('#cb_board').addClass('cb-live').removeClass('cb-archive'); } else { - $('#cb_undo').attr('disabled', true); - $('#cb_redo').attr('disabled', true); - $('#cb_pass').attr('disabled', true); - $('#cb_resign').attr('disabled', true); - $('#cb_board .ui-draggable').draggable('disable'); - $('#cb_board').removeClass('cb-live'); - $('#cb_board').addClass('cb-archive'); + $('#cb_board').removeClass('cb-live').addClass('cb-archive'); } } @@ -714,7 +760,7 @@ $(function (){ } else if (move.resign) { game.resign(); } else { - throw { message: "unknown move", move: move }; + throw { message: 'unknown move', move: move }; } }