diff --git a/takhta.html b/takhta.html index 53301a1..e94da99 100644 --- a/takhta.html +++ b/takhta.html @@ -15,6 +15,7 @@ display: flex; justify-content: center; align-items: center; + line-height: 1em; } #board tbody :nth-child(odd) :nth-child(even) { background-color: #fff; @@ -329,6 +330,10 @@ case 'h': piece = 'P'; break; + case 'O': + case '0': + // Castling, handled in its own function + return doCastle(move); default: return {error: 'Not a valid piece: ' + piece}; } @@ -367,22 +372,21 @@ if (move.slice(index, index + 1) === '=') { index++; switch (move.slice(index, index + 1)) { + case 'K': case 'Q': case 'B': case 'N': case 'R': - promotion = move.slice(index, index + 1); - break; - case 'K': - return {error: 'Cannot promote a piece to king'}; - break; case 'P': - return {error: 'Cannot promote a piece to pawn'}; + promotion = move.slice(index, index + 1); break; default: return {error: 'Unrecognized piece: ' + move.slice(index, index + 1)}; } index++; + } else if (['K', 'Q', 'B', 'N', 'R', 'P'].includes(move.slice(index, index + 1))) { + promotion = move.slice(index, index + 1); + index++; } // Check(mate) @@ -405,7 +409,7 @@ let whitePieces = {'K': '♔', 'Q': '♕', 'R': '♖', 'B': '♗', 'N': '♘', 'P': '♙'}; let blackPieces = {'K': '♚', 'Q': '♛', 'R': '♜', 'B': '♝', 'N': '♞', 'P': '♟'}; - let whiteMove = document.getElementById('tomove').childNodes[0].data == 'White'; + let whiteMove = document.getElementById('tomove').childNodes[0].data === 'White'; let renderedPiece = whiteMove ? whitePieces[piece] : blackPieces[piece]; let renderedPromotion = null; @@ -462,6 +466,125 @@ return {} } + function doCastle(move) { + let ballChar = move[0]; + + // Figure out distance between king and rook + let emptySquares = 1; + let index = 1; + while (index < move.length) { + if (move.slice(index, index + 2) === '-' + ballChar) { + emptySquares += 1; + index += 2; + } else { + break; + } + } + + // Check(mate) + if (!['', '+', '++', '#'].includes(move.slice(index))) { + // TODO: Implement handling + return {error: 'Unexpected contents at the end of the move: ' + move.slice(index)}; + } + + // Find the king(s) + let whiteMove = document.getElementById('tomove').childNodes[0].data === 'White'; + let renderedKing = whiteMove ? '♔' : '♚'; + let renderedRook = whiteMove ? '♖' : '♜'; + + let kings = []; + for (let kingLetter of 'abcdefgh') { + for (let kingNumber of '12345678') { + let square = document.getElementById(kingLetter + kingNumber); + if (square.childNodes.length !== 0 && square.childNodes[0].data === renderedKing) { + kings.push([kingLetter, kingNumber]); + } + } + } + + // Find the rook(s) + function squareDiff(squareLetter, squareNumber, dLetter, dNumber) { + let x = 'abcdefgh'.indexOf(squareLetter) + dLetter; + let y = '12345678'.indexOf(squareNumber) + dNumber; + if (x < 0 || x > 7 || y < 0 || y > 7) { + // Would be outside of the board + return null; + } + return ['abcdefgh'[x], '12345678'[y]] + } + + let distance = emptySquares + 1; + let possiblePairs = []; + for (let [kingLetter, kingNumber] of kings) { + for (let [dLetter, dNumber] of [[-distance, 0], [distance, 0], [0, -distance], [0, distance]]) { + let rookCoords = squareDiff(kingLetter, kingNumber, dLetter, dNumber); + if (rookCoords === null) { + continue; + } + let [rookLetter, rookNumber] = rookCoords; + + let square = document.getElementById(rookLetter + rookNumber); + if (square.childNodes.length !== 0 && square.childNodes[0].data === renderedRook) { + possiblePairs.push([kingLetter, kingNumber, rookLetter, rookNumber, dLetter, dNumber]); + } + } + } + + if (possiblePairs.length === 0) { + return {error: 'Could not find king and rook matching castling criteria'}; + } else if (possiblePairs.length > 1) { + return {error: 'Found ' + String(possiblePairs.length) + ' possible pairs of king and rook to castle'}; + } + + let [kingLetter, kingNumber, rookLetter, rookNumber, dLetter, dNumber] = possiblePairs[0]; + let kingStartSquare = document.getElementById(kingLetter + kingNumber); + let rookStartSquare = document.getElementById(rookLetter + rookNumber); + + // Make sure there there are no pieces between the king and the rook + let signLetter = Math.sign(dLetter); + let signNumber = Math.sign(dNumber); + let [squareLetter, squareNumber] = squareDiff(kingLetter, kingNumber, signLetter, signNumber); + while (squareLetter !== rookLetter || squareNumber !== rookNumber) { + if (document.getElementById(squareLetter + squareNumber).childNodes.length !== 0) { + return {error: squareLetter + squareNumber + ' is occupied'}; + } + [squareLetter, squareNumber] = squareDiff(squareLetter, squareNumber, signLetter, signNumber); + } + + // King moves two squares towards rook + let [kingEndLetter, kingEndNumber] = squareDiff(kingLetter, kingNumber, 2 * signLetter, 2 * signNumber); + let kingEndSquare = document.getElementById(kingEndLetter + kingEndNumber); + + // Rook moves to the square king crossed + let [rookEndLetter, rookEndNumber] = squareDiff(kingLetter, kingNumber, signLetter, signNumber); + let rookEndSquare = document.getElementById(rookEndLetter + rookEndNumber); + + // Move pieces + kingStartSquare.removeChild(kingStartSquare.childNodes[0]); + rookStartSquare.removeChild(rookStartSquare.childNodes[0]); + kingEndSquare.appendChild(document.createTextNode(renderedKing)); + rookEndSquare.appendChild(document.createTextNode(renderedRook)); + + // Flip whose turn it is + document.getElementById('tomove').childNodes[0].data = whiteMove ? 'Black' : 'White'; + + // Record our alterations to the table + let removedPieces = {}; + let placedPieces = {}; + removedPieces[kingLetter + kingNumber] = renderedKing; + removedPieces[rookLetter + rookNumber] = renderedRook; + placedPieces[kingEndLetter + kingEndNumber] = renderedKing; + placedPieces[rookEndLetter + rookEndNumber] = renderedRook; + let moveRecord = { + removedPieces: removedPieces, + placedPieces: placedPieces, + moveText: move + }; + moveHistory.push(moveRecord); + + return {} + } + function undoMove() { if (moveHistory.length === 0) { alert('No moves to undo'); @@ -480,7 +603,7 @@ } // Flip whose turn it is - let whiteMove = document.getElementById('tomove').childNodes[0].data == 'White'; + let whiteMove = document.getElementById('tomove').childNodes[0].data === 'White'; document.getElementById('tomove').childNodes[0].data = whiteMove ? 'Black' : 'White'; // Add to list of redoable moves @@ -617,6 +740,7 @@ let blackQueen = blackKing === 'd8' ? 'e8' : 'd8'; // Undo all moves + let historylength = moveHistory.length; while (moveHistory.length > 0) { undoMove(); } @@ -627,8 +751,8 @@ document.getElementById(blackKing).firstChild.data = '♚'; document.getElementById(blackQueen).firstChild.data = '♛'; - // Redo all the moves we can - while (moveFuture.length > 0) { + // Redo until we can or until the point where we were before, whichever is first + while (moveHistory.length < historylength) { if (!redoMove()) { break; }