|
<!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</title> |
|
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
<style> |
|
|
|
.chess-board svg { |
|
cursor: default; |
|
} |
|
.square { |
|
width: 40px; height: 40px; |
|
display: flex; align-items: center; justify-content: center; |
|
border: 1px solid #ccc; |
|
} |
|
.selected-square { |
|
background-color: rgba(255, 255, 0, 0.5) !important; |
|
} |
|
</style> |
|
</head> |
|
<body class="bg-gray-100 flex flex-col items-center justify-center min-h-screen p-4"> |
|
|
|
<div class="bg-white p-6 rounded-lg shadow-xl w-full max-w-3xl"> |
|
<h1 class="text-3xl font-bold text-center text-gray-700 mb-6">Jeu d'Échecs</h1> |
|
|
|
<div class="controls mb-4 flex flex-wrap gap-2 justify-center"> |
|
<button id="newGameHuman" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"> |
|
Nouvelle Partie (Humain vs Humain) |
|
</button> |
|
<button id="newGameAIWhite" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"> |
|
Nouvelle Partie (Jouer Blancs vs IA) |
|
</button> |
|
<button id="newGameAIBlack" class="bg-gray-700 hover:bg-gray-900 text-white font-bold py-2 px-4 rounded"> |
|
Nouvelle Partie (Jouer Noirs vs IA) |
|
</button> |
|
</div> |
|
|
|
<div class="game-area flex flex-col md:flex-row gap-4 items-start"> |
|
|
|
<div id="boardDisplay" class="chess-board mx-auto md:mx-0 border-2 border-gray-500" style="width: 350px; height: 350px;"> |
|
|
|
{{ initial_board_svg|safe if initial_board_svg else "Chargement de l'échiquier..." }} |
|
</div> |
|
|
|
|
|
<div class="info-panel flex-grow p-4 bg-gray-50 rounded-md shadow"> |
|
<h2 class="text-xl font-semibold mb-2">Informations</h2> |
|
<p id="turnInfo" class="mb-1">Tour: Chargement...</p> |
|
<p id="statusInfo" class="mb-3 font-medium">Statut: Chargement...</p> |
|
|
|
<h3 class="text-lg font-semibold mb-1">Jouer un coup (ex: e2e4):</h3> |
|
<div class="move-input flex gap-2 mb-3"> |
|
<input type="text" id="moveInput" placeholder="e.g., e2e4" |
|
class="flex-grow p-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500"> |
|
<button id="submitMove" class="bg-indigo-500 hover:bg-indigo-700 text-white font-bold py-2 px-3 rounded"> |
|
Jouer |
|
</button> |
|
</div> |
|
<p class="text-xs text-gray-500 mb-3"> |
|
Alternative: cliquez sur la case de départ puis la case d'arrivée sur un échiquier interactif (non implémenté ici, utilisez l'input). |
|
</p> |
|
|
|
<h3 class="text-lg font-semibold mb-1">Derniers coups:</h3> |
|
<p id="playerMoveInfo" class="text-sm"></p> |
|
<p id="aiMoveInfo" class="text-sm"></p> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
const boardDisplay = document.getElementById('boardDisplay'); |
|
const turnInfo = document.getElementById('turnInfo'); |
|
const statusInfo = document.getElementById('statusInfo'); |
|
const moveInput = document.getElementById('moveInput'); |
|
const playerMoveInfo = document.getElementById('playerMoveInfo'); |
|
const aiMoveInfo = document.getElementById('aiMoveInfo'); |
|
|
|
let currentFen = "{{ initial_fen }}"; |
|
let selectedSquare = null; |
|
|
|
async function updateBoard(data) { |
|
if (data.error) { |
|
statusInfo.textContent = `Erreur: ${data.error}`; |
|
if (data.board_svg) boardDisplay.innerHTML = data.board_svg; |
|
return; |
|
} |
|
currentFen = data.fen; |
|
boardDisplay.innerHTML = data.board_svg; |
|
turnInfo.textContent = `Tour: ${data.turn}`; |
|
statusInfo.textContent = `Statut: ${data.status}`; |
|
moveInput.value = ''; |
|
|
|
playerMoveInfo.textContent = data.player_move_san ? `Votre coup: ${data.player_move_san}` : ""; |
|
aiMoveInfo.textContent = data.ai_move_san ? `Coup IA: ${data.ai_move_san}` : ""; |
|
|
|
if (data.game_over) { |
|
moveInput.disabled = true; |
|
document.getElementById('submitMove').disabled = true; |
|
|
|
setTimeout(() => alert(`Partie terminée ! ${data.status}`), 100); |
|
} else { |
|
moveInput.disabled = false; |
|
document.getElementById('submitMove').disabled = false; |
|
} |
|
} |
|
|
|
async function submitMove() { |
|
const move = moveInput.value.trim().toLowerCase(); |
|
if (!move.match(/^[a-h][1-8][a-h][1-8]([qrbn])?$/)) { |
|
statusInfo.textContent = "Format de coup invalide (ex: e2e4, e7e8q)."; |
|
return; |
|
} |
|
|
|
playerMoveInfo.textContent = ""; |
|
aiMoveInfo.textContent = ""; |
|
|
|
try { |
|
const response = await fetch('/move', { |
|
method: 'POST', |
|
headers: { 'Content-Type': 'application/json' }, |
|
body: JSON.stringify({ move: move }) |
|
}); |
|
const data = await response.json(); |
|
updateBoard(data); |
|
} catch (error) { |
|
console.error('Erreur lors de la soumission du coup:', error); |
|
statusInfo.textContent = 'Erreur de communication avec le serveur.'; |
|
} |
|
} |
|
|
|
async function startNewGame(mode, playerPlaysWhite = true) { |
|
playerMoveInfo.textContent = ""; |
|
aiMoveInfo.textContent = ""; |
|
try { |
|
const response = await fetch('/new_game', { |
|
method: 'POST', |
|
headers: { 'Content-Type': 'application/json' }, |
|
body: JSON.stringify({ mode: mode, player_color_white: playerPlaysWhite}) |
|
}); |
|
const data = await response.json(); |
|
updateBoard(data); |
|
statusInfo.textContent = `Nouvelle partie commencée. ${mode === 'ai' ? (playerPlaysWhite ? 'Vous jouez les Blancs.' : 'L\'IA joue les Blancs.') : 'Humain vs Humain.'}`; |
|
} catch (error) { |
|
console.error('Erreur lors du démarrage de la nouvelle partie:', error); |
|
statusInfo.textContent = 'Erreur de communication avec le serveur.'; |
|
} |
|
} |
|
|
|
document.getElementById('submitMove').addEventListener('click', submitMove); |
|
moveInput.addEventListener('keypress', function(event) { |
|
if (event.key === 'Enter') { |
|
submitMove(); |
|
} |
|
}); |
|
|
|
document.getElementById('newGameHuman').addEventListener('click', () => startNewGame('human')); |
|
document.getElementById('newGameAIWhite').addEventListener('click', () => startNewGame('ai', true)); |
|
document.getElementById('newGameAIBlack').addEventListener('click', () => startNewGame('ai', false)); |
|
|
|
|
|
async function initializeBoardState() { |
|
if (boardDisplay.innerHTML.includes("Chargement")) { |
|
try { |
|
const response = await fetch('/get_board_state'); |
|
const data = await response.json(); |
|
updateBoard(data); |
|
} catch (error) { |
|
console.error('Erreur de récupération de l\'état initial:', error); |
|
statusInfo.textContent = 'Erreur de chargement de la partie.'; |
|
} |
|
} else { |
|
try { |
|
const response = await fetch('/get_board_state'); |
|
const data = await response.json(); |
|
turnInfo.textContent = `Tour: ${data.turn}`; |
|
statusInfo.textContent = `Statut: ${data.status}`; |
|
if (data.game_over) { |
|
moveInput.disabled = true; |
|
document.getElementById('submitMove').disabled = true; |
|
} |
|
} catch (error) { |
|
console.error('Erreur de récupération de l\'état initial (infos):', error); |
|
} |
|
} |
|
} |
|
|
|
window.onload = initializeBoardState; |
|
|
|
</script> |
|
</body> |
|
</html> |