|
<!DOCTYPE html> |
|
<html lang="fr"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Jeu d'Échecs Tactile</title> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
<style> |
|
|
|
.chess-piece-font { |
|
font-family: 'Segoe UI Symbol', 'Apple Symbols', 'Noto Sans Symbols 2', sans-serif; |
|
} |
|
|
|
|
|
.possible-move-dot::after { |
|
content: ''; |
|
position: absolute; |
|
width: 30%; |
|
height: 30%; |
|
background-color: currentColor; |
|
border-radius: 50%; |
|
opacity: 0.6; |
|
|
|
} |
|
|
|
|
|
.chess-board-grid { |
|
display: grid; |
|
grid-template-columns: repeat(8, 1fr); |
|
grid-template-rows: repeat(8, 1fr); |
|
aspect-ratio: 1; |
|
} |
|
</style> |
|
</head> |
|
<body class="bg-gray-900 text-gray-100 min-h-screen flex flex-col items-center p-4 selection:bg-teal-500 selection:text-white"> |
|
|
|
<div class="container mx-auto max-w-6xl w-full"> |
|
<h1 class="text-3xl sm:text-4xl font-bold text-center my-6 text-teal-400 tracking-tight">Jeu d'Échecs Tactile</h1> |
|
|
|
<div class="bg-gray-800 p-4 sm:p-6 rounded-lg shadow-xl mb-6"> |
|
<h2 class="text-xl sm:text-2xl font-semibold mb-4 text-sky-400">Configuration</h2> |
|
<div class="flex flex-wrap gap-3 items-center"> |
|
<button id="setPvP" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-75 transition-colors"> |
|
Humain vs Humain |
|
</button> |
|
<div class="flex flex-col sm:flex-row gap-2"> |
|
<button id="setPvAIWhite" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded-md focus:outline-none focus:ring-2 focus:ring-green-400 focus:ring-opacity-75 transition-colors"> |
|
Jouer vs IA (Blancs) |
|
</button> |
|
<button id="setPvAIBlack" class="bg-purple-600 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-400 focus:ring-opacity-75 transition-colors"> |
|
Jouer vs IA (Noirs) |
|
</button> |
|
</div> |
|
<button id="resetGame" class="bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-md focus:outline-none focus:ring-2 focus:ring-red-400 focus:ring-opacity-75 transition-colors"> |
|
Nouvelle Partie |
|
</button> |
|
</div> |
|
<p class="mt-3 text-sm text-gray-400">Mode: <span id="currentMode" class="font-semibold text-gray-200">PVP</span></p> |
|
<p id="playerColorInfo" class="mt-1 text-sm text-gray-400 hidden"> |
|
Vous jouez: <span id="currentPlayerColor" class="font-semibold text-gray-200">Blancs</span> |
|
</p> |
|
</div> |
|
|
|
<div class="grid lg:grid-cols-3 gap-6"> |
|
<div class="lg:col-span-2 bg-gray-800 p-3 sm:p-4 rounded-lg shadow-xl"> |
|
<div id="chessBoard" class="chess-board-grid max-w-[600px] mx-auto border-4 border-gray-700 rounded-md overflow-hidden select-none"> |
|
|
|
</div> |
|
<div class="mt-4 text-center text-xs sm:text-sm text-gray-400"> |
|
<p>Cliquez sur une pièce puis sur la destination, ou glissez-déposez.</p> |
|
</div> |
|
</div> |
|
|
|
<div class="bg-gray-800 p-4 sm:p-6 rounded-lg shadow-xl lg:col-span-1"> |
|
<h3 class="text-lg sm:text-xl font-semibold mb-3 text-sky-400">Informations</h3> |
|
<p id="turnDisplay" class="mb-2 text-sm">Tour: <span class="font-bold text-white">Blancs</span></p> |
|
<p id="status" class="text-yellow-400 font-semibold mb-4 min-h-[1.5em] text-sm"></p> |
|
|
|
<div id="outcomeDisplay" class="mb-4 text-lg font-bold text-center text-green-400"></div> |
|
|
|
<div class="space-y-3 text-sm"> |
|
<div> |
|
<p class="text-gray-400">Dernier coup:</p> |
|
<p id="lastMove" class="text-gray-200 font-mono">-</p> |
|
</div> |
|
<div> |
|
<p class="text-gray-400">Dernier coup IA:</p> |
|
<p id="lastAIMove" class="text-gray-200 font-mono">-</p> |
|
</div> |
|
</div> |
|
|
|
<div class="mt-6"> |
|
<h4 class="text-md sm:text-lg font-semibold mb-2 text-sky-400">Notation manuelle</h4> |
|
<form id="moveForm" class="space-y-3"> |
|
<input type="text" id="moveInput" |
|
class="w-full bg-gray-700 border border-gray-600 rounded-md py-2 px-3 text-white text-sm focus:outline-none focus:ring-2 focus:ring-sky-500 focus:border-sky-500 placeholder-gray-500" |
|
placeholder="ex: e2e4, Nf3"> |
|
<button type="submit" |
|
class="w-full bg-teal-600 hover:bg-teal-700 text-white font-bold py-2 px-4 rounded-md focus:outline-none focus:ring-2 focus:ring-teal-400 focus:ring-opacity-75 transition duration-150 ease-in-out"> |
|
Jouer |
|
</button> |
|
</form> |
|
</div> |
|
</div> |
|
</div> |
|
<footer class="text-center text-gray-500 py-8 text-sm"> |
|
Jeu d'Échecs Tactile © 2024 |
|
</footer> |
|
</div> |
|
|
|
<script> |
|
let gameMode = 'pvp'; |
|
let playerColor = 'white'; |
|
let isPlayerTurn = true; |
|
let gameBoard = {}; |
|
let selectedSquare = null; |
|
let currentPossibleMoves = []; |
|
let lastMoveSquares = []; |
|
|
|
const chessBoardEl = document.getElementById('chessBoard'); |
|
const moveForm = document.getElementById('moveForm'); |
|
const moveInput = document.getElementById('moveInput'); |
|
const statusDisplay = document.getElementById('status'); |
|
const turnDisplayEl = document.getElementById('turnDisplay').querySelector('span'); |
|
const outcomeDisplay = document.getElementById('outcomeDisplay'); |
|
const lastMoveDisplay = document.getElementById('lastMove'); |
|
const lastAIMoveDisplay = document.getElementById('lastAIMove'); |
|
const currentModeDisplay = document.getElementById('currentMode'); |
|
const playerColorInfoDisplay = document.getElementById('playerColorInfo'); |
|
const currentPlayerColorDisplay = document.getElementById('currentPlayerColor'); |
|
|
|
const pieceSymbols = { |
|
'K': '♔', 'Q': '♕', 'R': '♖', 'B': '♗', 'N': '♘', 'P': '♙', |
|
'k': '♚', 'q': '♛', 'r': '♜', 'b': '♝', 'n': '♞', 'p': '♟' |
|
}; |
|
|
|
const initialFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; |
|
let currentFEN = initialFEN; |
|
|
|
|
|
const selectedClasses = ['ring-4', 'ring-yellow-400', 'ring-inset', 'z-20']; |
|
const possibleMoveClasses = ['ring-2', 'ring-emerald-500', 'ring-inset', 'possible-move-dot', 'text-emerald-500']; |
|
const lastMoveHighlightClasses = ['ring-2', 'ring-amber-500', 'ring-inset']; |
|
|
|
function createChessBoard() { |
|
chessBoardEl.innerHTML = ''; |
|
|
|
for (let rank = 8; rank >= 1; rank--) { |
|
for (let file = 0; file < 8; file++) { |
|
const fileChar = String.fromCharCode(97 + file); |
|
const squareId = fileChar + rank; |
|
|
|
const squareEl = document.createElement('div'); |
|
|
|
squareEl.className = `relative flex items-center justify-center cursor-pointer transition-all duration-150 ease-in-out |
|
${(rank + file) % 2 === 0 ? 'bg-[#f0d9b5]' : 'bg-[#b58863]'}`; |
|
squareEl.dataset.square = squareId; |
|
|
|
if (file === 0) { |
|
const rankCoord = document.createElement('div'); |
|
rankCoord.className = 'absolute top-0.5 left-1 text-xs font-bold text-gray-700 opacity-60'; |
|
rankCoord.textContent = rank; |
|
squareEl.appendChild(rankCoord); |
|
} |
|
if (rank === 1) { |
|
const fileCoord = document.createElement('div'); |
|
fileCoord.className = 'absolute bottom-0.5 right-1 text-xs font-bold text-gray-700 opacity-60'; |
|
fileCoord.textContent = fileChar; |
|
squareEl.appendChild(fileCoord); |
|
} |
|
|
|
squareEl.addEventListener('click', handleSquareClick); |
|
squareEl.addEventListener('dragover', handleDragOver); |
|
squareEl.addEventListener('drop', handleDrop); |
|
|
|
chessBoardEl.appendChild(squareEl); |
|
} |
|
} |
|
} |
|
|
|
function updateBoardFromFEN(fen) { |
|
const [boardPart] = fen.split(' '); |
|
const ranks = boardPart.split('/'); |
|
gameBoard = {}; |
|
|
|
document.querySelectorAll('.chess-piece').forEach(piece => piece.remove()); |
|
|
|
for (let rankIdx = 0; rankIdx < 8; rankIdx++) { |
|
const rank = 8 - rankIdx; |
|
const rankStr = ranks[rankIdx]; |
|
let fileIdx = 0; |
|
|
|
for (let char of rankStr) { |
|
if (isNaN(parseInt(char))) { |
|
const file = String.fromCharCode(97 + fileIdx); |
|
const squareId = file + rank; |
|
gameBoard[squareId] = char; |
|
|
|
const squareEl = document.querySelector(`[data-square="${squareId}"]`); |
|
if (squareEl) { |
|
const pieceEl = document.createElement('div'); |
|
|
|
pieceEl.className = `chess-piece-font text-3xl sm:text-4xl md:text-5xl |
|
cursor-grab transition-transform duration-100 ease-in-out z-10 |
|
${char === char.toUpperCase() ? 'text-white' : 'text-black'} |
|
hover:scale-110`; |
|
|
|
|
|
|
|
|
|
|
|
|
|
pieceEl.className = `chess-piece-font text-4xl sm:text-5xl |
|
cursor-grab transition-transform duration-100 ease-in-out z-10 |
|
${char === char.toUpperCase() ? 'text-gray-100' : 'text-gray-900'} |
|
hover:scale-110`; |
|
|
|
|
|
pieceEl.textContent = pieceSymbols[char]; |
|
pieceEl.draggable = true; |
|
pieceEl.dataset.piece = char; |
|
pieceEl.dataset.square = squareId; |
|
|
|
pieceEl.addEventListener('dragstart', handleDragStart); |
|
pieceEl.addEventListener('dragend', handleDragEnd); |
|
|
|
squareEl.appendChild(pieceEl); |
|
} |
|
fileIdx++; |
|
} else { |
|
fileIdx += parseInt(char); |
|
} |
|
} |
|
} |
|
currentFEN = fen; |
|
updateTurnDisplay(); |
|
} |
|
|
|
function updateTurnDisplay() { |
|
const turn = currentFEN.split(' ')[1]; |
|
turnDisplayEl.textContent = turn === 'w' ? 'Blancs' : 'Noirs'; |
|
turnDisplayEl.className = `font-bold ${turn === 'w' ? 'text-white' : 'text-gray-300'}`; |
|
|
|
if (gameMode === 'ai') { |
|
const aiIsWhite = (playerColor === 'black'); |
|
const aiIsBlack = (playerColor === 'white'); |
|
|
|
if ((aiIsWhite && turn === 'w') || (aiIsBlack && turn === 'b')) { |
|
isPlayerTurn = false; |
|
statusDisplay.textContent = "L'IA réfléchit..."; |
|
} else { |
|
isPlayerTurn = true; |
|
statusDisplay.textContent = "À vous de jouer"; |
|
} |
|
} else { |
|
isPlayerTurn = true; |
|
statusDisplay.textContent = ""; |
|
} |
|
|
|
if (!currentFEN.includes(' # ')) { |
|
outcomeDisplay.innerHTML = ''; |
|
} |
|
} |
|
|
|
function handleSquareClick(e) { |
|
if (!isPlayerTurn) return; |
|
|
|
const squareId = e.currentTarget.dataset.square; |
|
const piece = gameBoard[squareId]; |
|
|
|
if (selectedSquare) { |
|
if (selectedSquare === squareId) { |
|
clearSelectionAndHighlights(); |
|
} else if (currentPossibleMoves.includes(squareId)) { |
|
makeMove(selectedSquare + squareId); |
|
} else if (piece && isPlayerPiece(piece)) { |
|
selectSquare(squareId); |
|
} else { |
|
clearSelectionAndHighlights(); |
|
} |
|
} else if (piece && isPlayerPiece(piece)) { |
|
selectSquare(squareId); |
|
} |
|
} |
|
|
|
function selectSquare(squareId) { |
|
clearSelectionAndHighlights(); |
|
selectedSquare = squareId; |
|
|
|
const squareEl = document.querySelector(`[data-square="${squareId}"]`); |
|
if (squareEl) squareEl.classList.add(...selectedClasses); |
|
|
|
showPossibleMovesForSquare(squareId); |
|
} |
|
|
|
function clearSelectionAndHighlights() { |
|
selectedSquare = null; |
|
currentPossibleMoves = []; |
|
|
|
document.querySelectorAll('.chess-board-grid > div').forEach(sq => { |
|
sq.classList.remove(...selectedClasses, ...possibleMoveClasses); |
|
}); |
|
} |
|
|
|
function clearAllHighlights() { |
|
clearSelectionAndHighlights(); |
|
document.querySelectorAll('.chess-board-grid > div').forEach(sq => { |
|
sq.classList.remove(...lastMoveHighlightClasses); |
|
}); |
|
} |
|
|
|
|
|
function showPossibleMovesForSquare(fromSquareId) { |
|
|
|
const piece = gameBoard[fromSquareId]; |
|
currentPossibleMoves = getSimulatedPossibleMoves(fromSquareId, piece); |
|
|
|
currentPossibleMoves.forEach(toSquareId => { |
|
const squareEl = document.querySelector(`[data-square="${toSquareId}"]`); |
|
if (squareEl) { |
|
squareEl.classList.add(...possibleMoveClasses); |
|
} |
|
}); |
|
} |
|
|
|
|
|
function getSimulatedPossibleMoves(square, piece) { |
|
const moves = []; |
|
if (!piece) return moves; |
|
const file = square.charCodeAt(0) - 97; |
|
const rankVal = parseInt(square[1]); |
|
|
|
if (piece.toLowerCase() === 'p') { |
|
const direction = piece === 'P' ? 1 : -1; |
|
const nextRank = rankVal + direction; |
|
if (nextRank >= 1 && nextRank <= 8) { |
|
const forwardOne = String.fromCharCode(97 + file) + nextRank; |
|
if (!gameBoard[forwardOne]) { |
|
moves.push(forwardOne); |
|
if ((piece === 'P' && rankVal === 2) || (piece === 'p' && rankVal === 7)) { |
|
const forwardTwo = String.fromCharCode(97 + file) + (rankVal + 2 * direction); |
|
if (!gameBoard[forwardTwo]) moves.push(forwardTwo); |
|
} |
|
} |
|
|
|
if (file > 0) { |
|
const captureLeft = String.fromCharCode(97 + file - 1) + nextRank; |
|
if (gameBoard[captureLeft] && isOpponentPiece(piece, gameBoard[captureLeft])) moves.push(captureLeft); |
|
} |
|
if (file < 7) { |
|
const captureRight = String.fromCharCode(97 + file + 1) + nextRank; |
|
if (gameBoard[captureRight] && isOpponentPiece(piece, gameBoard[captureRight])) moves.push(captureRight); |
|
} |
|
} |
|
} |
|
|
|
return moves; |
|
} |
|
|
|
function isOpponentPiece(myPiece, targetPiece) { |
|
if (!targetPiece) return false; |
|
const myPieceIsWhite = myPiece === myPiece.toUpperCase(); |
|
const targetPieceIsWhite = targetPiece === targetPiece.toUpperCase(); |
|
return myPieceIsWhite !== targetPieceIsWhite; |
|
} |
|
|
|
|
|
function isPlayerPiece(piece) { |
|
if (!piece) return false; |
|
const currentTurnColor = currentFEN.split(' ')[1]; |
|
const pieceIsWhite = piece === piece.toUpperCase(); |
|
|
|
if (gameMode === 'pvp') { |
|
return (currentTurnColor === 'w' && pieceIsWhite) || (currentTurnColor === 'b' && !pieceIsWhite); |
|
} |
|
|
|
|
|
return (playerColor === 'white' && pieceIsWhite && currentTurnColor === 'w') || |
|
(playerColor === 'black' && !pieceIsWhite && currentTurnColor === 'b'); |
|
} |
|
|
|
function handleDragStart(e) { |
|
const piece = e.target.dataset.piece; |
|
if (!isPlayerTurn || !isPlayerPiece(piece)) { |
|
e.preventDefault(); |
|
return; |
|
} |
|
|
|
e.target.classList.add('cursor-grabbing', 'scale-125', 'opacity-75', 'z-50'); |
|
e.dataTransfer.setData('text/plain', e.target.dataset.square); |
|
e.dataTransfer.effectAllowed = "move"; |
|
|
|
selectSquare(e.target.dataset.square); |
|
} |
|
|
|
function handleDragEnd(e) { |
|
e.target.classList.remove('cursor-grabbing', 'scale-125', 'opacity-75', 'z-50'); |
|
|
|
} |
|
|
|
function handleDragOver(e) { |
|
e.preventDefault(); |
|
e.dataTransfer.dropEffect = "move"; |
|
} |
|
|
|
function handleDrop(e) { |
|
e.preventDefault(); |
|
const fromSquareId = e.dataTransfer.getData('text/plain'); |
|
const toSquareEl = e.target.closest('[data-square]'); |
|
if (!toSquareEl) { |
|
clearSelectionAndHighlights(); |
|
return; |
|
} |
|
const toSquareId = toSquareEl.dataset.square; |
|
|
|
if (fromSquareId && toSquareId && fromSquareId !== toSquareId) { |
|
if (currentPossibleMoves.includes(toSquareId)) { |
|
makeMove(fromSquareId + toSquareId); |
|
} else { |
|
clearSelectionAndHighlights(); |
|
} |
|
} else { |
|
clearSelectionAndHighlights(); |
|
} |
|
} |
|
|
|
function highlightLastMoveUI(moveUci) { |
|
|
|
document.querySelectorAll('.chess-board-grid > div').forEach(sq => { |
|
sq.classList.remove(...lastMoveHighlightClasses); |
|
}); |
|
|
|
if (moveUci && moveUci.length >= 4) { |
|
const fromSquareId = moveUci.substring(0, 2); |
|
const toSquareId = moveUci.substring(2, 4); |
|
|
|
const fromEl = document.querySelector(`[data-square="${fromSquareId}"]`); |
|
const toEl = document.querySelector(`[data-square="${toSquareId}"]`); |
|
|
|
if (fromEl) fromEl.classList.add(...lastMoveHighlightClasses); |
|
if (toEl) toEl.classList.add(...lastMoveHighlightClasses); |
|
|
|
lastMoveSquares = [fromSquareId, toSquareId]; |
|
} |
|
} |
|
|
|
async function makeMove(moveStr) { |
|
statusDisplay.textContent = 'Traitement...'; |
|
clearSelectionAndHighlights(); |
|
|
|
try { |
|
const response = await fetch('/make_move', { |
|
method: 'POST', |
|
headers: { 'Content-Type': 'application/json' }, |
|
body: JSON.stringify({ move: moveStr }) |
|
}); |
|
const data = await response.json(); |
|
|
|
if (data.error) { |
|
statusDisplay.textContent = `Erreur: ${data.error}`; |
|
|
|
updateTurnDisplay(); |
|
} else { |
|
currentFEN = data.fen; |
|
updateBoardFromFEN(data.fen); |
|
lastMoveDisplay.textContent = data.human_move_san || moveStr; |
|
highlightLastMoveUI(moveStr); |
|
|
|
if (data.ai_move_uci) { |
|
|
|
setTimeout(() => { |
|
currentFEN = data.fen; |
|
updateBoardFromFEN(data.fen); |
|
lastAIMoveDisplay.textContent = data.ai_move_san || data.ai_move_uci; |
|
highlightLastMoveUI(data.ai_move_uci); |
|
updateTurnDisplay(); |
|
|
|
if (data.game_over) { |
|
handleGameOver(data.outcome); |
|
} |
|
}, gameMode === 'ai' && data.human_move_san ? 500 : 0); |
|
} else { |
|
updateTurnDisplay(); |
|
if (data.game_over) { |
|
handleGameOver(data.outcome); |
|
} |
|
} |
|
|
|
if (data.game_over && !data.ai_move_uci) { |
|
handleGameOver(data.outcome); |
|
} |
|
} |
|
} catch (error) { |
|
console.error("Erreur lors de la communication:", error); |
|
statusDisplay.textContent = "Erreur de communication avec le serveur."; |
|
updateTurnDisplay(); |
|
} |
|
} |
|
|
|
function handleGameOver(outcome) { |
|
statusDisplay.textContent = "Partie terminée!"; |
|
outcomeDisplay.innerHTML = `<p>${outcome}</p>`; |
|
isPlayerTurn = false; |
|
} |
|
|
|
|
|
moveForm.addEventListener('submit', async (e) => { |
|
e.preventDefault(); |
|
const move = moveInput.value.trim(); |
|
if (!move || !isPlayerTurn) return; |
|
await makeMove(move); |
|
moveInput.value = ''; |
|
}); |
|
|
|
document.getElementById('resetGame').addEventListener('click', async () => { |
|
statusDisplay.textContent = 'Réinitialisation...'; |
|
try { |
|
const response = await fetch('/reset_game', { method: 'POST' }); |
|
const data = await response.json(); |
|
|
|
gameMode = data.game_mode || 'pvp'; |
|
playerColor = data.player_color || 'white'; |
|
currentFEN = data.fen; |
|
|
|
updateBoardFromFEN(data.fen); |
|
lastMoveDisplay.textContent = "-"; |
|
lastAIMoveDisplay.textContent = "-"; |
|
outcomeDisplay.innerHTML = ''; |
|
clearAllHighlights(); |
|
|
|
currentModeDisplay.textContent = gameMode.toUpperCase(); |
|
if (gameMode === 'ai') { |
|
currentPlayerColorDisplay.textContent = playerColor === 'white' ? 'Blancs' : 'Noirs'; |
|
playerColorInfoDisplay.classList.remove('hidden'); |
|
} else { |
|
playerColorInfoDisplay.classList.add('hidden'); |
|
} |
|
statusDisplay.textContent = "Nouvelle partie prête."; |
|
updateTurnDisplay(); |
|
|
|
} catch (error) { |
|
console.error("Erreur lors de la réinitialisation:", error); |
|
statusDisplay.textContent = "Erreur lors de la réinitialisation."; |
|
} |
|
}); |
|
|
|
async function setGameMode(newMode, pColor = 'white') { |
|
statusDisplay.textContent = `Changement de mode...`; |
|
try { |
|
const response = await fetch('/set_mode', { |
|
method: 'POST', |
|
headers: { 'Content-Type': 'application/json' }, |
|
body: JSON.stringify({ game_mode: newMode, player_color: pColor }) |
|
}); |
|
const data = await response.json(); |
|
|
|
if (data.error) { |
|
statusDisplay.textContent = `Erreur: ${data.error}`; |
|
} else { |
|
gameMode = data.game_mode; |
|
playerColor = data.player_color; |
|
currentFEN = data.fen; |
|
|
|
updateBoardFromFEN(data.fen); |
|
clearAllHighlights(); |
|
|
|
currentModeDisplay.textContent = gameMode.toUpperCase(); |
|
if (gameMode === 'ai') { |
|
currentPlayerColorDisplay.textContent = playerColor === 'white' ? 'Blancs' : 'Noirs'; |
|
playerColorInfoDisplay.classList.remove('hidden'); |
|
} else { |
|
playerColorInfoDisplay.classList.add('hidden'); |
|
} |
|
|
|
statusDisplay.textContent = data.message || `Mode ${gameMode.toUpperCase()} activé.`; |
|
lastMoveDisplay.textContent = "-"; |
|
lastAIMoveDisplay.textContent = "-"; |
|
outcomeDisplay.innerHTML = ''; |
|
|
|
if (data.initial_ai_move_uci) { |
|
setTimeout(() => { |
|
currentFEN = data.fen; |
|
updateBoardFromFEN(data.fen); |
|
lastAIMoveDisplay.textContent = data.initial_ai_move_san || data.initial_ai_move_uci; |
|
highlightLastMoveUI(data.initial_ai_move_uci); |
|
updateTurnDisplay(); |
|
}, 500); |
|
} else { |
|
updateTurnDisplay(); |
|
} |
|
} |
|
} catch (error) { |
|
console.error("Erreur de changement de mode:", error); |
|
statusDisplay.textContent = "Erreur de changement de mode."; |
|
} |
|
} |
|
|
|
document.getElementById('setPvP').addEventListener('click', () => setGameMode('pvp')); |
|
document.getElementById('setPvAIWhite').addEventListener('click', () => setGameMode('ai', 'white')); |
|
document.getElementById('setPvAIBlack').addEventListener('click', () => setGameMode('ai', 'black')); |
|
|
|
function initializeGame() { |
|
createChessBoard(); |
|
|
|
|
|
fetch('/reset_game', { method: 'POST' }) |
|
.then(res => res.json()) |
|
.then(data => { |
|
gameMode = data.game_mode || 'pvp'; |
|
playerColor = data.player_color || 'white'; |
|
currentFEN = data.fen || initialFEN; |
|
|
|
updateBoardFromFEN(currentFEN); |
|
currentModeDisplay.textContent = gameMode.toUpperCase(); |
|
if (gameMode === 'ai') { |
|
currentPlayerColorDisplay.textContent = playerColor === 'white' ? 'Blancs' : 'Noirs'; |
|
playerColorInfoDisplay.classList.remove('hidden'); |
|
} else { |
|
playerColorInfoDisplay.classList.add('hidden'); |
|
} |
|
statusDisplay.textContent = "Prêt à jouer."; |
|
updateTurnDisplay(); |
|
}) |
|
.catch(err => { |
|
console.error("Failed to initialize game from server:", err); |
|
|
|
updateBoardFromFEN(initialFEN); |
|
currentModeDisplay.textContent = 'PVP'; |
|
playerColorInfoDisplay.classList.add('hidden'); |
|
statusDisplay.textContent = "Prêt à jouer (mode local)."; |
|
updateTurnDisplay(); |
|
}); |
|
} |
|
|
|
initializeGame(); |
|
</script> |
|
</body> |
|
</html> |