Tutorial pas a pas · Basat en el codi del Prof. F. Pérez · Responsiu per a PC i mòbil
Aprendrem a crear un joc d'escacs jugable al navegador utilitzant les llibreries chessboard.js (per al tauler interactiu) i chess.js (per a la lògica del joc). El jugador controla les peces blanques arrossegant-les, i l'ordinador respon amb un moviment aleatori de les negres.
Definim el tipus de document HTML5, l'idioma català, el joc de caràcters UTF-8,
i la meta etiqueta viewport per garantir que el disseny s'adapti
a qualsevol dispositiu.
<!DOCTYPE html>
<html lang="ca">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Play Chess</title>
</head>
Carreguem des d'un CDN el CSS de chessboard.js, la llibreria jQuery (necessària per chessboard.js), el JavaScript de chessboard.js i chess.js per gestionar la lògica dels escacs (moviments, torns, escac i mat, etc.).
<link rel="stylesheet" href="https://unpkg.com/@chrisoakman/chessboardjs@1.0.0/dist/chessboard-1.0.0.min.css">
<script src="https://unpkg.com/jquery@3.6.0/dist/jquery.min.js"></script>
<script src="https://unpkg.com/@chrisoakman/chessboardjs@1.0.0/dist/chessboard-1.0.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.12.1/chess.min.js"></script>
Donem estil al contenidor del tauler (#board) perquè es centri
i tingui una amplada fixa de 400px (suficient per a la majoria
de pantalles). Els divs de missatges (#game-status,
#error-message, #moves) es mostren centrats
amb colors distintius.
#board {
width: 400px;
margin: 20px auto;
}
.messages {
text-align: center;
margin-top: 20px;
}
#game-status {
color: green;
}
#error-message {
color: red;
}
#moves {
color: blue;
}
Dins del body afegim un títol, un div#board on
chessboard.js renderitzarà el tauler, i tres div per als missatges
d'estat, error i llista de moviments legals.
<body>
<h1>Play Chess</h1>
<div id="board"></div>
<div class="messages">
<div id="game-status"></div>
<div id="error-message"></div>
<div id="moves"></div>
</div>
</body>
</html>
Quan el DOM està completament carregat, creem el tauler amb
Chessboard('board', { ... }). L'opció draggable: true
permet arrossegar les peces. position: 'start' col·loca la
posició inicial. pieceTheme defineix la ruta de les imatges
de les peces (en aquest exemple, des d'una carpeta local; a la demo farem
servir imatges en línia). onDrop associa la funció que
gestionarà cada moviment.
document.addEventListener("DOMContentLoaded", function() {
let board, game;
function initChessboard() {
board = Chessboard('board', {
draggable: true,
position: 'start',
pieceTheme: 'img/chesspieces/wikipedia/{piece}.png',
onDrop: handleMove,
});
game = new Chess();
updateMoves();
}
initChessboard();
});
pieceTheme accepta el comodí {piece},
que se substitueix per codis com wP (peó blanc), bK (rei negre), etc.
La funció handleMove(source, target) s'executa cada cop que
s'arrossega una peça. Primer neteja els missatges anteriors. Després intenta
fer el moviment amb game.move(); si no és vàlid, mostra un error
i retorna 'snapback' (la peça torna al lloc original). Si el
moviment és vàlid, actualitza el tauler amb board.position(game.fen()).
Si el joc ha acabat, mostra "Game over". Si és el torn de les negres, l'ordinador
tria un moviment aleatori i el juga automàticament.
function handleMove(source, target) {
clearMessages();
const move = game.move({
from: source,
to: target,
promotion: 'q' // promoció automàtica a reina
});
if (move === null) {
displayErrorMessage('Invalid move!');
return 'snapback';
}
board.position(game.fen());
if (game.game_over()) {
displayGameStatus('Game over');
return;
}
if (game.turn() === 'b') {
// L'ordinador (negres) juga un moviment aleatori
const blackMoves = game.moves({ verbose: true });
const randomIndex = Math.floor(Math.random() * blackMoves.length);
const randomBlackMove = blackMoves[randomIndex];
game.move(randomBlackMove);
board.position(game.fen());
if (game.game_over()) {
displayGameStatus('Game over');
return;
}
updateMoves();
} else {
updateMoves();
}
}
Fixa't que s'utilitza promotion: 'q' per promocionar automàticament
a reina. En una versió més avançada es podria preguntar a l'usuari quina peça vol.
updateMoves() obté tots els moviments legals, en filtra els que
deixarien el rei en escac, i mostra la llista en notació algebraica (SAN).
Les altres funcions s'encarreguen de mostrar o netejar els missatges d'estat
i error.
function updateMoves() {
const moves = game.moves({ verbose: true });
const legalMoves = moves
.filter(move => {
game.move(move);
const isLegal = !game.in_check();
game.undo();
return isLegal;
})
.map(move => move.san);
document.getElementById('moves').innerText = 'Legal moves: ' + legalMoves.join(', ');
}
function displayGameStatus(message) {
document.getElementById('game-status').innerText = message;
}
function displayErrorMessage(message) {
document.getElementById('error-message').innerText = message;
}
function clearMessages() {
document.getElementById('game-status').innerText = '';
document.getElementById('error-message').innerText = '';
document.getElementById('moves').innerText = '';
}
Amb window.onerror capturem qualsevol error no controlat i el
mostrem a l'usuari mitjançant handleError, que escriu el missatge
al div d'error i el registra a la consola.
function handleError(error) {
displayErrorMessage('Error: ' + error.message);
console.error(error);
}
window.onerror = function (message, source, lineno, colno, error) {
handleError(error || new Error(message + ' at ' + source + ':' + lineno + ':' + colno));
};
A continuació tens el codi complet, adaptat perquè les imatges de les peces
es carreguin des d'un CDN públic i el tauler sigui responsiu. Desa'l com a
fitxer .html i obre'l al navegador per jugar.
<!DOCTYPE html>
<html lang="ca">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Play Chess</title>
<link rel="stylesheet" href="https://unpkg.com/@chrisoakman/chessboardjs@1.0.0/dist/chessboard-1.0.0.min.css">
<script src="https://unpkg.com/jquery@3.6.0/dist/jquery.min.js"></script>
<script src="https://unpkg.com/@chrisoakman/chessboardjs@1.0.0/dist/chessboard-1.0.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.12.1/chess.min.js"></script>
<style>
#board {
width: 400px;
margin: 20px auto;
}
.messages {
text-align: center;
margin-top: 20px;
}
#game-status {
color: green;
}
#error-message {
color: red;
}
#moves {
color: blue;
}
</style>
</head>
<body>
<h1>Play Chess</h1>
<div id="board"></div>
<div class="messages">
<div id="game-status"></div>
<div id="error-message"></div>
<div id="moves"></div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
let board, game;
function initChessboard() {
board = Chessboard('board', {
draggable: true,
position: 'start',
pieceTheme: 'https://chessboardjs.com/img/chesspieces/wikipedia/{piece}.png',
onDrop: handleMove,
});
game = new Chess();
updateMoves();
}
function handleMove(source, target) {
clearMessages();
const move = game.move({
from: source,
to: target,
promotion: 'q'
});
if (move === null) {
displayErrorMessage('Invalid move!');
return 'snapback';
}
board.position(game.fen());
if (game.game_over()) {
displayGameStatus('Game over');
return;
}
if (game.turn() === 'b') {
const blackMoves = game.moves({ verbose: true });
const randomIndex = Math.floor(Math.random() * blackMoves.length);
const randomBlackMove = blackMoves[randomIndex];
game.move(randomBlackMove);
board.position(game.fen());
if (game.game_over()) {
displayGameStatus('Game over');
return;
}
updateMoves();
} else {
updateMoves();
}
}
function updateMoves() {
const moves = game.moves({ verbose: true });
const legalMoves = moves
.filter(move => {
game.move(move);
const isLegal = !game.in_check();
game.undo();
return isLegal;
})
.map(move => move.san);
document.getElementById('moves').innerText = 'Legal moves: ' + legalMoves.join(', ');
}
function displayGameStatus(message) {
document.getElementById('game-status').innerText = message;
}
function displayErrorMessage(message) {
document.getElementById('error-message').innerText = message;
}
function clearMessages() {
document.getElementById('game-status').innerText = '';
document.getElementById('error-message').innerText = '';
document.getElementById('moves').innerText = '';
}
function handleError(error) {
displayErrorMessage('Error: ' + error.message);
console.error(error);
}
window.onerror = function (message, source, lineno, colno, error) {
handleError(error || new Error(message + ' at ' + source + ':' + lineno + ':' + colno));
};
initChessboard();
});
</script>
</body>
</html>
pieceTheme
a un CDN públic (https://chessboardjs.com/img/chesspieces/wikipedia/{piece}.png)
perquè les imatges es carreguin correctament sense necessitat de carpeta local.
Arrossega les peces blanques per jugar. L'ordinador mourà una peça negra aleatòriament. El tauler és interactiu i funciona tant amb ratolí com amb pantalla tàctil.