<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chess vs AI</title>
<style>
body { font-family: sans-serif; display: flex; flex-direction: column; align-items: center; margin-top: 20px; }
#status { margin-bottom: 10px; font-weight: bold; }
#chessboard { display: grid; grid-template-columns: repeat(8, 60px); grid-template-rows: repeat(8, 60px); }
.square { width: 60px; height: 60px; display: flex; justify-content: center; align-items: center; font-size: 36px; cursor: pointer; }
.white { background-color: #f0d9b5; }
.black { background-color: #b58863; }
.selected { outline: 3px solid red; }
.threat { outline: 3px solid red; box-sizing: border-box; }
</style>
</head>
<body>
<div id="status">White's turn</div>
<div id="chessboard"></div>
<button onclick="resetBoard()">Reset</button>
<script>
const boardContainer = document.getElementById('chessboard');
const status = document.getElementById('status');
let board = [];
let selected = null;
let whiteTurn = true;
// Unicode pieces
const pieces = {
'r':'♜', 'n':'♞', 'b':'♝', 'q':'♛', 'k':'♚', 'p':'♟',
'R':'♖', 'N':'♘', 'B':'♗', 'Q':'♕', 'K':'♔', 'P':'♙', '':''
};
// Initial board
const initialBoard = [
['r','n','b','q','k','b','n','r'],
['p','p','p','p','p','p','p','p'],
['','','','','','','',''],
['','','','','','','',''],
['','','','','','','',''],
['','','','','','','',''],
['P','P','P','P','P','P','P','P'],
['R','N','B','Q','K','B','N','R']
];
function renderBoard() {
boardContainer.innerHTML = '';
const threatened = getThreatenedSquares('white');
for (let i=0;i<8;i++) {
for (let j=0;j\<8;j++) {
const square = document.createElement('div');
square.classList.add('square');
square.classList.add((i+j)%2 === 0 ? 'white' : 'black');
square.textContent = pieces\[board\[i\]\[j\]\];
square.dataset.row = i;
square.dataset.col = j;
square.addEventListener('click', handleSquareClick);
if (selected && selected.row == i && selected.col == j) {
square.classList.add('selected');
}
if (threatened.some(s =\> s\[0\]===i && s\[1\]===j)) {
square.classList.add('threat');
}
boardContainer.appendChild(square);
}
}
}
// =====================
// Player (Black) logic
// =====================
function handleSquareClick(e) {
if (whiteTurn) return;
const row = parseInt(e.currentTarget.dataset.row);
const col = parseInt(e.currentTarget.dataset.col);
const piece = board[row][col];
if (!selected) {
if (piece && piece === piece.toLowerCase()) {
selected = {row, col};
renderBoard();
}
return;
}
if (isLegalMove(selected.row, selected.col, row, col, board[selected.row][selected.col])) {
board\[row\]\[col\] = board\[selected.row\]\[selected.col\];
board\[selected.row\]\[selected.col\] = '';
selected = null;
whiteTurn = true;
status.textContent = "White's turn";
renderBoard();
setTimeout(makeWhiteMove, 500);
} else {
selected = null;
renderBoard();
}
}
// =====================
// AI (White) logic
// =====================
function makeWhiteMove() {
const moves = getAllLegalMoves('white');
if (moves.length===0) {
status.textContent = "Black wins!";
return;
}
// prefer captures
let captureMoves = moves.filter(m => board[m.to[0]][m.to[1]] && board[m.to[0]][m.to[1]].toLowerCase());
let move = captureMoves.length ? captureMoves[Math.floor(Math.random()*captureMoves.length)] : moves[Math.floor(Math.random()*moves.length)];
board[move.to[0]][move.to[1]] = board[move.from[0]][move.from[1]];
board[move.from[0]][move.from[1]] = '';
whiteTurn = false;
status.textContent = "Black's turn";
renderBoard();
}
// =====================
// Move generation
// =====================
function getAllLegalMoves(color) {
const moves = [];
for (let i=0;i<8;i++){
for (let j=0;j\<8;j++){
let piece = board\[i\]\[j\];
if (!piece) continue;
if (color==='white' && piece!==piece.toUpperCase()) continue;
if (color==='black' && piece!==piece.toLowerCase()) continue;
const legal = getLegalMoves(i,j,piece);
legal.forEach(to =\> moves.push({from:\[i,j\],to}));
}
}
return moves;
}
function isLegalMove(r1,c1,r2,c2,piece) {
const legal = getLegalMoves(r1,c1,piece);
return legal.some(m => m[0]===r2 && m[1]===c2);
}
function getLegalMoves(r,c,piece) {
const moves = [];
const color = piece===piece.toUpperCase() ? 'white' : 'black';
const direction = color==='white' ? -1 : 1;
switch(piece.toLowerCase()){
case 'p':
if (board\[r+direction\] && board\[r+direction\]\[c\]==='') moves.push(\[r+direction,c\]);
if ((r===6 && color==='white') || (r===1 && color==='black')) {
if (board\[r+direction\]\[c\]==='' && board\[r+2\*direction\]\[c\]==='') moves.push(\[r+2\*direction,c\]);
}
if (c\>0 && board\[r+direction\]\[c-1\] && ((color==='white' && board\[r+direction\]\[c-1\].toLowerCase()) || (color==='black' && board\[r+direction\]\[c-1\].toUpperCase()))) moves.push(\[r+direction,c-1\]);
if (c\<7 && board\[r+direction\]\[c+1\] && ((color==='white' && board\[r+direction\]\[c+1\].toLowerCase()) || (color==='black' && board\[r+direction\]\[c+1\].toUpperCase()))) moves.push(\[r+direction,c+1\]);
break;
case 'r': moves.push(...linearMoves(r,c,\[\[1,0\],\[-1,0\],\[0,1\],\[0,-1\]\],color)); break;
case 'b': moves.push(...linearMoves(r,c,\[\[1,1\],\[1,-1\],\[-1,1\],\[-1,-1\]\],color)); break;
case 'q': moves.push(...linearMoves(r,c,\[\[1,0\],\[-1,0\],\[0,1\],\[0,-1\],\[1,1\],\[1,-1\],\[-1,1\],\[-1,-1\]\],color)); break;
case 'k':
for (let dr=-1;dr\<=1;dr++){
for (let dc=-1;dc\<=1;dc++){
if(dr===0 && dc===0) continue;
const nr=r+dr,nc=c+dc;
if(nr\>=0 && nr\<8 && nc\>=0 && nc\<8 && (!board\[nr\]\[nc\] || (color==='white'?board\[nr\]\[nc\].toLowerCase():board\[nr\]\[nc\].toUpperCase()))) moves.push(\[nr,nc\]);
}
}
break;
case 'n':
const knightMoves=\[\[2,1\],\[1,2\],\[2,-1\],\[1,-2\],\[-2,1\],\[-1,2\],\[-2,-1\],\[-1,-2\]\];
knightMoves.forEach(m=\>{
const nr=r+m\[0\],nc=c+m\[1\];
if(nr\>=0 && nr\<8 && nc\>=0 && nc\<8 && (!board\[nr\]\[nc\] || (color==='white'?board\[nr\]\[nc\].toLowerCase():board\[nr\]\[nc\].toUpperCase()))) moves.push(\[nr,nc\]);
});
break;
}
return moves;
}
function linearMoves(r,c,directions,color){
const moves = [];
directions.forEach(d=>{
let nr=r+d\[0\],nc=c+d\[1\];
while(nr\>=0 && nr\<8 && nc\>=0 && nc\<8){
if(!board\[nr\]\[nc\]) moves.push(\[nr,nc\]);
else {
if((color==='white' && board\[nr\]\[nc\].toLowerCase()) || (color==='black' && board\[nr\]\[nc\].toUpperCase())) moves.push(\[nr,nc\]);
break;
}
nr+=d\[0\]; nc+=d\[1\];
}
});
return moves;
}
// =====================
// Highlight AI threats
// =====================
function getThreatenedSquares(color) {
const moves = getAllLegalMoves(color);
return moves.map(m => m.to);
}
// =====================
// Reset
// =====================
function resetBoard() {
board = JSO
N.parse(JSON.stringify(initialBoard));
selected = null;
whiteTurn = true;
status.textContent = "White's turn";
renderBoard();
setTimeout(makeWhiteMove, 500);
}
resetBoard();
</script>
</body>
</html>