Docfile commited on
Commit
34d1909
·
verified ·
1 Parent(s): dfaf016

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +353 -419
templates/index.html CHANGED
@@ -12,496 +12,430 @@
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>
 
12
  grid-template-rows: repeat(8, 1fr);
13
  aspect-ratio: 1;
14
  max-width: 600px;
15
+ margin: 0 auto;
16
+ border: 4px solid #8B4513;
17
  }
18
 
19
  .chess-square {
20
  display: flex;
21
  align-items: center;
22
  justify-content: center;
23
+ font-size: 2.5rem;
24
  cursor: pointer;
25
  transition: all 0.2s;
26
+ user-select: none;
27
  }
28
 
29
  .chess-square.light {
30
+ background-color: #F0D9B5;
31
  }
32
 
33
  .chess-square.dark {
34
+ background-color: #B58863;
35
  }
36
 
37
  .chess-square.selected {
38
+ background-color: #7fc97f !important;
39
+ box-shadow: inset 0 0 0 3px #4CAF50;
40
  }
41
 
42
  .chess-square.legal-move {
43
  background-color: #90EE90 !important;
44
  }
45
 
46
+ .chess-square:hover {
47
+ opacity: 0.8;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }
49
 
50
+ .piece-white { color: white; text-shadow: 1px 1px 1px black; }
51
+ .piece-black { color: black; text-shadow: 1px 1px 1px white; }
 
52
 
53
+ .status-bar {
54
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 
 
 
 
 
55
  }
56
  </style>
57
  </head>
58
+ <body class="bg-gray-100 min-h-screen">
59
+ <div class="container mx-auto px-4 py-8">
60
+ <!-- Header -->
61
+ <div class="text-center mb-8">
62
+ <h1 class="text-4xl font-bold text-gray-800 mb-4">♔ Jeu d'Échecs ♛</h1>
63
+ <div class="status-bar text-white px-6 py-4 rounded-lg shadow-lg">
64
+ <div id="game-status" class="text-xl font-semibold">
65
+ Choisissez un mode de jeu pour commencer
66
+ </div>
67
+ <div id="current-player" class="text-sm mt-1 opacity-90"></div>
 
 
68
  </div>
69
  </div>
70
+
71
+ <!-- Game Controls -->
72
+ <div class="flex flex-wrap justify-center gap-4 mb-8">
73
+ <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">
74
+ 🤝 Mode Humain vs Humain
75
+ </button>
76
+ <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">
77
+ 🤖 Mode vs IA
78
+ </button>
79
+ <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">
80
+ 🔄 Nouvelle Partie
81
+ </button>
82
+ </div>
83
+
84
+ <!-- Chess Board -->
85
+ <div class="flex justify-center mb-8">
86
+ <div class="bg-white p-6 rounded-xl shadow-2xl">
87
+ <div id="chess-board" class="chess-board">
88
+ <!-- Le plateau sera généré par JavaScript -->
89
  </div>
90
  </div>
91
+ </div>
92
+
93
+ <!-- Game Info -->
94
+ <div class="grid md:grid-cols-2 gap-6 max-w-4xl mx-auto">
95
+ <!-- Moves History -->
96
+ <div class="bg-white rounded-lg shadow-lg p-6">
97
+ <h3 class="text-xl font-bold text-gray-800 mb-4">📝 Historique des coups</h3>
98
+ <div id="moves-history" class="max-h-60 overflow-y-auto text-sm">
99
+ <p class="text-gray-500 italic">Aucun coup joué</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  </div>
101
  </div>
102
+
103
+ <!-- Game Instructions -->
104
+ <div class="bg-white rounded-lg shadow-lg p-6">
105
+ <h3 class="text-xl font-bold text-gray-800 mb-4">ℹ️ Instructions</h3>
106
+ <ul class="text-sm text-gray-600 space-y-2">
107
+ <li><strong>Mode Humain:</strong> Deux joueurs alternent sur le même appareil</li>
108
+ <li><strong>Mode IA:</strong> Jouez contre l'intelligence artificielle</li>
109
+ <li><strong>Comment jouer:</strong> Cliquez sur une pièce puis sur la case de destination</li>
110
+ <li><strong>Cases vertes:</strong> Coups légaux possibles</li>
111
+ </ul>
112
+ <div class="mt-4 text-xs text-gray-500">
113
+ <p>🔴 Cases rouges = sélection | 🟢 Cases vertes = coups possibles</p>
114
+ </div>
 
 
115
  </div>
116
  </div>
117
  </div>
118
 
119
  <script>
120
+ class ChessGame {
121
+ constructor() {
122
+ this.gameState = null;
123
+ this.selectedSquare = null;
124
+ this.legalMoves = [];
125
+ this.initializeBoard();
126
+ this.setupEventListeners();
127
+ }
128
+
129
+ initializeBoard() {
130
+ const board = document.getElementById('chess-board');
131
+ board.innerHTML = '';
132
+
133
+ for (let row = 0; row < 8; row++) {
134
+ for (let col = 0; col < 8; col++) {
135
+ const square = document.createElement('div');
136
+ square.className = `chess-square ${(row + col) % 2 === 0 ? 'light' : 'dark'}`;
137
+ square.dataset.square = this.getSquareName(row, col);
138
+ square.addEventListener('click', (e) => this.handleSquareClick(e));
139
+ board.appendChild(square);
140
+ }
 
 
 
 
 
141
  }
142
  }
143
+
144
+ getSquareName(row, col) {
145
+ const files = 'abcdefgh';
146
+ const ranks = '87654321';
147
+ return files[col] + ranks[row];
148
+ }
149
+
150
+ getSquareFromName(squareName) {
151
+ const files = 'abcdefgh';
152
+ const ranks = '87654321';
153
+ const col = files.indexOf(squareName[0]);
154
+ const row = ranks.indexOf(squareName[1]);
155
+ return { row, col };
156
  }
157
+
158
+ pieceToUnicode(piece) {
159
+ const pieces = {
160
+ 'K': '♔', 'Q': '♕', 'R': '♖', 'B': '♗', 'N': '♘', 'P': '♙',
161
+ 'k': '♚', 'q': '♛', 'r': '♜', 'b': '♝', 'n': '♞', 'p': '♟'
162
+ };
163
+ return pieces[piece] || '';
164
  }
165
+
166
+ updateBoard() {
167
+ if (!this.gameState) return;
168
+
169
+ // Parse FEN pour obtenir la position des pièces
170
+ const fenParts = this.gameState.fen.split(' ');
171
+ const position = fenParts[0];
172
+ const rows = position.split('/');
173
+
174
+ const squares = document.querySelectorAll('.chess-square');
175
+ squares.forEach(square => {
176
+ square.textContent = '';
177
+ square.className = square.className.replace(/piece-(white|black)/, '');
178
+ });
179
+
180
+ rows.forEach((row, rowIndex) => {
181
+ let colIndex = 0;
182
+ for (let char of row) {
183
+ if (char >= '1' && char <= '8') {
184
+ colIndex += parseInt(char);
185
+ } else {
186
+ const squareName = this.getSquareName(rowIndex, colIndex);
187
+ const square = document.querySelector(`[data-square="${squareName}"]`);
188
+ if (square) {
189
+ square.textContent = this.pieceToUnicode(char);
190
+ square.classList.add(char === char.toUpperCase() ? 'piece-white' : 'piece-black');
191
+ }
192
+ colIndex++;
 
 
 
 
 
 
 
 
193
  }
194
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  });
196
+ }
197
+
198
+ updateGameStatus() {
199
+ const statusEl = document.getElementById('game-status');
200
+ const playerEl = document.getElementById('current-player');
201
+
202
+ if (!this.gameState) {
203
+ statusEl.textContent = 'Choisissez un mode de jeu pour commencer';
204
+ playerEl.textContent = '';
205
+ return;
206
+ }
207
+
208
+ if (this.gameState.game_over) {
209
+ if (this.gameState.winner === 'draw') {
210
+ statusEl.textContent = '🤝 Match nul !';
211
+ } else {
212
+ const winner = this.gameState.winner === 'white' ? 'Blancs' : 'Noirs';
213
+ statusEl.textContent = `🏆 ${winner} gagnent !`;
214
  }
215
+ playerEl.textContent = 'Partie terminée';
216
  } else {
217
+ const currentPlayer = this.gameState.current_player === 'white' ? 'Blancs' : 'Noirs';
218
+ const modeText = this.gameState.mode === 'ai' ? 'vs IA' : 'Humain vs Humain';
219
+ statusEl.textContent = `🎮 ${modeText}`;
220
+ playerEl.textContent = `Au tour des ${currentPlayer}`;
221
  }
 
 
 
222
  }
223
+
224
+ updateMovesHistory() {
225
+ const historyEl = document.getElementById('moves-history');
226
+
227
+ if (!this.gameState || this.gameState.moves_history.length === 0) {
228
+ historyEl.innerHTML = '<p class="text-gray-500 italic">Aucun coup joué</p>';
229
+ return;
230
+ }
231
+
232
+ let historyHtml = '<div class="grid grid-cols-2 gap-2 text-sm">';
233
+ this.gameState.moves_history.forEach((move, index) => {
234
+ const moveNumber = Math.floor(index / 2) + 1;
235
+ const isWhite = index % 2 === 0;
236
+
237
+ if (isWhite) {
238
+ historyHtml += `<div class="font-semibold">${moveNumber}. ${move}</div>`;
239
+ } else {
240
+ historyHtml += `<div class="pl-4">${move}</div>`;
241
  }
242
  });
243
+ historyHtml += '</div>';
244
+ historyEl.innerHTML = historyHtml;
245
 
246
+ // Scroll vers le bas
247
+ historyEl.scrollTop = historyEl.scrollHeight;
248
+ }
249
+
250
+ async handleSquareClick(event) {
251
+ const square = event.target;
252
+ const squareName = square.dataset.square;
253
+
254
+ if (!this.gameState || this.gameState.game_over) return;
255
+
256
+ // Si aucune pièce n'est sélectionnée
257
+ if (!this.selectedSquare) {
258
+ if (square.textContent && this.canSelectPiece(square)) {
259
+ this.selectSquare(square);
260
+ }
261
+ return;
262
+ }
263
+
264
+ // Si on clique sur la même case
265
+ if (this.selectedSquare === square) {
266
+ this.deselectSquare();
267
+ return;
268
+ }
269
+
270
+ // Si on clique sur une autre pièce de la même couleur
271
+ if (square.textContent && this.canSelectPiece(square)) {
272
+ this.selectSquare(square);
273
+ return;
274
+ }
275
+
276
+ // Tentative de coup
277
+ const fromSquare = this.selectedSquare.dataset.square;
278
+ const toSquare = squareName;
279
+ const move = fromSquare + toSquare;
280
+
281
+ await this.makeMove(move);
282
+ }
283
+
284
+ canSelectPiece(square) {
285
+ if (!square.textContent) return false;
286
 
287
+ const isWhitePiece = square.classList.contains('piece-white');
288
+ const isCurrentPlayerWhite = this.gameState.current_player === 'white';
289
+
290
+ return isWhitePiece === isCurrentPlayerWhite;
291
+ }
292
+
293
+ selectSquare(square) {
294
+ this.deselectSquare();
295
+ this.selectedSquare = square;
296
+ square.classList.add('selected');
297
+ this.highlightLegalMoves(square.dataset.square);
298
+ }
299
+
300
+ deselectSquare() {
301
+ if (this.selectedSquare) {
302
+ this.selectedSquare.classList.remove('selected');
303
+ this.selectedSquare = null;
304
  }
305
+ this.clearHighlights();
 
306
  }
307
+
308
+ highlightLegalMoves(fromSquare) {
309
+ this.clearHighlights();
 
 
 
 
310
 
311
+ if (!this.gameState.legal_moves) return;
 
 
312
 
313
+ const movesFromSquare = this.gameState.legal_moves.filter(move =>
314
+ move.startsWith(fromSquare)
315
+ );
316
 
317
+ movesFromSquare.forEach(move => {
318
+ const toSquare = move.substring(2, 4);
319
+ const square = document.querySelector(`[data-square="${toSquare}"]`);
320
+ if (square) {
321
+ square.classList.add('legal-move');
322
+ }
323
+ });
324
  }
325
+
326
+ clearHighlights() {
327
+ document.querySelectorAll('.legal-move').forEach(square => {
328
+ square.classList.remove('legal-move');
329
+ });
330
+ }
331
+
332
+ async makeMove(move) {
333
+ try {
334
+ const response = await fetch('/api/make_move', {
335
+ method: 'POST',
336
+ headers: {
337
+ 'Content-Type': 'application/json'
338
+ },
339
+ body: JSON.stringify({ move })
340
+ });
341
+
342
+ const data = await response.json();
343
+
344
+ if (data.success) {
345
+ this.gameState = data.game_state;
346
+ this.updateDisplay();
347
+ this.deselectSquare();
348
+
349
+ // Si l'IA a joué un coup
350
+ if (data.ai_move) {
351
+ setTimeout(() => {
352
+ console.log('IA a joué:', data.ai_move);
353
+ }, 500);
354
+ }
355
  } else {
356
+ alert('Coup invalide: ' + data.error);
 
357
  }
358
+ } catch (error) {
359
+ console.error('Erreur lors du coup:', error);
360
+ alert('Erreur de connexion');
361
  }
362
  }
363
+
364
+ async startNewGame(mode) {
365
+ try {
366
+ const response = await fetch('/api/new_game', {
367
+ method: 'POST',
368
+ headers: {
369
+ 'Content-Type': 'application/json'
370
+ },
371
+ body: JSON.stringify({ mode })
372
+ });
373
+
374
+ const data = await response.json();
375
+
376
+ if (data.success) {
377
+ this.gameState = data.game_state;
378
+ this.updateDisplay();
379
+ this.deselectSquare();
380
+ } else {
381
+ alert('Erreur lors de la création de la partie');
382
+ }
383
+ } catch (error) {
384
+ console.error('Erreur:', error);
385
+ alert('Erreur de connexion');
386
+ }
387
  }
388
+
389
+ async resetGame() {
390
+ if (!this.gameState) return;
391
+
392
+ try {
393
+ const response = await fetch('/api/reset_game', {
394
+ method: 'POST',
395
+ headers: {
396
+ 'Content-Type': 'application/json'
397
+ }
398
+ });
399
+
400
+ const data = await response.json();
401
+
402
+ if (data.success) {
403
+ this.gameState = data.game_state;
404
+ this.updateDisplay();
405
+ this.deselectSquare();
406
+ }
407
+ } catch (error) {
408
+ console.error('Erreur:', error);
409
+ alert('Erreur de connexion');
410
+ }
411
  }
412
+
413
+ updateDisplay() {
414
+ this.updateBoard();
415
+ this.updateGameStatus();
416
+ this.updateMovesHistory();
 
 
 
 
 
417
  }
418
+
419
+ setupEventListeners() {
420
+ document.getElementById('btn-human-mode').addEventListener('click', () => {
421
+ this.startNewGame('human');
422
+ });
423
+
424
+ document.getElementById('btn-ai-mode').addEventListener('click', () => {
425
+ this.startNewGame('ai');
426
+ });
427
+
428
+ document.getElementById('btn-reset').addEventListener('click', () => {
429
+ if (confirm('Êtes-vous sûr de vouloir recommencer la partie ?')) {
430
+ this.resetGame();
 
 
 
 
 
 
 
 
 
 
431
  }
432
  });
 
 
 
 
 
 
 
433
  }
434
  }
435
+
436
+ // Initialiser le jeu quand la page est chargée
437
+ document.addEventListener('DOMContentLoaded', () => {
438
+ new ChessGame();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
439
  });
440
  </script>
441
  </body>