darkmaria / templates /index.html
Docfile's picture
Update templates/index.html
34d1909 verified
raw
history blame
17.3 kB
<!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 {
display: grid;
grid-template-columns: repeat(8, 1fr);
grid-template-rows: repeat(8, 1fr);
aspect-ratio: 1;
max-width: 600px;
margin: 0 auto;
border: 4px solid #8B4513;
}
.chess-square {
display: flex;
align-items: center;
justify-content: center;
font-size: 2.5rem;
cursor: pointer;
transition: all 0.2s;
user-select: none;
}
.chess-square.light {
background-color: #F0D9B5;
}
.chess-square.dark {
background-color: #B58863;
}
.chess-square.selected {
background-color: #7fc97f !important;
box-shadow: inset 0 0 0 3px #4CAF50;
}
.chess-square.legal-move {
background-color: #90EE90 !important;
}
.chess-square:hover {
opacity: 0.8;
}
.piece-white { color: white; text-shadow: 1px 1px 1px black; }
.piece-black { color: black; text-shadow: 1px 1px 1px white; }
.status-bar {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<div class="text-center mb-8">
<h1 class="text-4xl font-bold text-gray-800 mb-4">♔ Jeu d'Échecs ♛</h1>
<div class="status-bar text-white px-6 py-4 rounded-lg shadow-lg">
<div id="game-status" class="text-xl font-semibold">
Choisissez un mode de jeu pour commencer
</div>
<div id="current-player" class="text-sm mt-1 opacity-90"></div>
</div>
</div>
<!-- Game Controls -->
<div class="flex flex-wrap justify-center gap-4 mb-8">
<button id="btn-human-mode" class="px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white rounded-lg font-semibold transition-colors shadow-lg">
🤝 Mode Humain vs Humain
</button>
<button id="btn-ai-mode" class="px-6 py-3 bg-purple-600 hover:bg-purple-700 text-white rounded-lg font-semibold transition-colors shadow-lg">
🤖 Mode vs IA
</button>
<button id="btn-reset" class="px-6 py-3 bg-green-600 hover:bg-green-700 text-white rounded-lg font-semibold transition-colors shadow-lg">
🔄 Nouvelle Partie
</button>
</div>
<!-- Chess Board -->
<div class="flex justify-center mb-8">
<div class="bg-white p-6 rounded-xl shadow-2xl">
<div id="chess-board" class="chess-board">
<!-- Le plateau sera généré par JavaScript -->
</div>
</div>
</div>
<!-- Game Info -->
<div class="grid md:grid-cols-2 gap-6 max-w-4xl mx-auto">
<!-- Moves History -->
<div class="bg-white rounded-lg shadow-lg p-6">
<h3 class="text-xl font-bold text-gray-800 mb-4">📝 Historique des coups</h3>
<div id="moves-history" class="max-h-60 overflow-y-auto text-sm">
<p class="text-gray-500 italic">Aucun coup joué</p>
</div>
</div>
<!-- Game Instructions -->
<div class="bg-white rounded-lg shadow-lg p-6">
<h3 class="text-xl font-bold text-gray-800 mb-4">ℹ️ Instructions</h3>
<ul class="text-sm text-gray-600 space-y-2">
<li><strong>Mode Humain:</strong> Deux joueurs alternent sur le même appareil</li>
<li><strong>Mode IA:</strong> Jouez contre l'intelligence artificielle</li>
<li><strong>Comment jouer:</strong> Cliquez sur une pièce puis sur la case de destination</li>
<li><strong>Cases vertes:</strong> Coups légaux possibles</li>
</ul>
<div class="mt-4 text-xs text-gray-500">
<p>🔴 Cases rouges = sélection | 🟢 Cases vertes = coups possibles</p>
</div>
</div>
</div>
</div>
<script>
class ChessGame {
constructor() {
this.gameState = null;
this.selectedSquare = null;
this.legalMoves = [];
this.initializeBoard();
this.setupEventListeners();
}
initializeBoard() {
const board = document.getElementById('chess-board');
board.innerHTML = '';
for (let row = 0; row < 8; row++) {
for (let col = 0; col < 8; col++) {
const square = document.createElement('div');
square.className = `chess-square ${(row + col) % 2 === 0 ? 'light' : 'dark'}`;
square.dataset.square = this.getSquareName(row, col);
square.addEventListener('click', (e) => this.handleSquareClick(e));
board.appendChild(square);
}
}
}
getSquareName(row, col) {
const files = 'abcdefgh';
const ranks = '87654321';
return files[col] + ranks[row];
}
getSquareFromName(squareName) {
const files = 'abcdefgh';
const ranks = '87654321';
const col = files.indexOf(squareName[0]);
const row = ranks.indexOf(squareName[1]);
return { row, col };
}
pieceToUnicode(piece) {
const pieces = {
'K': '♔', 'Q': '♕', 'R': '♖', 'B': '♗', 'N': '♘', 'P': '♙',
'k': '♚', 'q': '♛', 'r': '♜', 'b': '♝', 'n': '♞', 'p': '♟'
};
return pieces[piece] || '';
}
updateBoard() {
if (!this.gameState) return;
// Parse FEN pour obtenir la position des pièces
const fenParts = this.gameState.fen.split(' ');
const position = fenParts[0];
const rows = position.split('/');
const squares = document.querySelectorAll('.chess-square');
squares.forEach(square => {
square.textContent = '';
square.className = square.className.replace(/piece-(white|black)/, '');
});
rows.forEach((row, rowIndex) => {
let colIndex = 0;
for (let char of row) {
if (char >= '1' && char <= '8') {
colIndex += parseInt(char);
} else {
const squareName = this.getSquareName(rowIndex, colIndex);
const square = document.querySelector(`[data-square="${squareName}"]`);
if (square) {
square.textContent = this.pieceToUnicode(char);
square.classList.add(char === char.toUpperCase() ? 'piece-white' : 'piece-black');
}
colIndex++;
}
}
});
}
updateGameStatus() {
const statusEl = document.getElementById('game-status');
const playerEl = document.getElementById('current-player');
if (!this.gameState) {
statusEl.textContent = 'Choisissez un mode de jeu pour commencer';
playerEl.textContent = '';
return;
}
if (this.gameState.game_over) {
if (this.gameState.winner === 'draw') {
statusEl.textContent = '🤝 Match nul !';
} else {
const winner = this.gameState.winner === 'white' ? 'Blancs' : 'Noirs';
statusEl.textContent = `🏆 ${winner} gagnent !`;
}
playerEl.textContent = 'Partie terminée';
} else {
const currentPlayer = this.gameState.current_player === 'white' ? 'Blancs' : 'Noirs';
const modeText = this.gameState.mode === 'ai' ? 'vs IA' : 'Humain vs Humain';
statusEl.textContent = `🎮 ${modeText}`;
playerEl.textContent = `Au tour des ${currentPlayer}`;
}
}
updateMovesHistory() {
const historyEl = document.getElementById('moves-history');
if (!this.gameState || this.gameState.moves_history.length === 0) {
historyEl.innerHTML = '<p class="text-gray-500 italic">Aucun coup joué</p>';
return;
}
let historyHtml = '<div class="grid grid-cols-2 gap-2 text-sm">';
this.gameState.moves_history.forEach((move, index) => {
const moveNumber = Math.floor(index / 2) + 1;
const isWhite = index % 2 === 0;
if (isWhite) {
historyHtml += `<div class="font-semibold">${moveNumber}. ${move}</div>`;
} else {
historyHtml += `<div class="pl-4">${move}</div>`;
}
});
historyHtml += '</div>';
historyEl.innerHTML = historyHtml;
// Scroll vers le bas
historyEl.scrollTop = historyEl.scrollHeight;
}
async handleSquareClick(event) {
const square = event.target;
const squareName = square.dataset.square;
if (!this.gameState || this.gameState.game_over) return;
// Si aucune pièce n'est sélectionnée
if (!this.selectedSquare) {
if (square.textContent && this.canSelectPiece(square)) {
this.selectSquare(square);
}
return;
}
// Si on clique sur la même case
if (this.selectedSquare === square) {
this.deselectSquare();
return;
}
// Si on clique sur une autre pièce de la même couleur
if (square.textContent && this.canSelectPiece(square)) {
this.selectSquare(square);
return;
}
// Tentative de coup
const fromSquare = this.selectedSquare.dataset.square;
const toSquare = squareName;
const move = fromSquare + toSquare;
await this.makeMove(move);
}
canSelectPiece(square) {
if (!square.textContent) return false;
const isWhitePiece = square.classList.contains('piece-white');
const isCurrentPlayerWhite = this.gameState.current_player === 'white';
return isWhitePiece === isCurrentPlayerWhite;
}
selectSquare(square) {
this.deselectSquare();
this.selectedSquare = square;
square.classList.add('selected');
this.highlightLegalMoves(square.dataset.square);
}
deselectSquare() {
if (this.selectedSquare) {
this.selectedSquare.classList.remove('selected');
this.selectedSquare = null;
}
this.clearHighlights();
}
highlightLegalMoves(fromSquare) {
this.clearHighlights();
if (!this.gameState.legal_moves) return;
const movesFromSquare = this.gameState.legal_moves.filter(move =>
move.startsWith(fromSquare)
);
movesFromSquare.forEach(move => {
const toSquare = move.substring(2, 4);
const square = document.querySelector(`[data-square="${toSquare}"]`);
if (square) {
square.classList.add('legal-move');
}
});
}
clearHighlights() {
document.querySelectorAll('.legal-move').forEach(square => {
square.classList.remove('legal-move');
});
}
async makeMove(move) {
try {
const response = await fetch('/api/make_move', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ move })
});
const data = await response.json();
if (data.success) {
this.gameState = data.game_state;
this.updateDisplay();
this.deselectSquare();
// Si l'IA a joué un coup
if (data.ai_move) {
setTimeout(() => {
console.log('IA a joué:', data.ai_move);
}, 500);
}
} else {
alert('Coup invalide: ' + data.error);
}
} catch (error) {
console.error('Erreur lors du coup:', error);
alert('Erreur de connexion');
}
}
async startNewGame(mode) {
try {
const response = await fetch('/api/new_game', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ mode })
});
const data = await response.json();
if (data.success) {
this.gameState = data.game_state;
this.updateDisplay();
this.deselectSquare();
} else {
alert('Erreur lors de la création de la partie');
}
} catch (error) {
console.error('Erreur:', error);
alert('Erreur de connexion');
}
}
async resetGame() {
if (!this.gameState) return;
try {
const response = await fetch('/api/reset_game', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
});
const data = await response.json();
if (data.success) {
this.gameState = data.game_state;
this.updateDisplay();
this.deselectSquare();
}
} catch (error) {
console.error('Erreur:', error);
alert('Erreur de connexion');
}
}
updateDisplay() {
this.updateBoard();
this.updateGameStatus();
this.updateMovesHistory();
}
setupEventListeners() {
document.getElementById('btn-human-mode').addEventListener('click', () => {
this.startNewGame('human');
});
document.getElementById('btn-ai-mode').addEventListener('click', () => {
this.startNewGame('ai');
});
document.getElementById('btn-reset').addEventListener('click', () => {
if (confirm('Êtes-vous sûr de vouloir recommencer la partie ?')) {
this.resetGame();
}
});
}
}
// Initialiser le jeu quand la page est chargée
document.addEventListener('DOMContentLoaded', () => {
new ChessGame();
});
</script>
</body>
</html>