Docfile commited on
Commit
8a91eec
·
verified ·
1 Parent(s): 1a3554c

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +446 -265
templates/index.html CHANGED
@@ -4,324 +4,505 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Jeu d'Échecs</title>
7
- <!-- Utilisation du CDN de Tailwind CSS pour la simplicité -->
8
- <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
9
  <style>
10
- /* Styles additionnels si besoin */
11
- .piece-font { /* Optionnel: si vous voulez une police spécifique pour les pièces unicode */
12
- font-family: 'Arial Unicode MS', 'DejaVu Sans', 'Symbola', sans-serif;
13
- }
14
- .selected-square {
15
- background-color: #48bb78 !important; /* Vert pour la sélection */
16
- outline: 2px solid #2f855a;
17
- }
18
- .possible-move {
19
- background-color: rgba(72, 187, 120, 0.3) !important; /* Vert clair pour les coups possibles */
20
- }
21
- .last-move-highlight {
22
- background-color: rgba(251, 191, 36, 0.4) !important; /* Jaune pour le dernier coup */
23
- }
24
- #chessboard {
25
  display: grid;
26
- grid-template-columns: repeat(8, minmax(0, 1fr));
27
- grid-template-rows: repeat(8, minmax(0, 1fr));
28
- width: 400px; /* Ajustez selon vos besoins */
29
- height: 400px; /* Ajustez selon vos besoins */
30
- border: 2px solid #4A5568; /* gray-700 */
31
  }
32
- .square {
 
33
  display: flex;
34
  align-items: center;
35
  justify-content: center;
36
- font-size: 2.25rem; /* text-4xl */
37
  cursor: pointer;
38
- user-select: none; /* Empêcher la sélection de texte des pièces */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  }
40
- .light-square {
41
- background-color: #F7FAFC; /* gray-100 */
 
42
  }
43
- .dark-square {
44
- background-color: #A0AEC0; /* gray-500 */
 
 
45
  }
46
  </style>
47
  </head>
48
- <body class="bg-gray-200 flex flex-col items-center justify-center min-h-screen p-4">
49
-
50
- <div class="bg-white p-6 rounded-lg shadow-xl w-full max-w-lg">
51
- <h1 class="text-3xl font-bold text-center text-gray-800 mb-6">Jeu d'Échecs</h1>
52
-
53
- <div class="mb-4 flex justify-around">
54
- <button id="new-game-human" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
55
- Humain vs Humain
56
- </button>
57
- <button id="new-game-ai" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
58
- Jouer vs IA
59
- </button>
60
- </div>
61
-
62
- <div class="flex flex-col items-center">
63
- <div id="chessboard" class="mb-4">
64
- <!-- Les cases de l'échiquier seront générées ici par JavaScript -->
65
  </div>
66
-
67
- <div id="turn-indicator" class="text-xl font-semibold text-gray-700 mb-2">
68
- Tour: Blancs
69
- </div>
70
- <div id="status-message" class="text-md text-gray-600 mb-4 h-6">
71
- <!-- Les messages de statut apparaîtront ici -->
 
 
 
 
 
 
 
 
 
 
 
72
  </div>
73
- <div id="evaluation-indicator" class="text-sm text-gray-500 mb-4 h-5">
74
- <!-- L'évaluation de Stockfish (optionnel) -->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  </div>
76
  </div>
77
-
78
- <div class="mt-4 p-4 bg-gray-100 rounded max-h-48 overflow-y-auto">
79
- <h3 class="font-semibold text-gray-700 mb-2">Historique des coups:</h3>
80
- <div id="history-log" class="text-sm text-gray-600">
81
- <!-- L'historique des coups sera affiché ici -->
 
 
 
 
 
 
 
 
 
82
  </div>
83
  </div>
84
-
85
  </div>
86
 
87
  <script>
88
- const boardElement = document.getElementById('chessboard');
89
- const turnIndicatorElement = document.getElementById('turn-indicator');
90
- const statusMessageElement = document.getElementById('status-message');
91
- const evaluationIndicatorElement = document.getElementById('evaluation-indicator'); // Pour l'évaluation
92
- const newGameAiButton = document.getElementById('new-game-ai');
93
- const newGameHumanButton = document.getElementById('new-game-human');
94
- const historyLogElement = document.getElementById('history-log');
95
-
96
- let selectedSquareId = null;
97
- let currentFen = '';
98
- let currentPlayerTurn = 'w'; // 'w' for white, 'b' for black
99
- let gameMode = 'human'; // 'human' or 'ai'
100
- let lastMoveSquares = []; // Pour surligner le dernier coup [from, to]
101
-
102
- const pieceUnicode = {
103
- 'p': '♟', 'r': '♜', 'n': '♞', 'b': '♝', 'q': '♛', 'k': '♚',
104
- 'P': '♙', 'R': '♖', 'N': '♘', 'B': '♗', 'Q': '♕', 'K': '♔'
105
- };
106
- const pieceColor = {
107
- 'p': 'b', 'r': 'b', 'n': 'b', 'b': 'b', 'q': 'b', 'k': 'b',
108
- 'P': 'w', 'R': 'w', 'N': 'w', 'B': 'w', 'Q': 'w', 'K': 'w'
109
  };
110
-
111
- function getSquareId(rankIndex, fileIndex) {
112
- return `${String.fromCharCode(97 + fileIndex)}${8 - rankIndex}`;
113
- }
114
 
115
- function clearHighlights() {
116
- document.querySelectorAll('.selected-square').forEach(el => el.classList.remove('selected-square'));
117
- document.querySelectorAll('.possible-move').forEach(el => el.classList.remove('possible-move'));
118
- // Ne pas enlever last-move-highlight ici, on le gère au moment de renderBoard
119
- }
120
-
121
- function renderBoard(fen, lastHumanMove = null, lastAiMove = null) {
122
- currentFen = fen;
123
- boardElement.innerHTML = ''; // Clear previous board
124
- const fenParts = fen.split(' ');
125
- const boardState = fenParts[0];
126
- currentPlayerTurn = fenParts[1]; // 'w' or 'b'
127
 
128
- // Gestion du surlignage du dernier coup
129
- document.querySelectorAll('.last-move-highlight').forEach(el => el.classList.remove('last-move-highlight'));
130
- lastMoveSquares = [];
131
- if (lastAiMove) { // Si l'IA a joué, son coup est le plus récent
132
- lastMoveSquares = [lastAiMove.substring(0,2), lastAiMove.substring(2,4)];
133
- } else if (lastHumanMove) { // Sinon, c'est le coup humain
134
- lastMoveSquares = [lastHumanMove.substring(0,2), lastHumanMove.substring(2,4)];
135
- }
136
-
137
-
138
- const ranks = boardState.split('/');
139
- for (let rankIndex = 0; rankIndex < 8; rankIndex++) {
140
- const rank = ranks[rankIndex];
141
- let fileIndex = 0;
142
- for (const char of rank) {
143
- if (isNaN(parseInt(char))) { // It's a piece
144
- const squareId = getSquareId(rankIndex, fileIndex);
145
- const squareElement = createSquareElement(squareId, rankIndex, fileIndex, pieceUnicode[char]);
146
- boardElement.appendChild(squareElement);
147
- fileIndex++;
148
- } else { // It's a number for empty squares
149
- for (let i = 0; i < parseInt(char); i++) {
150
- const squareId = getSquareId(rankIndex, fileIndex);
151
- const squareElement = createSquareElement(squareId, rankIndex, fileIndex, '');
152
- boardElement.appendChild(squareElement);
153
- fileIndex++;
154
- }
155
- }
156
  }
157
  }
158
- updateTurnIndicator();
159
  }
160
 
161
- function createSquareElement(id, rankIndex, fileIndex, pieceSymbol) {
162
- const squareElement = document.createElement('div');
163
- squareElement.id = id;
164
- squareElement.classList.add('square', 'piece-font');
165
- squareElement.classList.add((rankIndex + fileIndex) % 2 === 0 ? 'light-square' : 'dark-square');
166
- squareElement.textContent = pieceSymbol;
167
- squareElement.dataset.piece = pieceSymbol; // Stocker la pièce pour une vérification facile
168
 
169
- if (lastMoveSquares.includes(id)) {
170
- squareElement.classList.add('last-move-highlight');
 
 
 
 
171
  }
172
-
173
- squareElement.addEventListener('click', handleSquareClick);
174
- return squareElement;
 
 
 
 
 
 
175
  }
176
-
177
-
178
- function updateTurnIndicator() {
179
- turnIndicatorElement.textContent = `Tour: ${currentPlayerTurn === 'w' ? 'Blancs' : 'Noirs'}`;
 
 
 
 
 
 
 
180
  }
181
 
182
- function updateHistoryLog(historyArray) {
183
- historyLogElement.innerHTML = '';
184
- if (!historyArray || historyArray.length === 0) {
185
- historyLogElement.textContent = "Aucun coup joué.";
186
- return;
187
- }
188
- let moveNumber = 1;
189
- for (let i = 0; i < historyArray.length; i += 2) {
190
- const whiteMove = historyArray[i];
191
- const blackMove = historyArray[i+1] ? historyArray[i+1] : '';
192
- const logEntry = document.createElement('div');
193
- logEntry.textContent = `${moveNumber}. ${whiteMove} ${blackMove}`;
194
- historyLogElement.appendChild(logEntry);
195
- moveNumber++;
196
- }
197
- historyLogElement.scrollTop = historyLogElement.scrollHeight; // Scroll to bottom
198
  }
199
-
200
- async function handleSquareClick(event) {
201
- const clickedSquareElement = event.currentTarget; // L'élément div de la case
202
- const clickedSquareId = clickedSquareElement.id;
203
-
204
- // Si la partie est terminée, ne rien faire
205
- if (statusMessageElement.textContent.includes("Mat!") || statusMessageElement.textContent.includes("Pat!")) {
206
- return;
 
 
 
 
 
 
 
 
 
207
  }
208
-
209
- const pieceSymbol = clickedSquareElement.dataset.piece;
210
- const isPieceOnSquare = pieceSymbol && pieceSymbol.trim() !== '';
211
-
212
- if (!selectedSquareId) { // Premier clic: sélection d'une pièce
213
- if (isPieceOnSquare) {
214
- // Vérifier si c'est la pièce du joueur actuel
215
- const colorOfPiece = pieceColor[pieceSymbol];
216
- if (colorOfPiece === currentPlayerTurn) {
217
- selectedSquareId = clickedSquareId;
218
- clickedSquareElement.classList.add('selected-square');
219
- // TODO: Optionnel - appeler le backend pour obtenir les coups légaux et les surligner
 
 
 
 
 
 
 
 
 
 
220
  }
 
 
221
  }
222
- } else { // Deuxième clic: tentative de déplacement
223
- const previousSelectedElement = document.getElementById(selectedSquareId);
224
- previousSelectedElement?.classList.remove('selected-square');
225
-
226
- if (selectedSquareId === clickedSquareId) { // Clic sur la même case pour désélectionner
227
- selectedSquareId = null;
228
- return;
229
- }
230
-
231
- const moveUci = `${selectedSquareId}${clickedSquareId}`;
232
- // Vérification si c'est une promotion de pion (simplifié, suppose promotion en reine)
233
- const fromSquare = document.getElementById(selectedSquareId);
234
- const fromPiece = fromSquare?.dataset.piece;
235
- const toRank = clickedSquareId.charAt(1);
236
- let finalMoveUci = moveUci;
237
-
238
- if (fromPiece === 'P' && toRank === '8') {
239
- finalMoveUci += 'q'; // Promotion en Reine pour les blancs
240
- } else if (fromPiece === 'p' && toRank === '1') {
241
- finalMoveUci += 'q'; // Promotion en Reine pour les noirs
 
 
242
  }
 
 
 
 
 
 
 
 
 
 
243
 
244
- selectedSquareId = null; // Réinitialiser la sélection après la tentative de coup
245
-
246
- statusMessageElement.textContent = 'Traitement du coup...';
247
- try {
248
- const response = await fetch('/move', {
249
- method: 'POST',
250
- headers: { 'Content-Type': 'application/json' },
251
- body: JSON.stringify({ move: finalMoveUci })
252
- });
253
- const data = await response.json();
254
-
255
- if (data.error) {
256
- statusMessageElement.textContent = `Erreur: ${data.error}`;
257
- // Re-render old fen and turn if move was illegal
258
- if(data.fen) renderBoard(data.fen, data.last_move_human, data.last_move_ai); // S'assurer que le dernier coup est passé
259
- if(data.turn) currentPlayerTurn = data.turn;
260
- updateTurnIndicator();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  } else {
262
- renderBoard(data.fen, data.last_move_human, data.last_move_ai);
263
- currentPlayerTurn = data.turn; // Le backend doit retourner le tour actuel
264
- statusMessageElement.textContent = data.game_status || (data.last_move_ai ? `L'IA a joué: ${data.last_move_ai}` : "Coup joué.");
265
- updateHistoryLog(data.history);
266
- if (data.evaluation) { // Afficher l'évaluation si fournie
267
- evaluationIndicatorElement.textContent = `Éval: ${data.evaluation.type} ${data.evaluation.value}`;
268
- } else {
269
- evaluationIndicatorElement.textContent = '';
270
- }
271
-
272
- if (data.game_status && (data.game_status.includes("Mat!") || data.game_status.includes("Pat!"))) {
273
- // La partie est terminée, on pourrait désactiver les clics ou afficher un message plus grand
274
- turnIndicatorElement.textContent = "Partie terminée";
275
- } else if (gameMode === 'ai' && currentPlayerTurn !== 'w') { // Si c'est le tour de l'IA (assumant que l'IA joue noir pour l'instant)
276
- // Le backend gère déjà le coup de l'IA dans la même réponse /move
277
- }
278
  }
279
- } catch (error) {
280
- console.error('Erreur lors de l\'envoi du coup:', error);
281
- statusMessageElement.textContent = 'Erreur de communication avec le serveur.';
282
  }
283
  }
 
 
 
 
 
 
 
 
284
  }
285
-
286
- async function startNewGame(mode) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  gameMode = mode;
288
- selectedSquareId = null;
289
- statusMessageElement.textContent = `Démarrage d'une nouvelle partie (${mode === 'ai' ? 'vs IA' : 'Humain vs Humain'})...`;
290
- evaluationIndicatorElement.textContent = '';
291
- historyLogElement.innerHTML = "Aucun coup joué.";
292
- lastMoveSquares = []; // Réinitialiser le surlignage du dernier coup
293
-
 
 
 
294
  try {
295
- const response = await fetch('/new_game', {
296
  method: 'POST',
297
- headers: { 'Content-Type': 'application/json' },
298
- body: JSON.stringify({ mode: gameMode })
 
299
  });
300
- const data = await response.json();
301
-
302
- if (data.error) {
303
- statusMessageElement.textContent = `Erreur: ${data.error}`;
304
- } else {
305
- renderBoard(data.fen);
306
- currentPlayerTurn = data.turn;
307
- statusMessageElement.textContent = data.message || 'Partie commencée. Aux Blancs de jouer.';
308
- updateTurnIndicator();
309
- updateHistoryLog([]); // Réinitialise l'historique
310
  }
311
  } catch (error) {
312
- console.error('Erreur lors du démarrage de la nouvelle partie:', error);
313
- statusMessageElement.textContent = 'Erreur de communication avec le serveur.';
314
  }
315
  }
316
-
317
- newGameAiButton.addEventListener('click', () => startNewGame('ai'));
318
- newGameHumanButton.addEventListener('click', () => startNewGame('human'));
319
-
320
- // Initialisation de la première partie au chargement de la page
321
- document.addEventListener('DOMContentLoaded', () => {
322
- startNewGame('human'); // Ou 'ai' si vous préférez
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
  });
324
-
325
  </script>
326
  </body>
327
  </html>
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Jeu d'Échecs</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
 
8
  <style>
9
+ .chess-board {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  display: grid;
11
+ grid-template-columns: repeat(8, 1fr);
12
+ grid-template-rows: repeat(8, 1fr);
13
+ aspect-ratio: 1;
14
+ max-width: 600px;
15
+ border: 4px solid #8b5a2b;
16
  }
17
+
18
+ .chess-square {
19
  display: flex;
20
  align-items: center;
21
  justify-content: center;
22
+ font-size: 2rem;
23
  cursor: pointer;
24
+ transition: all 0.2s;
25
+ position: relative;
26
+ }
27
+
28
+ .chess-square.light {
29
+ background-color: #f0d9b5;
30
+ }
31
+
32
+ .chess-square.dark {
33
+ background-color: #b58863;
34
+ }
35
+
36
+ .chess-square.selected {
37
+ background-color: #7fb069 !important;
38
+ box-shadow: inset 0 0 0 3px #2d5a27;
39
+ }
40
+
41
+ .chess-square.legal-move {
42
+ background-color: #90EE90 !important;
43
+ }
44
+
45
+ .chess-square.legal-move::after {
46
+ content: '';
47
+ position: absolute;
48
+ width: 20px;
49
+ height: 20px;
50
+ border-radius: 50%;
51
+ background-color: rgba(0, 128, 0, 0.5);
52
+ }
53
+
54
+ .chess-square.check {
55
+ background-color: #ff6b6b !important;
56
+ }
57
+
58
+ .chess-piece {
59
+ user-select: none;
60
+ pointer-events: none;
61
+ }
62
+
63
+ .status-indicator {
64
+ transition: all 0.3s ease;
65
  }
66
+
67
+ .pulse {
68
+ animation: pulse 2s infinite;
69
  }
70
+
71
+ @keyframes pulse {
72
+ 0%, 100% { opacity: 1; }
73
+ 50% { opacity: 0.5; }
74
  }
75
  </style>
76
  </head>
77
+ <body class="bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 min-h-screen">
78
+ <!-- Header -->
79
+ <div class="container mx-auto px-4 py-6">
80
+ <div class="flex justify-between items-center mb-6">
81
+ <h1 class="text-4xl font-bold text-white">♔ Jeu d'Échecs</h1>
82
+ <div class="space-x-4">
83
+ <button onclick="startNewGame('human')" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-lg transition">
84
+ Humain vs Humain
85
+ </button>
86
+ <button onclick="startNewGame('ai')" class="bg-green-600 hover:bg-green-700 text-white px-6 py-2 rounded-lg transition">
87
+ Humain vs IA
88
+ </button>
 
 
 
 
 
89
  </div>
90
+ </div>
91
+
92
+ <!-- Game Info -->
93
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
94
+ <!-- Chess Board -->
95
+ <div class="lg:col-span-2 flex justify-center">
96
+ <div class="relative">
97
+ <div id="chess-board" class="chess-board mx-auto shadow-2xl"></div>
98
+
99
+ <!-- Coordinates -->
100
+ <div class="absolute -left-6 top-0 h-full flex flex-col justify-around text-white font-bold">
101
+ <div>8</div><div>7</div><div>6</div><div>5</div><div>4</div><div>3</div><div>2</div><div>1</div>
102
+ </div>
103
+ <div class="absolute -bottom-6 left-0 w-full flex justify-around text-white font-bold">
104
+ <div>a</div><div>b</div><div>c</div><div>d</div><div>e</div><div>f</div><div>g</div><div>h</div>
105
+ </div>
106
+ </div>
107
  </div>
108
+
109
+ <!-- Game Panel -->
110
+ <div class="bg-slate-800 rounded-lg p-6 shadow-xl">
111
+ <!-- Status -->
112
+ <div class="mb-6">
113
+ <h3 class="text-xl font-bold text-white mb-2">État du Jeu</h3>
114
+ <div id="game-status" class="status-indicator">
115
+ <div class="bg-slate-700 rounded-lg p-4">
116
+ <div class="flex items-center space-x-2">
117
+ <div id="current-player-indicator" class="w-4 h-4 rounded-full bg-white"></div>
118
+ <span id="current-player-text" class="text-white font-medium">Tour des Blancs</span>
119
+ </div>
120
+ <div id="check-indicator" class="mt-2 text-red-400 font-medium hidden">
121
+ ⚠️ Échec !
122
+ </div>
123
+ </div>
124
+ </div>
125
+ </div>
126
+
127
+ <!-- Game Mode -->
128
+ <div class="mb-6">
129
+ <h3 class="text-xl font-bold text-white mb-2">Mode de Jeu</h3>
130
+ <div id="game-mode" class="bg-slate-700 rounded-lg p-3 text-white">
131
+ Sélectionnez un mode
132
+ </div>
133
+ </div>
134
+
135
+ <!-- Moves History -->
136
+ <div class="mb-6">
137
+ <h3 class="text-xl font-bold text-white mb-2">Historique</h3>
138
+ <div id="moves-history" class="bg-slate-700 rounded-lg p-3 max-h-48 overflow-y-auto">
139
+ <div class="text-slate-400 text-sm">Aucun coup joué</div>
140
+ </div>
141
+ </div>
142
+
143
+ <!-- Controls -->
144
+ <div class="space-y-3">
145
+ <button onclick="resetGame()" class="w-full bg-red-600 hover:bg-red-700 text-white py-2 px-4 rounded-lg transition">
146
+ Nouvelle Partie
147
+ </button>
148
+ <button onclick="undoMove()" class="w-full bg-yellow-600 hover:bg-yellow-700 text-white py-2 px-4 rounded-lg transition" disabled>
149
+ Annuler le Coup
150
+ </button>
151
+ </div>
152
  </div>
153
  </div>
154
+ </div>
155
+
156
+ <!-- Game Over Modal -->
157
+ <div id="game-over-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
158
+ <div class="bg-slate-800 rounded-lg p-8 max-w-md mx-4 text-center">
159
+ <h2 id="game-over-title" class="text-3xl font-bold text-white mb-4"></h2>
160
+ <p id="game-over-message" class="text-slate-300 mb-6"></p>
161
+ <div class="space-x-4">
162
+ <button onclick="closeGameOverModal()" class="bg-gray-600 hover:bg-gray-700 text-white px-6 py-2 rounded-lg transition">
163
+ Fermer
164
+ </button>
165
+ <button onclick="resetGame(); closeGameOverModal()" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-lg transition">
166
+ Nouvelle Partie
167
+ </button>
168
  </div>
169
  </div>
 
170
  </div>
171
 
172
  <script>
173
+ // Variables globales
174
+ let gameMode = null;
175
+ let selectedSquare = null;
176
+ let currentPlayer = 'white';
177
+ let gameActive = true;
178
+ let boardState = null;
179
+
180
+ // Symboles des pièces Unicode
181
+ const pieceSymbols = {
182
+ 'K': '♔', 'Q': '', 'R': '♖', 'B': '♗', 'N': '♘', 'P': '♙',
183
+ 'k': '♚', 'q': '', 'r': '', 'b': '♝', 'n': '♞', 'p': '♟'
 
 
 
 
 
 
 
 
 
 
184
  };
 
 
 
 
185
 
186
+ // Initialisation
187
+ function initializeBoard() {
188
+ const board = document.getElementById('chess-board');
189
+ board.innerHTML = '';
 
 
 
 
 
 
 
 
190
 
191
+ for (let row = 0; row < 8; row++) {
192
+ for (let col = 0; col < 8; col++) {
193
+ const square = document.createElement('div');
194
+ const isLight = (row + col) % 2 === 0;
195
+ square.className = `chess-square ${isLight ? 'light' : 'dark'}`;
196
+ square.dataset.square = String.fromCharCode(97 + col) + (8 - row);
197
+ square.addEventListener('click', handleSquareClick);
198
+ board.appendChild(square);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  }
200
  }
 
201
  }
202
 
203
+ // Gestion des clics sur les cases
204
+ function handleSquareClick(event) {
205
+ if (!gameActive) return;
 
 
 
 
206
 
207
+ const square = event.target.dataset.square;
208
+
209
+ if (selectedSquare === square) {
210
+ // Désélectionner
211
+ clearSelection();
212
+ return;
213
  }
214
+
215
+ if (selectedSquare && event.target.classList.contains('legal-move')) {
216
+ // Effectuer le coup
217
+ makeMove(selectedSquare + square);
218
+ return;
219
+ }
220
+
221
+ // Sélectionner une nouvelle case
222
+ selectSquare(square);
223
  }
224
+
225
+ // Sélection d'une case
226
+ function selectSquare(square) {
227
+ clearSelection();
228
+ selectedSquare = square;
229
+
230
+ const squareElement = document.querySelector(`[data-square="${square}"]`);
231
+ squareElement.classList.add('selected');
232
+
233
+ // Afficher les coups légaux
234
+ showLegalMoves(square);
235
  }
236
 
237
+ // Effacer la sélection
238
+ function clearSelection() {
239
+ selectedSquare = null;
240
+ document.querySelectorAll('.chess-square').forEach(sq => {
241
+ sq.classList.remove('selected', 'legal-move');
242
+ });
 
 
 
 
 
 
 
 
 
 
243
  }
244
+
245
+ // Afficher les coups légaux
246
+ async function showLegalMoves(square) {
247
+ try {
248
+ const response = await fetch(`/api/game/legal-moves?square=${square}`);
249
+ const data = await response.json();
250
+
251
+ if (data.legal_moves) {
252
+ data.legal_moves.forEach(targetSquare => {
253
+ const element = document.querySelector(`[data-square="${targetSquare}"]`);
254
+ if (element) {
255
+ element.classList.add('legal-move');
256
+ }
257
+ });
258
+ }
259
+ } catch (error) {
260
+ console.error('Erreur lors de la récupération des coups légaux:', error);
261
  }
262
+ }
263
+
264
+ // Effectuer un coup
265
+ async function makeMove(move) {
266
+ try {
267
+ const response = await fetch('/api/game/move', {
268
+ method: 'POST',
269
+ headers: {
270
+ 'Content-Type': 'application/json',
271
+ },
272
+ body: JSON.stringify({ move: move })
273
+ });
274
+
275
+ const result = await response.json();
276
+
277
+ if (result.success) {
278
+ clearSelection();
279
+ await updateGameState();
280
+
281
+ // Si mode IA et c'est le tour de l'IA
282
+ if (gameMode === 'ai' && result.current_player === 'black' && result.game_status === 'active') {
283
+ setTimeout(makeAIMove, 500);
284
  }
285
+ } else {
286
+ alert('Coup invalide: ' + result.error);
287
  }
288
+ } catch (error) {
289
+ console.error('Erreur lors du coup:', error);
290
+ alert('Erreur lors du coup');
291
+ }
292
+ }
293
+
294
+ // Coup de l'IA
295
+ async function makeAIMove() {
296
+ try {
297
+ const response = await fetch('/api/game/ai-move', {
298
+ method: 'POST',
299
+ headers: {
300
+ 'Content-Type': 'application/json',
301
+ }
302
+ });
303
+
304
+ const result = await response.json();
305
+
306
+ if (result.success) {
307
+ await updateGameState();
308
+ } else {
309
+ console.error('Erreur IA:', result.error);
310
  }
311
+ } catch (error) {
312
+ console.error('Erreur lors du coup de l\'IA:', error);
313
+ }
314
+ }
315
+
316
+ // Mettre à jour l'état du jeu
317
+ async function updateGameState() {
318
+ try {
319
+ const response = await fetch('/api/game/state');
320
+ const state = await response.json();
321
 
322
+ boardState = state;
323
+ currentPlayer = state.current_player;
324
+ gameActive = state.game_status === 'active';
325
+
326
+ updateBoard(state.fen);
327
+ updateUI(state);
328
+
329
+ if (!gameActive) {
330
+ showGameOverModal(state.game_status);
331
+ }
332
+ } catch (error) {
333
+ console.error('Erreur lors de la mise à jour:', error);
334
+ }
335
+ }
336
+
337
+ // Mettre à jour le plateau
338
+ function updateBoard(fen) {
339
+ const position = fen.split(' ')[0];
340
+ const rows = position.split('/');
341
+
342
+ document.querySelectorAll('.chess-square').forEach(square => {
343
+ square.textContent = '';
344
+ square.classList.remove('check');
345
+ });
346
+
347
+ let squareIndex = 0;
348
+ for (let row = 0; row < 8; row++) {
349
+ let col = 0;
350
+ for (let char of rows[row]) {
351
+ if (isNaN(char)) {
352
+ const square = document.querySelectorAll('.chess-square')[squareIndex];
353
+ square.textContent = pieceSymbols[char] || '';
354
+ squareIndex++;
355
+ col++;
356
  } else {
357
+ squareIndex += parseInt(char);
358
+ col += parseInt(char);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
  }
 
 
 
360
  }
361
  }
362
+
363
+ // Marquer l'échec
364
+ if (boardState && boardState.is_check) {
365
+ // Trouver le roi en échec et le marquer
366
+ document.getElementById('check-indicator').classList.remove('hidden');
367
+ } else {
368
+ document.getElementById('check-indicator').classList.add('hidden');
369
+ }
370
  }
371
+
372
+ // Mettre à jour l'interface
373
+ function updateUI(state) {
374
+ // Joueur actuel
375
+ const indicator = document.getElementById('current-player-indicator');
376
+ const text = document.getElementById('current-player-text');
377
+
378
+ if (state.current_player === 'white') {
379
+ indicator.className = 'w-4 h-4 rounded-full bg-white';
380
+ text.textContent = 'Tour des Blancs';
381
+ } else {
382
+ indicator.className = 'w-4 h-4 rounded-full bg-gray-800 border-2 border-white';
383
+ text.textContent = 'Tour des Noirs';
384
+ }
385
+
386
+ // Historique des coups
387
+ updateMovesHistory(state.moves_history);
388
+ }
389
+
390
+ // Mettre à jour l'historique
391
+ function updateMovesHistory(moves) {
392
+ const historyDiv = document.getElementById('moves-history');
393
+
394
+ if (moves.length === 0) {
395
+ historyDiv.innerHTML = '<div class="text-slate-400 text-sm">Aucun coup joué</div>';
396
+ return;
397
+ }
398
+
399
+ let html = '<div class="space-y-1 text-sm">';
400
+ for (let i = 0; i < moves.length; i += 2) {
401
+ const moveNumber = Math.floor(i / 2) + 1;
402
+ const whiteMove = moves[i] || '';
403
+ const blackMove = moves[i + 1] || '';
404
+ html += `<div class="text-white">
405
+ <span class="text-slate-400">${moveNumber}.</span>
406
+ ${whiteMove} ${blackMove}
407
+ </div>`;
408
+ }
409
+ html += '</div>';
410
+
411
+ historyDiv.innerHTML = html;
412
+ historyDiv.scrollTop = historyDiv.scrollHeight;
413
+ }
414
+
415
+ // Commencer une nouvelle partie
416
+ function startNewGame(mode) {
417
  gameMode = mode;
418
+ const modeText = mode === 'ai' ? 'Humain vs IA' : 'Humain vs Humain';
419
+ document.getElementById('game-mode').textContent = modeText;
420
+
421
+ // Rediriger vers la nouvelle partie
422
+ window.location.href = `/game/${mode}`;
423
+ }
424
+
425
+ // Réinitialiser la partie
426
+ async function resetGame() {
427
  try {
428
+ const response = await fetch('/api/game/reset', {
429
  method: 'POST',
430
+ headers: {
431
+ 'Content-Type': 'application/json',
432
+ }
433
  });
434
+
435
+ if (response.ok) {
436
+ clearSelection();
437
+ await updateGameState();
 
 
 
 
 
 
438
  }
439
  } catch (error) {
440
+ console.error('Erreur lors de la réinitialisation:', error);
 
441
  }
442
  }
443
+
444
+ // Modal de fin de partie
445
+ function showGameOverModal(gameStatus) {
446
+ const modal = document.getElementById('game-over-modal');
447
+ const title = document.getElementById('game-over-title');
448
+ const message = document.getElementById('game-over-message');
449
+
450
+ let titleText = '';
451
+ let messageText = '';
452
+
453
+ switch (gameStatus) {
454
+ case 'checkmate_white':
455
+ titleText = '♔ Victoire des Blancs !';
456
+ messageText = 'Échec et mat ! Les blancs ont gagné.';
457
+ break;
458
+ case 'checkmate_black':
459
+ titleText = '♚ Victoire des Noirs !';
460
+ messageText = 'Échec et mat ! Les noirs ont gagné.';
461
+ break;
462
+ case 'stalemate':
463
+ titleText = 'Match Nul';
464
+ messageText = 'Pat ! La partie se termine par un match nul.';
465
+ break;
466
+ case 'draw_material':
467
+ titleText = 'Match Nul';
468
+ messageText = 'Match nul par manque de matériel.';
469
+ break;
470
+ default:
471
+ titleText = 'Partie Terminée';
472
+ messageText = 'La partie est terminée.';
473
+ }
474
+
475
+ title.textContent = titleText;
476
+ message.textContent = messageText;
477
+ modal.classList.remove('hidden');
478
+ modal.classList.add('flex');
479
+ }
480
+
481
+ function closeGameOverModal() {
482
+ const modal = document.getElementById('game-over-modal');
483
+ modal.classList.add('hidden');
484
+ modal.classList.remove('flex');
485
+ }
486
+
487
+ function undoMove() {
488
+ // TODO: Implémenter l'annulation de coup
489
+ alert('Fonction à implémenter');
490
+ }
491
+
492
+ // Initialisation au chargement
493
+ document.addEventListener('DOMContentLoaded', function() {
494
+ initializeBoard();
495
+
496
+ // Si on est sur une page de jeu, charger l'état
497
+ if (window.location.pathname.includes('/game/')) {
498
+ const mode = window.location.pathname.split('/').pop();
499
+ gameMode = mode;
500
+ const modeText = mode === 'ai' ? 'Humain vs IA' : 'Humain vs Humain';
501
+ document.getElementById('game-mode').textContent = modeText;
502
+
503
+ updateGameState();
504
+ }
505
  });
 
506
  </script>
507
  </body>
508
  </html>