anycoder-a1890e72 / index.html
bytestorm's picture
Upload folder using huggingface_hub
54099ef verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Adaptive Chess - Learn With Me</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--primary: #2c3e50;
--secondary: #3498db;
--accent: #e74c3c;
--light: #ecf0f1;
--dark: #1a252f;
--light-square: #f0d9b5;
--dark-square: #b58863;
--highlight: rgba(255, 255, 0, 0.4);
--possible-move: rgba(0, 0, 0, 0.2);
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, var(--dark) 0%, var(--primary) 100%);
min-height: 100vh;
color: var(--light);
overflow-x: hidden;
}
header {
text-align: center;
padding: 1.5rem;
background: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px);
}
header h1 {
font-size: clamp(1.5rem, 4vw, 2.5rem);
margin-bottom: 0.5rem;
background: linear-gradient(90deg, var(--secondary), var(--accent));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.built-with {
font-size: 0.85rem;
color: var(--light);
opacity: 0.8;
}
.built-with a {
color: var(--secondary);
text-decoration: none;
transition: color 0.3s;
}
.built-with a:hover {
color: var(--accent);
}
.container {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: flex-start;
gap: 2rem;
padding: 2rem;
max-width: 1400px;
margin: 0 auto;
}
.game-section {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
}
.board-container {
position: relative;
padding: 10px;
background: linear-gradient(145deg, #34495e, #2c3e50);
border-radius: 12px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}
.board {
display: grid;
grid-template-columns: repeat(8, 1fr);
width: clamp(280px, 80vw, 560px);
aspect-ratio: 1;
border-radius: 8px;
overflow: hidden;
}
.square {
aspect-ratio: 1;
display: flex;
justify-content: center;
align-items: center;
font-size: clamp(2rem, 8vw, 4rem);
cursor: pointer;
position: relative;
transition: transform 0.1s;
user-select: none;
}
.square.light {
background: var(--light-square);
}
.square.dark {
background: var(--dark-square);
}
.square.selected {
background: var(--highlight) !important;
}
.square.possible-move::after {
content: '';
position: absolute;
width: 30%;
height: 30%;
background: var(--possible-move);
border-radius: 50%;
}
.square.possible-capture::after {
content: '';
position: absolute;
width: 90%;
height: 90%;
border: 4px solid var(--possible-move);
border-radius: 50%;
background: transparent;
}
.square.last-move {
background: rgba(155, 199, 0, 0.4) !important;
}
.square.check {
background: radial-gradient(circle, var(--accent) 0%, transparent 70%) !important;
}
.piece {
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
transition: transform 0.15s;
}
.square:hover .piece {
transform: scale(1.1);
}
.info-panel {
background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(10px);
border-radius: 12px;
padding: 1.5rem;
width: clamp(280px, 80vw, 350px);
display: flex;
flex-direction: column;
gap: 1rem;
}
.stat-card {
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
padding: 1rem;
}
.stat-card h3 {
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 1px;
color: var(--secondary);
margin-bottom: 0.5rem;
}
.stat-value {
font-size: 1.8rem;
font-weight: bold;
}
.level-bar {
width: 100%;
height: 10px;
background: rgba(255, 255, 255, 0.2);
border-radius: 5px;
overflow: hidden;
margin-top: 0.5rem;
}
.level-fill {
height: 100%;
background: linear-gradient(90deg, var(--secondary), var(--accent));
transition: width 0.5s ease;
border-radius: 5px;
}
.status {
text-align: center;
padding: 0.75rem;
border-radius: 8px;
font-weight: bold;
background: rgba(255, 255, 255, 0.1);
}
.status.your-turn {
background: rgba(46, 204, 113, 0.3);
color: #2ecc71;
}
.status.ai-turn {
background: rgba(231, 76, 60, 0.3);
color: #e74c3c;
}
.status.game-over {
background: rgba(155, 89, 182, 0.3);
color: #9b59b6;
}
.buttons {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
button {
flex: 1;
min-width: 100px;
padding: 0.75rem 1rem;
border: none;
border-radius: 8px;
font-size: 0.9rem;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
text-transform: uppercase;
letter-spacing: 1px;
}
.btn-primary {
background: linear-gradient(135deg, var(--secondary), #2980b9);
color: white;
}
.btn-secondary {
background: rgba(255, 255, 255, 0.2);
color: white;
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3);
}
.move-history {
max-height: 200px;
overflow-y: auto;
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
padding: 0.75rem;
}
.move-history::-webkit-scrollbar {
width: 6px;
}
.move-history::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
}
.move-history::-webkit-scrollbar-thumb {
background: var(--secondary);
border-radius: 3px;
}
.move-row {
display: flex;
padding: 0.25rem 0;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
}
.move-number {
width: 30px;
color: var(--secondary);
}
.move-white,
.move-black {
flex: 1;
}
.thinking {
display: flex;
align-items: center;
gap: 0.5rem;
justify-content: center;
}
.thinking-dots {
display: flex;
gap: 4px;
}
.thinking-dots span {
width: 8px;
height: 8px;
background: var(--secondary);
border-radius: 50%;
animation: bounce 1.4s infinite ease-in-out both;
}
.thinking-dots span:nth-child(1) {
animation-delay: -0.32s;
}
.thinking-dots span:nth-child(2) {
animation-delay: -0.16s;
}
@keyframes bounce {
0%,
80%,
100% {
transform: scale(0);
}
40% {
transform: scale(1);
}
}
.promotion-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: none;
justify-content: center;
align-items: center;
z-index: 1000;
}
.promotion-modal.active {
display: flex;
}
.promotion-options {
display: flex;
gap: 1rem;
background: var(--primary);
padding: 1.5rem;
border-radius: 12px;
}
.promotion-piece {
font-size: 3rem;
padding: 0.5rem 1rem;
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
}
.promotion-piece:hover {
background: var(--secondary);
transform: scale(1.1);
}
.performance-graph {
height: 80px;
display: flex;
align-items: flex-end;
gap: 2px;
padding: 0.5rem;
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
}
.graph-bar {
flex: 1;
background: var(--secondary);
border-radius: 2px 2px 0 0;
transition: height 0.3s;
min-height: 4px;
}
.graph-bar.win {
background: #2ecc71;
}
.graph-bar.loss {
background: #e74c3c;
}
.graph-bar.draw {
background: #f39c12;
}
@media (max-width: 768px) {
.container {
padding: 1rem;
gap: 1rem;
}
.info-panel {
width: 100%;
}
header {
padding: 1rem;
}
}
.captured-pieces {
display: flex;
flex-wrap: wrap;
gap: 2px;
min-height: 30px;
font-size: 1.2rem;
}
.advantage {
font-size: 0.9rem;
color: var(--secondary);
}
</style>
</head>
<body>
<header>
<h1>♔ Adaptive Chess</h1>
<p class="built-with">Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder"
target="_blank">anycoder</a></p>
</header>
<div class="container">
<div class="game-section">
<div class="captured-pieces" id="captured-black"></div>
<div class="board-container">
<div class="board" id="board"></div>
</div>
<div class="captured-pieces" id="captured-white"></div>
</div>
<div class="info-panel">
<div id="status" class="status your-turn">Your Turn (White)</div>
<div class="stat-card">
<h3>AI Skill Level</h3>
<div class="stat-value" id="skill-level">1</div>
<div class="level-bar">
<div class="level-fill" id="level-fill" style="width: 5%"></div>
</div>
<small style="opacity: 0.7">Adapts to your play style</small>
</div>
<div class="stat-card">
<h3>Your Performance</h3>
<div style="display: flex; justify-content: space-between; margin-bottom: 0.5rem;">
<span>Wins: <strong id="wins">0</strong></span>
<span>Draws: <strong id="draws">0</strong></span>
<span>Losses: <strong id="losses">0</strong></span>
</div>
<div class="performance-graph" id="performance-graph"></div>
</div>
<div class="stat-card">
<h3>Move History</h3>
<div class="move-history" id="move-history"></div>
</div>
<div class="buttons">
<button class="btn-primary" onclick="newGame()">New Game</button>
<button class="btn-secondary" onclick="undoMove()">Undo</button>
</div>
<div class="buttons">
<button class="btn-secondary" onclick="flipBoard()">Flip Board</button>
<button class="btn-secondary" onclick="resetProgress()">Reset AI</button>
</div>
</div>
</div>
<div class="promotion-modal" id="promotion-modal">
<div class="promotion-options" id="promotion-options"></div>
</div>
<script>
// Chess piece unicode symbols
const PIECES = {
'K': '♔', 'Q': '♕', 'R': '♖', 'B': '♗', 'N': '♘', 'P': '♙',
'k': '♚', 'q': '♛', 'r': '♜', 'b': '♝', 'n': '♞', 'p': '♟'
};
// Game state
let board = [];
let selectedSquare = null;
let possibleMoves = [];
let currentTurn = 'white';
let moveHistory = [];
let gameHistory = [];
let castlingRights = { K: true, Q: true, k: true, q: true };
let enPassantSquare = null;
let halfMoveClock = 0;
let fullMoveNumber = 1;
let isFlipped = false;
let lastMove = null;
let capturedPieces = { white: [], black: [] };
// AI and adaptive learning state
let skillLevel = 1;
let maxSkillLevel = 20;
let playerStats = { wins: 0, draws: 0, losses: 0 };
let gameResults = [];
let playerMoveQuality = [];
let isAIThinking = false;
// Piece values for evaluation
const PIECE_VALUES = {
'p': 100, 'n': 320, 'b': 330, 'r': 500, 'q': 900, 'k': 20000,
'P': 100, 'N': 320, 'B': 330, 'R': 500, 'Q': 900, 'K': 20000
};
// Position tables for piece-square evaluation
const PAWN_TABLE = [
0, 0, 0, 0, 0, 0, 0, 0,
50, 50, 50, 50, 50, 50, 50, 50,
10, 10, 20, 30, 30, 20, 10, 10,
5, 5, 10, 25, 25, 10, 5, 5,
0, 0, 0, 20, 20, 0, 0, 0,
5, -5,-10, 0, 0,-10, -5, 5,
5, 10, 10,-20,-20, 10, 10, 5,
0, 0, 0, 0, 0, 0, 0, 0
];
const KNIGHT_TABLE = [
-50,-40,-30,-30,-30,-30,-40,-50,
-40,-20, 0, 0, 0, 0,-20,-40,
-30, 0, 10, 15, 15, 10, 0,-30,
-30, 5, 15, 20, 20, 15, 5,-30,
-30, 0, 15, 20, 20, 15, 0,-30,
-30, 5, 10, 15, 15, 10, 5,-30,
-40,-20, 0, 5, 5, 0,-20,-40,
-50,-40,-30,-30,-30,-30,-40,-50
];
const BISHOP_TABLE = [
-20,-10,-10,-10,-10,-10,-10,-20,
-10, 0, 0, 0, 0, 0, 0,-10,
-10, 0, 5, 10, 10, 5, 0,-10,
-10, 5, 5, 10, 10, 5, 5,-10,
-10, 0, 10, 10, 10, 10, 0,-10,
-10, 10, 10, 10, 10, 10, 10,-10,
-10, 5, 0, 0, 0, 0, 5,-10,
-20,-10,-10,-10,-10,-10,-10,-20
];
const ROOK_TABLE = [
0, 0, 0, 0, 0, 0, 0, 0,
5, 10, 10, 10, 10, 10, 10, 5,
-5, 0, 0, 0, 0, 0, 0, -5,
-5, 0, 0, 0, 0, 0, 0, -5,
-5, 0, 0, 0, 0, 0, 0, -5,
-5, 0, 0, 0, 0, 0, 0, -5,
-5, 0, 0, 0, 0, 0, 0, -5,
0, 0, 0, 5, 5, 0, 0, 0
];
const QUEEN_TABLE = [
-20,-10,-10, -5, -5,-10,-10,-20,
-10, 0, 0, 0, 0, 0, 0,-10,
-10, 0, 5, 5, 5, 5, 0,-10,
-5, 0, 5, 5, 5, 5, 0, -5,
0, 0, 5, 5, 5, 5, 0, -5,
-10, 5, 5, 5, 5, 5, 0,-10,
-10, 0, 5, 0, 0, 0, 0,-10,
-20,-10,-10, -5, -5,-10,-10,-20
];
const KING_TABLE = [
-30,-40,-40,-50,-50,-40,-40,-30,
-30,-40,-40,-50,-50,-40,-40,-30,
-30,-40,-40,-50,-50,-40,-40,-30,
-30,-40,-40,-50,-50,-40,-40,-30,
-20,-30,-30,-40,-40,-30,-30,-20,
-10,-20,-20,-20,-20,-20,-20,-10,
20, 20, 0, 0, 0, 0, 20, 20,
20, 30, 10, 0, 0, 10, 30, 20
];
const KING_ENDGAME_TABLE = [
-50,-40,-30,-20,-20,-30,-40,-50,
-30,-20,-10, 0, 0,-10,-20,-30,
-30,-10, 20, 30, 30, 20,-10,-30,
-30,-10, 30, 40, 40, 30,-10,-30,
-30,-10, 30, 40, 40, 30,-10,-30,
-30,-10, 20, 30, 30, 20,-10,-30,
-30,-30, 0, 0, 0, 0,-30,-30,
-50,-30,-30,-30,-30,-30,-30,-50
];
// Initialize the game
function initGame() {
loadProgress();
setupBoard();
renderBoard();
updateUI();
}
function setupBoard() {
board = [
['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'],
['p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'],
[null, null, null, null, null, null, null, null],
[null, null, null, null, null, null, null, null],
[null, null, null, null, null, null, null, null],
[null, null, null, null, null, null, null, null],
['P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'],
['R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R']
];
currentTurn = 'white';
selectedSquare = null;
possibleMoves = [];
moveHistory = [];
gameHistory = [];
castlingRights = { K: true, Q: true, k: true, q: true };
enPassantSquare = null;
halfMoveClock = 0;
fullMoveNumber = 1;
lastMove = null;
capturedPieces = { white: [], black: [] };
playerMoveQuality = [];
}
function renderBoard() {
const boardEl = document.getElementById('board');
boardEl.innerHTML = '';
for (let row = 0; row < 8; row++) {
for (let col = 0; col < 8; col++) {
const displayRow = isFlipped ? 7 - row : row;
const displayCol = isFlipped ? 7 - col : col;
const square = document.createElement('div');
const isLight = (displayRow + displayCol) % 2 === 0;
square.className = `square ${isLight ? 'light' : 'dark'}`;
square.dataset.row = displayRow;
square.dataset.col = displayCol;
// Highlight last move
if (lastMove) {
if ((displayRow === lastMove.fromRow && displayCol === lastMove.fromCol) ||
(displayRow === lastMove.toRow && displayCol === lastMove.toCol)) {
square.classList.add('last-move');
}
}
// Highlight selected square
if (selectedSquare && selectedSquare.row === displayRow && selectedSquare.col === displayCol) {
square.classList.add('selected');
}
// Show possible moves
const isPossibleMove = possibleMoves.some(m => m.toRow === displayRow && m.toCol === displayCol);
if (isPossibleMove) {
const targetPiece = board[displayRow][displayCol];
if (targetPiece) {
square.classList.add('possible-capture');
} else {
square.classList.add('possible-move');
}
}
// Check highlight
const piece = board[displayRow][displayCol];
if (piece && piece.toLowerCase() === 'k') {
const pieceColor = piece === piece.toUpperCase() ? 'white' : 'black';
if (isInCheck(pieceColor)) {
square.classList.add('check');
}
}
// Add piece
if (piece) {
const pieceEl = document.createElement('span');
pieceEl.className = 'piece';
pieceEl.textContent = PIECES[piece];
square.appendChild(pieceEl);
}
square.addEventListener('click', () => handleSquareClick(displayRow, displayCol));
boardEl.appendChild(square);
}
}
updateCapturedPieces();
}
function updateCapturedPieces() {
const whiteEl = document.getElementById('captured-white');
const blackEl = document.getElementById('captured-black');
whiteEl.innerHTML = capturedPieces.white.map(p => PIECES[p]).join(' ');
blackEl.innerHTML = capturedPieces.black.map(p => PIECES[p]).join(' ');
// Calculate material advantage
const whiteValue = capturedPieces.black.reduce((sum, p) => sum + PIECE_VALUES[p], 0);
const blackValue = capturedPieces.white.reduce((sum, p) => sum + PIECE_VALUES[p], 0);
const diff = whiteValue - blackValue;
if (diff > 0) {
whiteEl.innerHTML += ` <span class="advantage">+${Math.floor(diff/100)}</span>`;
} else if (diff < 0) {
blackEl.innerHTML += ` <span class="advantage">+${Math.floor(-diff/100)}</span>`;
}
}
function handleSquareClick(row, col) {
if (isAIThinking) return;
if (currentTurn !== 'white') return;
const piece = board[row][col];
if (selectedSquare) {
// Check if clicking on a possible move
const move = possibleMoves.find(m => m.toRow === row && m.toCol === col);
if (move) {
makeMove(move);
return;
}
}
// Select a new piece
if (piece && isWhitePiece(piece)) {
selectedSquare = { row, col };
possibleMoves = getLegalMoves(row, col);
renderBoard();
} else {
selectedSquare = null;
possibleMoves = [];
renderBoard();
}
}
function isWhitePiece(piece) {
return piece && piece === piece.toUpperCase();
}
function isBlackPiece(piece) {
return piece && piece === piece.toLowerCase();
}
function getPieceColor(piece) {
if (!piece) return null;
return piece === piece.toUpperCase() ? 'white' : 'black';
}
function getLegalMoves(row, col) {
const piece = board[row][col];
if (!piece) return [];
const moves = getPseudoLegalMoves(row, col);
const color = getPieceColor(piece);
// Filter out moves that leave king in check
return moves.filter(move => {
const testBoard = JSON.parse(JSON.stringify(board));
testBoard[move.toRow][move.toCol] = testBoard[move.fromRow][move.fromCol];
testBoard[move.fromRow][move.fromCol] = null;
// Handle en passant capture
if (move.enPassant) {
testBoard[move.fromRow][move.toCol] = null;
}
// Handle castling - move rook
if (move.castling) {
if (move.toCol === 6) { // Kingside
testBoard[move.toRow][5] = testBoard[move.toRow][7];
testBoard[move.toRow][7] = null;
} else { // Queenside
testBoard[move.toRow][3] = testBoard[move.toRow][0];
testBoard[move.toRow][0] = null;
}
}
return !isInCheckWithBoard(color, testBoard);
});
}
function getPseudoLegalMoves(row, col) {
const piece = board[row][col];
if (!piece) return [];
const pieceType = piece.toLowerCase();
const color = getPieceColor(piece);
const moves = [];
switch (pieceType) {
case 'p':
moves.push(...getPawnMoves(row, col, color));
break;
case 'n':
moves.push(...getKnightMoves(row, col, color));
break;
case 'b':
moves.push(...getBishopMoves(row, col, color));
break;
case 'r':
moves.push(...getRookMoves(row, col, color));
break;
case 'q':
moves.push(...getQueenMoves(row, col, color));
break;
case 'k':
moves.push(...getKingMoves(row, col, color));
break;
}
return moves;
}
function getPawnMoves(row, col, color) {
const moves = [];
const direction = color === 'white' ? -1 : 1;
const startRow = color === 'white' ? 6 : 1;
const promotionRow = color === 'white' ? 0 : 7;
// Forward move
if (isValidSquare(row + direction, col) && !board[row + direction][col]) {
if (row + direction === promotionRow) {
moves.push({ fromRow: row, fromCol: col, toRow: row + direction, toCol: col, promotion: true });
} else {
moves.push({ fromRow: row, fromCol: col, toRow: row + direction, toCol: col });
}
// Double move from start
if (row === startRow && !board[row + 2 * direction][col]) {
moves.push({ fromRow: row, fromCol: col, toRow: row + 2 * direction, toCol: col, doublePawn: true });
}
}
// Captures
for (const dc of [-1, 1]) {
const newCol = col + dc;
if (isValidSquare(row + direction, newCol)) {
const target = board[row + direction][newCol];
if (target && getPieceColor(target) !== color) {
if (row + direction === promotionRow) {
moves.push({ fromRow: row, fromCol: col, toRow: row + direction, toCol: newCol, promotion: true });
} else {
moves.push({ fromRow: row, fromCol: col, toRow: row + direction, toCol: newCol });
}
}
// En passant
if (enPassantSquare && enPassantSquare.row === row + direction && enPassantSquare.col === newCol) {
moves.push({ fromRow: row, fromCol: col, toRow: row + direction, toCol: newCol, enPassant: true });
}
}
}
return moves;
}
function getKnightMoves(row, col, color) {
const moves = [];
const offsets = [[-2, -1], [-2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2], [2, -1], [2, 1]];
for (const [dr, dc] of offsets) {
const newRow = row + dr;
const newCol = col + dc;
if (isValidSquare(newRow, newCol)) {
const target = board[newRow][newCol];
if (!target || getPieceColor(target) !== color) {
moves.push({ fromRow: row, fromCol: col, toRow: newRow, toCol: newCol });
}
}
}
return moves;
}
function getSlidingMoves(row, col, color, directions) {
const moves = [];
for (const [dr, dc] of directions) {
let newRow = row + dr;
let newCol = col + dc;
while (isValidSquare(newRow, newCol)) {
const target = board[newRow][newCol];
if (!target) {
moves.push({ fromRow: row, fromCol: col, toRow: newRow, toCol: newCol });
} else {
if (getPieceColor(target) !== color) {
moves.push({ fromRow: row, fromCol: col, toRow: newRow, toCol: newCol });
}
break;
}
newRow += dr;
newCol += dc;
}
}
return moves;
}
function getBishopMoves(row, col, color) {
return getSlidingMoves(row, col, color, [[-1, -1], [-1, 1], [1, -1], [1, 1]]);
}
function getRookMoves(row, col, color) {
return getSlidingMoves(row, col, color, [[-1, 0], [1, 0], [0, -1], [0, 1]]);
}
function getQueenMoves(row, col, color) {
return [...getBishopMoves(row, col, color), ...getRookMoves(row, col, color)];
}
function getKingMoves(row, col, color) {
const moves = [];
const offsets = [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]];
for (const [dr, dc] of offsets) {
const newRow = row + dr;
const newCol = col + dc;
if (isValidSquare(newRow, newCol)) {
const target = board[newRow][newCol];
if (!target || getPieceColor(target) !== color) {
moves.push({ fromRow: row, fromCol: col, toRow: newRow, toCol: newCol });
}
}
}
// Castling
if (!isInCheck(color)) {
const kingRow = color === 'white' ? 7 : 0;
if (row === kingRow && col === 4) {
// Kingside
const kingSideRight = color === 'white' ? castlingRights.K : castlingRights.k;
if (kingSideRight && !board[kingRow][5] && !board[kingRow][6]) {
if (!isSquareAttacked(kingRow, 5, color) && !isSquareAttacked(kingRow, 6, color)) {
moves.push({ fromRow: row, fromCol: col, toRow: kingRow, toCol: 6, castling: true });
}
}
// Queenside
const queenSideRight = color === 'white' ? castlingRights.Q : castlingRights.q;
if (queenSideRight && !board[kingRow][1] && !board[kingRow][2] && !board[kingRow][3]) {
if (!isSquareAttacked(kingRow, 2, color) && !isSquareAttacked(kingRow, 3, color)) {
moves.push({ fromRow: row, fromCol: col, toRow: kingRow, toCol: 2, castling: true });
}
}
}
}
return moves;
}
function isValidSquare(row, col) {
return row >= 0 && row < 8 && col >= 0 && col < 8;
}
function isInCheck(color) {
return isInCheckWithBoard(color, board);
}
function isInCheckWithBoard(color, testBoard) {
// Find king
let kingRow, kingCol;
for (let r = 0; r < 8; r++) {
for (let c = 0; c < 8; c++) {
const piece = testBoard[r][c];
if (piece && piece.toLowerCase() === 'k' && getPieceColor(piece) === color) {
kingRow = r;
kingCol = c;
break;
}
}
}
return isSquareAttackedWithBoard(kingRow, kingCol, color, testBoard);
}
function isSquareAttacked(row, col, defendingColor) {
return isSquareAttackedWithBoard(row, col, defendingColor, board);
}
function isSquareAttackedWithBoard(row, col, defendingColor, testBoard) {