Joffrey Thomas
change
3339781
(() => {
const { roomId, basePath } = window.__ROOM_CONFIG__ || {};
const boardEl = document.getElementById('boardUI');
let board, chess;
async function getJSON(path, options) {
const res = await fetch(path, options);
if (!res.ok) throw new Error(await res.text());
return res.json();
}
async function refreshSideInfo() {
const s = await getJSON(`${basePath}/room/${roomId}/status`);
document.getElementById('turn').textContent = s.turn + (s.is_check ? ' (check)' : '');
document.getElementById('result').textContent = s.is_game_over ? (s.result || 'game over') : '-';
const legal = await getJSON(`${basePath}/room/${roomId}/legal_moves`);
document.getElementById('legal').textContent = legal.join(', ');
}
async function syncFromServer() {
const st = await getJSON(`${basePath}/room/${roomId}/board`);
// chess.js v0.13 uses load(fen)
chess.load(st.fen);
board.position(chess.fen());
await refreshSideInfo();
}
function onDragStart(source, piece) {
// Disallow moves if game over or trying to move wrong color
if (chess.game_over()) return false;
const turn = chess.turn() === 'w' ? 'white' : 'black';
if ((turn === 'white' && piece.search(/^b/) !== -1) || (turn === 'black' && piece.search(/^w/) !== -1)) {
return false;
}
return true;
}
async function onDrop(source, target) {
let moveObj = chess.move({ from: source, to: target, promotion: 'q' });
if (moveObj == null) return 'snapback';
// Send move to server in UCI
const uci = `${source}${target}${moveObj.promotion ? moveObj.promotion : ''}`;
try {
await getJSON(`${basePath}/room/${roomId}/player_move`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ move: uci })
});
await syncFromServer();
} catch (e) {
console.error(e);
chess.undo();
return 'snapback';
}
}
function onSnapEnd() {
board.position(chess.fen());
}
async function init() {
if (typeof Chess === 'undefined' || typeof Chessboard === 'undefined') {
console.error('Required libraries not loaded: Chess and/or Chessboard');
return;
}
chess = new Chess();
const pieceSvg = (piece) => {
const glyphs = { wK:'โ™”', wQ:'โ™•', wR:'โ™–', wB:'โ™—', wN:'โ™˜', wP:'โ™™', bK:'โ™š', bQ:'โ™›', bR:'โ™œ', bB:'โ™', bN:'โ™ž', bP:'โ™Ÿ' };
const isWhite = piece && piece[0] === 'w';
const glyph = glyphs[piece] || '?';
const fill = isWhite ? '#ffffff' : '#111827';
const stroke = isWhite ? '#e5e7eb' : '#0b0f19';
const svg = `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns='http://www.w3.org/2000/svg' width='64' height='64'>
<rect x='0' y='0' width='64' height='64' fill='transparent'/>
<text x='50%' y='62%' font-size='48' text-anchor='middle' fill='${fill}' stroke='${stroke}' stroke-width='1.5' font-family='Segoe UI Symbol, DejaVu Sans, Noto Sans Symbols, Arial, sans-serif'>${glyph}</text>
</svg>`;
return 'data:image/svg+xml;utf8,' + encodeURIComponent(svg);
};
board = Chessboard('boardUI', {
position: 'start', draggable: true, dropOffBoard: 'snapback',
pieceTheme: (p) => pieceSvg(p),
onDragStart, onDrop, onSnapEnd
});
await syncFromServer();
}
document.getElementById('startBtn').addEventListener('click', async () => {
const color = document.getElementById('color').value;
await getJSON(`${basePath}/room/${roomId}/start`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ player_color: color })});
board.orientation(color);
await syncFromServer();
});
document.getElementById('aiBtn').addEventListener('click', async () => {
await getJSON(`${basePath}/room/${roomId}/ai_move`, { method: 'POST' });
await syncFromServer();
});
document.getElementById('copyPgnBtn').addEventListener('click', async () => {
const data = await getJSON(`${basePath}/room/${roomId}/pgn`);
await navigator.clipboard.writeText(data.pgn || '');
alert('PGN copied to clipboard');
});
init();
})();