takhta/takhta.html

395 lines
10 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Takhta</title>
<style>
tr :nth-child(1) {
height: 1em;
width: 1em;
}
td {
font-size: 200%;
}
.board tbody :nth-child(odd) :nth-child(even) {
background-color: #fff;
}
.board tbody :nth-child(even) :nth-child(odd) {
background-color: #fff;
}
.board tbody :nth-child(odd) :nth-child(odd) {
background-color: #999;
}
.board tbody :nth-child(even) :nth-child(even) {
background-color: #999;
}
.board tbody :last-child :nth-child(n) {
background-color: unset;
}
.board tbody :nth-child(n) :first-child {
background-color: unset;
}
body {
display: flex;
flex-wrap: wrap;
color: #000;
}
#movelist {
display: grid;
grid-template-columns: 1fr 1fr;
}
#movelist :nth-child(odd) {
background-color: #fff;
}
#movelist :nth-child(even) {
margin-left: 1em;
background-color: #999;
}
li {
padding-left: 0.2em;
padding-right: 0.2em;
display: block;
}
</style>
</head>
<body>
<section>
<table class="board">
<tbody>
<tr id="row8">
<th class="coords">8</th>
<td><div id="a8"></div></td>
<td><div id="b8"></div></td>
<td><div id="c8"></div></td>
<td><div id="d8"></div></td>
<td><div id="e8"></div></td>
<td><div id="f8"></div></td>
<td><div id="g8"></div></td>
<td><div id="h8"></div></td>
</tr>
<tr id="row7">
<th class="coords">7</th>
<td><div id="a7"></div></td>
<td><div id="b7"></div></td>
<td><div id="c7"></div></td>
<td><div id="d7"></div></td>
<td><div id="e7"></div></td>
<td><div id="f7"></div></td>
<td><div id="g7"></div></td>
<td><div id="h7"></div></td>
</tr>
<tr id="row6">
<th class="coords">6</th>
<td><div id="a6"></div></td>
<td><div id="b6"></div></td>
<td><div id="c6"></div></td>
<td><div id="d6"></div></td>
<td><div id="e6"></div></td>
<td><div id="f6"></div></td>
<td><div id="g6"></div></td>
<td><div id="h6"></div></td>
</tr>
<tr id="row5">
<th class="coords">5</th>
<td><div id="a5"></div></td>
<td><div id="b5"></div></td>
<td><div id="c5"></div></td>
<td><div id="d5"></div></td>
<td><div id="e5"></div></td>
<td><div id="f5"></div></td>
<td><div id="g5"></div></td>
<td><div id="h5"></div></td>
</tr>
<tr id="row4">
<th class="coords">4</th>
<td><div id="a4"></div></td>
<td><div id="b4"></div></td>
<td><div id="c4"></div></td>
<td><div id="d4"></div></td>
<td><div id="e4"></div></td>
<td><div id="f4"></div></td>
<td><div id="g4"></div></td>
<td><div id="h4"></div></td>
</tr>
<tr id="row3">
<th class="coords">3</th>
<td><div id="a3"></div></td>
<td><div id="b3"></div></td>
<td><div id="c3"></div></td>
<td><div id="d3"></div></td>
<td><div id="e3"></div></td>
<td><div id="f3"></div></td>
<td><div id="g3"></div></td>
<td><div id="h3"></div></td>
</tr>
<tr id="row2">
<th class="coords">2</th>
<td><div id="a2"></div></td>
<td><div id="b2"></div></td>
<td><div id="c2"></div></td>
<td><div id="d2"></div></td>
<td><div id="e2"></div></td>
<td><div id="f2"></div></td>
<td><div id="g2"></div></td>
<td><div id="h2"></div></td>
</tr>
<tr id="row1">
<th class="coords">1</th>
<td><div id="a1"></div></td>
<td><div id="b1"></div></td>
<td><div id="c1"></div></td>
<td><div id="d1"></div></td>
<td><div id="e1"></div></td>
<td><div id="f1"></div></td>
<td><div id="g1"></div></td>
<td><div id="h1"></div></td>
</tr>
<tr class="coords">
<th></th>
<th>A</th>
<th>B</th>
<th>C</th>
<th>D</th>
<th>E</th>
<th>F</th>
<th>G</th>
<th>H</th>
</tr>
</tbody>
</table>
<form id="form">
<label><span id="tomove">White</span>'s move
<input id="move" type="text" autocomplete="off">
</label>
<input type="submit" value="Move">
<input type="button" value="Undo" onclick="undoMove()">
</form>
</section>
<section id="moves">
<p>Moves thus far</p>
<ul id="movelist">
</ul>
</section>
<script>
'use strict';
let form = document.getElementById('form');
form.onsubmit = moveEvent;
let moveHistory = [];
function moveEvent(event) {
event.preventDefault();
let move = document.getElementById('move').value;
if (move === '') {
return;
}
// Piece
let piece = move.slice(0, 1);
let index = 0;
switch(piece) {
case 'K':
case 'Q':
case 'B':
case 'N':
case 'R':
case 'P':
index++;
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
piece = 'P';
break;
default:
alert('Not a valid piece: ' + piece);
return;
}
// Start square
let startLetter = move.slice(index, index + 1);
let startNumber = move.slice(index + 1, index + 2);
index += 2;
if (
startLetter === '' || !'abcdefgh'.includes(startLetter) ||
startNumber === '' || !'12345678'.includes(startNumber)
) {
alert('Not a valid coordinate: ' + startLetter + startNumber);
return;
}
// Capturing
let captures = false;
if (move.slice(index, index + 1) === 'x') {
captures = true;
index++;
}
// End square
let endLetter = move.slice(index, index + 1);
let endNumber = move.slice(index + 1, index + 2);
index += 2;
if (
endLetter === '' || !'abcdefgh'.includes(endLetter) ||
endNumber === '' || !'12345678'.includes(endNumber)
) {
alert('Not a valid coordinate: ' + endLetter + endNumber);
return;
}
// Promotion
let promotion = null;
if (move.slice(index, index + 1) === '=') {
index++;
switch (move.slice(index, index + 1)) {
case 'Q':
case 'B':
case 'N':
case 'R':
promotion = move.slice(index, index + 1);
break;
case 'K':
alert('Cannot promote a piece to king');
return;
break;
case 'P':
alert('Cannot promote a piece to pawn');
return;
break;
default:
alert('Unrecognized piece: ' + move.slice(index, index + 1));
return;
}
index++;
}
// Check(mate)
switch(move.slice(index)) {
case '':
// nothing
break;
case '+':
// TODO: Check
break;
case '++':
// TODO: Checkmate'
break;
default:
alert('Unexpected contents at the end of the move: ' + move.slice(index));
return;
}
// End of parsing, start of move construction
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 renderedPiece = whiteMove ? whitePieces[piece] : blackPieces[piece];
let renderedPromotion = null;
if (promotion) {
renderedPromotion = whiteMove ? whitePieces[promotion] : blackPieces[promotion];
}
// Are we moving the piece onto itself?
if (startLetter === endLetter && startNumber === endNumber) {
alert('Start and end squares of the move cannot be identical');
return;
}
// Are we moving the piece from where it is located?
let startSquare = document.getElementById(startLetter + startNumber);
if (startSquare.childNodes.length === 0) {
alert('No piece on square ' + startLetter + startNumber);
return;
}
if (startSquare.childNodes[0].data !== renderedPiece) {
let pieceNames = {'K': 'king', 'Q': 'vizier', 'R': 'rook', 'B': 'elephant', 'N': 'horse', 'P': 'pawn'};
alert((whiteMove ? 'White' : 'Black') + ' ' + pieceNames[piece] + ' not on square ' + startLetter + startNumber);
return;
}
// TODO: Check piece is allowed to move like that
// Can the piece move to this location?
let endSquare = document.getElementById(endLetter + endNumber);
if (endSquare.childNodes.length !== 0 && !captures) {
alert('Square ' + endLetter + endNumber + ' is occupied, did you mean to capture?');
return;
}
if (endSquare.childNodes.length === 0 && captures) {
alert('Square ' + endLetter + endNumber + ' empty even though capture was specified');
return;
}
// Record our alterations to the table
let moveRecord = {
start: startLetter + startNumber,
end: endLetter + endNumber,
startPiece: renderedPiece,
endPiece: promotion ? renderedPromotion : renderedPiece,
capturedPiece: captures ? endSquare.childNodes[0].data : null
};
moveHistory.push(moveRecord);
// Apply move
if (captures) {
endSquare.removeChild(endSquare.childNodes[0]);
}
startSquare.removeChild(startSquare.childNodes[0]);
endSquare.appendChild(document.createTextNode(promotion ? renderedPromotion : renderedPiece))
// Flip whose turn it is
document.getElementById('tomove').childNodes[0].data = whiteMove ? 'Black' : 'White';
// List the move
let listElement = document.createElement('li');
listElement.appendChild(document.createTextNode(move));
document.getElementById('movelist').appendChild(listElement);
// Empty input field
document.getElementById('move').value = '';
}
function undoMove() {
if (moveHistory.length === 0) {
alert('No moves to undo');
return;
}
// Undo move on the board
let {start, end, startPiece, endPiece, capturedPiece} = moveHistory.pop();
let startSquare = document.getElementById(start);
let endSquare = document.getElementById(end);
endSquare.removeChild(endSquare.childNodes[0]);
startSquare.appendChild(document.createTextNode(startPiece));
if (capturedPiece) {
endSquare.appendChild(document.createTextNode(capturedPiece));
}
// Flip whose turn it is
let whiteMove = document.getElementById('tomove').childNodes[0].data == 'White';
document.getElementById('tomove').childNodes[0].data = whiteMove ? 'Black' : 'White';
// Remove from list of moves
document.getElementById('movelist').removeChild(document.getElementById('movelist').lastChild);
}
</script>
</body>
</html>