Docfile commited on
Commit
294f52c
·
verified ·
1 Parent(s): a8e8b41

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +442 -118
templates/index.html CHANGED
@@ -3,26 +3,113 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Jeu d'Échecs Flask</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <style>
9
- /* Pour centrer le SVG et éviter qu'il ne soit trop large sur mobile */
10
- .board-container svg {
11
- display: block;
 
 
 
12
  margin: 0 auto;
13
- max-width: 100%;
14
- height: auto;
 
 
15
  }
16
- /* Simple highlight pour la dernière case jouée par l'IA */
17
- .highlight-last-move rect.lastmove {
18
- fill: rgba(255, 255, 0, 0.4) !important; /* Jaune semi-transparent */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  }
20
  </style>
21
  </head>
22
  <body class="bg-gray-900 text-gray-100 min-h-screen flex flex-col items-center p-4">
23
 
24
- <div class="container mx-auto max-w-4xl">
25
- <h1 class="text-4xl font-bold text-center my-6 text-teal-400">Jeu d'Échecs</h1>
26
 
27
  <div class="bg-gray-800 p-6 rounded-lg shadow-xl mb-6">
28
  <h2 class="text-2xl font-semibold mb-3 text-sky-400">Configuration</h2>
@@ -39,176 +126,411 @@
39
  </button>
40
  </div>
41
  <button id="resetGame" class="bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
42
- Nouvelle Partie (Reset)
43
  </button>
44
  </div>
45
- <p class="mt-3 text-sm text-gray-400">Mode Actuel: <span id="currentMode" class="font-semibold">{{ game_mode.upper() }}</span></p>
46
- <p id="playerColorInfo" class="mt-1 text-sm text-gray-400 {% if game_mode != 'ai' %}hidden{% endif %}">
47
- Vous jouez avec les: <span id="currentPlayerColor" class="font-semibold">{{ player_color.capitalize() if player_color else 'N/A' }}</span>
48
  </p>
49
  </div>
50
 
51
- <div class="grid md:grid-cols-3 gap-6">
52
- <div class="md:col-span-2 bg-gray-800 p-2 sm:p-6 rounded-lg shadow-xl board-container">
53
- <div id="boardDisplay" class="highlight-last-move">
54
- {{ initial_board_svg|safe }}
 
55
  </div>
56
  </div>
57
 
58
  <div class="bg-gray-800 p-6 rounded-lg shadow-xl">
59
  <h3 class="text-xl font-semibold mb-3 text-sky-400">Informations</h3>
60
- <p id="turnDisplay" class="mb-2">Tour des: <span class="font-bold">Blancs</span></p>
61
- <p id="status" class="text-yellow-400 font-semibold mb-4 h-6"></p>
 
 
62
 
63
- <form id="moveForm" class="space-y-3">
64
  <div>
65
- <label for="moveInput" class="block text-sm font-medium text-gray-300">Votre coup (ex: e2e4, Nf3):</label>
66
- <input type="text" id="moveInput" name="move"
67
- class="mt-1 block w-full bg-gray-700 border border-gray-600 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-sky-500 focus:border-sky-500 sm:text-sm text-white"
68
- placeholder="e.g., e2e4">
 
 
69
  </div>
70
- <button type="submit"
71
- class="w-full bg-teal-600 hover:bg-teal-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline transition duration-150 ease-in-out">
72
- Jouer
73
- </button>
74
- </form>
75
-
76
- <div id="outcomeDisplay" class="mt-4 text-lg font-bold text-center">
77
- {% if is_game_over %}
78
- <p class="text-green-400">{{ outcome }}</p>
79
- {% endif %}
80
  </div>
81
- <div class="mt-4">
82
- <p class="text-sm text-gray-400">Dernier coup IA:</p>
83
- <p id="lastAIMove" class="text-gray-200 font-mono">-</p>
 
 
 
 
 
 
 
 
 
84
  </div>
85
  </div>
86
  </div>
87
  </div>
88
 
89
  <script>
90
- const boardDisplay = document.getElementById('boardDisplay');
 
 
 
 
 
 
 
 
 
 
91
  const moveForm = document.getElementById('moveForm');
92
  const moveInput = document.getElementById('moveInput');
93
  const statusDisplay = document.getElementById('status');
94
  const turnDisplay = document.getElementById('turnDisplay').querySelector('span');
95
  const outcomeDisplay = document.getElementById('outcomeDisplay');
 
96
  const lastAIMoveDisplay = document.getElementById('lastAIMove');
97
-
98
  const currentModeDisplay = document.getElementById('currentMode');
99
  const playerColorInfoDisplay = document.getElementById('playerColorInfo');
100
  const currentPlayerColorDisplay = document.getElementById('currentPlayerColor');
101
 
102
- let currentFEN = "{{ initial_fen }}";
103
- let gameMode = "{{ game_mode }}";
104
- let playerColor = "{{ player_color if player_color else 'white' }}"; // Couleur du joueur humain en mode AI
105
- let isPlayerTurn = true; // Utilisé en mode AI
 
106
 
107
- function updateBoard(data) {
108
- if (data.board_svg) {
109
- boardDisplay.innerHTML = data.board_svg;
110
- }
111
- currentFEN = data.fen;
112
-
113
- if (data.turn) {
114
- turnDisplay.textContent = data.turn === 'white' ? 'Blancs' : 'Noirs';
115
- } else {
116
- // Déduire du FEN si non fourni explicitement
117
- const fenTurn = data.fen.split(' ')[1];
118
- turnDisplay.textContent = fenTurn === 'w' ? 'Blancs' : 'Noirs';
119
- }
120
 
121
- if (data.ai_move_uci) {
122
- lastAIMoveDisplay.textContent = data.ai_move_uci;
123
- } else if (!data.game_over) {
124
- // Ne pas effacer si la partie est finie et que le dernier coup était humain
125
- // lastAIMoveDisplay.textContent = "-";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  }
 
127
 
128
-
129
- if (data.game_over) {
130
- statusDisplay.textContent = "Partie terminée!";
131
- outcomeDisplay.innerHTML = `<p class="text-green-400">${data.outcome}</p>`;
132
- moveInput.disabled = true;
133
- moveForm.querySelector('button[type="submit"]').disabled = true;
134
- } else {
135
- statusDisplay.textContent = '';
136
- outcomeDisplay.innerHTML = '';
137
- moveInput.disabled = false;
138
- moveForm.querySelector('button[type="submit"]').disabled = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  }
140
 
141
- // Gérer le tour du joueur en mode AI
142
- updatePlayerTurnState();
143
  }
144
 
145
- function updatePlayerTurnState() {
146
- const fenTurn = currentFEN.split(' ')[1]; // 'w' or 'b'
 
 
147
  if (gameMode === 'ai') {
148
  const aiIsWhite = (playerColor === 'black');
149
  const aiIsBlack = (playerColor === 'white');
150
-
151
- if ((aiIsWhite && fenTurn === 'w') || (aiIsBlack && fenTurn === 'b')) {
152
  isPlayerTurn = false;
153
- moveInput.disabled = true;
154
- moveForm.querySelector('button[type="submit"]').disabled = true;
155
  statusDisplay.textContent = "L'IA réfléchit...";
 
156
  } else {
157
  isPlayerTurn = true;
158
- if (!currentFEN.split(' ')[0].includes('k') || !currentFEN.split(' ')[0].includes('K')) { // Check si la partie est terminée
159
- moveInput.disabled = false;
160
- moveForm.querySelector('button[type="submit"]').disabled = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  }
162
- statusDisplay.textContent = "À vous de jouer.";
163
  }
164
- } else { // PvP mode
165
- isPlayerTurn = true; // Toujours au tour du joueur en PvP
166
- moveInput.disabled = false;
167
- moveForm.querySelector('button[type="submit"]').disabled = false;
168
  }
 
 
169
  }
170
 
 
 
 
 
 
 
 
171
 
172
- moveForm.addEventListener('submit', async (e) => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  e.preventDefault();
174
- const move = moveInput.value.trim();
175
- if (!move) return;
176
 
177
- statusDisplay.textContent = 'Traitement...';
178
- lastAIMoveDisplay.textContent = "-"; // Clear last AI move when player submits
 
 
 
 
 
 
 
 
 
 
 
179
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  try {
181
  const response = await fetch('/make_move', {
182
  method: 'POST',
183
  headers: { 'Content-Type': 'application/json' },
184
- body: JSON.stringify({ move: move })
185
  });
186
  const data = await response.json();
187
 
188
  if (data.error) {
189
  statusDisplay.textContent = `Erreur: ${data.error}`;
190
  } else {
191
- updateBoard(data);
192
- moveInput.value = ''; // Clear input on successful move
 
 
 
 
 
 
 
 
 
 
 
 
193
  }
194
  } catch (error) {
195
  console.error("Erreur lors de la communication:", error);
196
  statusDisplay.textContent = "Erreur de communication avec le serveur.";
197
  }
 
 
 
 
 
 
 
 
 
 
198
  });
199
 
 
200
  document.getElementById('resetGame').addEventListener('click', async () => {
201
  statusDisplay.textContent = 'Réinitialisation...';
202
  try {
203
  const response = await fetch('/reset_game', { method: 'POST' });
204
  const data = await response.json();
205
- updateBoard(data); // data contient fen et board_svg
 
 
206
  lastAIMoveDisplay.textContent = "-";
207
- // Réinitialiser l'état du jeu pour une nouvelle partie
208
- moveInput.disabled = false;
209
- moveForm.querySelector('button[type="submit"]').disabled = false;
210
- outcomeDisplay.innerHTML = ''; // Effacer le message de fin de partie
211
- // Mettre à jour l'affichage du mode et de la couleur
212
  currentModeDisplay.textContent = data.game_mode ? data.game_mode.toUpperCase() : 'PVP';
213
  if (data.game_mode === 'ai' && data.player_color) {
214
  currentPlayerColorDisplay.textContent = data.player_color.charAt(0).toUpperCase() + data.player_color.slice(1);
@@ -216,9 +538,11 @@
216
  } else {
217
  playerColorInfoDisplay.classList.add('hidden');
218
  }
219
- // Mettre à jour l'état du tour
220
- updatePlayerTurnState();
221
-
 
 
222
  } catch (error) {
223
  console.error("Erreur lors de la réinitialisation:", error);
224
  statusDisplay.textContent = "Erreur lors de la réinitialisation.";
@@ -227,7 +551,6 @@
227
 
228
  async function setGameMode(mode, pColor = 'white') {
229
  statusDisplay.textContent = `Changement de mode vers ${mode.toUpperCase()}...`;
230
- lastAIMoveDisplay.textContent = "-";
231
  try {
232
  const response = await fetch('/set_mode', {
233
  method: 'POST',
@@ -235,12 +558,16 @@
235
  body: JSON.stringify({ game_mode: mode, player_color: pColor })
236
  });
237
  const data = await response.json();
 
238
  if (data.error) {
239
  statusDisplay.textContent = `Erreur: ${data.error}`;
240
  } else {
241
  gameMode = data.game_mode;
242
  playerColor = data.player_color;
243
- updateBoard(data); // data contient fen, board_svg, etc.
 
 
 
244
  currentModeDisplay.textContent = gameMode.toUpperCase();
245
  if (gameMode === 'ai') {
246
  currentPlayerColorDisplay.textContent = playerColor.charAt(0).toUpperCase() + playerColor.slice(1);
@@ -248,9 +575,11 @@
248
  } else {
249
  playerColorInfoDisplay.classList.add('hidden');
250
  }
251
- statusDisplay.textContent = data.message;
 
252
  if (data.initial_ai_move_uci) {
253
  lastAIMoveDisplay.textContent = data.initial_ai_move_uci;
 
254
  }
255
  }
256
  } catch (error) {
@@ -263,14 +592,9 @@
263
  document.getElementById('setPvAIWhite').addEventListener('click', () => setGameMode('ai', 'white'));
264
  document.getElementById('setPvAIBlack').addEventListener('click', () => setGameMode('ai', 'black'));
265
 
266
- // Initialiser l'état du tour au chargement de la page
267
- updatePlayerTurnState();
268
- // Afficher la couleur du joueur si en mode AI au chargement
269
- if (gameMode === 'ai') {
270
- currentPlayerColorDisplay.textContent = playerColor.charAt(0).toUpperCase() + playerColor.slice(1);
271
- playerColorInfoDisplay.classList.remove('hidden');
272
- }
273
-
274
  </script>
275
  </body>
276
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Jeu d'Échecs Tactile</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
  margin: 0 auto;
16
+ border: 3px solid #374151;
17
+ border-radius: 8px;
18
+ overflow: hidden;
19
+ user-select: none;
20
  }
21
+
22
+ .chess-square {
23
+ position: relative;
24
+ display: flex;
25
+ align-items: center;
26
+ justify-content: center;
27
+ cursor: pointer;
28
+ transition: all 0.2s ease;
29
+ }
30
+
31
+ .chess-square.light {
32
+ background-color: #f0d9b5;
33
+ }
34
+
35
+ .chess-square.dark {
36
+ background-color: #b58863;
37
+ }
38
+
39
+ .chess-square.selected {
40
+ box-shadow: inset 0 0 0 4px #fbbf24;
41
+ z-index: 2;
42
+ }
43
+
44
+ .chess-square.possible-move {
45
+ box-shadow: inset 0 0 0 3px #10b981;
46
+ }
47
+
48
+ .chess-square.possible-move:after {
49
+ content: '';
50
+ position: absolute;
51
+ width: 30%;
52
+ height: 30%;
53
+ background-color: #10b981;
54
+ border-radius: 50%;
55
+ opacity: 0.7;
56
+ }
57
+
58
+ .chess-square.last-move {
59
+ box-shadow: inset 0 0 0 3px #eab308;
60
+ }
61
+
62
+ .chess-piece {
63
+ font-size: clamp(2rem, 6vw, 3.5rem);
64
+ font-family: 'Segoe UI Symbol', 'Apple Symbols', sans-serif;
65
+ cursor: grab;
66
+ transition: transform 0.1s ease;
67
+ z-index: 1;
68
+ }
69
+
70
+ .chess-piece:hover {
71
+ transform: scale(1.1);
72
+ }
73
+
74
+ .chess-piece.dragging {
75
+ cursor: grabbing;
76
+ transform: scale(1.2);
77
+ z-index: 10;
78
+ pointer-events: none;
79
+ }
80
+
81
+ .coordinates {
82
+ color: #6b7280;
83
+ font-size: 0.75rem;
84
+ font-weight: bold;
85
+ }
86
+
87
+ .coord-file {
88
+ position: absolute;
89
+ bottom: 2px;
90
+ right: 4px;
91
+ }
92
+
93
+ .coord-rank {
94
+ position: absolute;
95
+ top: 2px;
96
+ left: 4px;
97
+ }
98
+
99
+ @media (max-width: 640px) {
100
+ .chess-piece {
101
+ font-size: 2rem;
102
+ }
103
+ .coordinates {
104
+ font-size: 0.6rem;
105
+ }
106
  }
107
  </style>
108
  </head>
109
  <body class="bg-gray-900 text-gray-100 min-h-screen flex flex-col items-center p-4">
110
 
111
+ <div class="container mx-auto max-w-6xl">
112
+ <h1 class="text-4xl font-bold text-center my-6 text-teal-400">Jeu d'Échecs Tactile</h1>
113
 
114
  <div class="bg-gray-800 p-6 rounded-lg shadow-xl mb-6">
115
  <h2 class="text-2xl font-semibold mb-3 text-sky-400">Configuration</h2>
 
126
  </button>
127
  </div>
128
  <button id="resetGame" class="bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
129
+ Nouvelle Partie
130
  </button>
131
  </div>
132
+ <p class="mt-3 text-sm text-gray-400">Mode: <span id="currentMode" class="font-semibold">PVP</span></p>
133
+ <p id="playerColorInfo" class="mt-1 text-sm text-gray-400 hidden">
134
+ Vous jouez: <span id="currentPlayerColor" class="font-semibold">Blancs</span>
135
  </p>
136
  </div>
137
 
138
+ <div class="grid lg:grid-cols-4 gap-6">
139
+ <div class="lg:col-span-3 bg-gray-800 p-4 rounded-lg shadow-xl">
140
+ <div id="chessBoard" class="chess-board"></div>
141
+ <div class="mt-4 text-center text-sm text-gray-400">
142
+ <p>Cliquez sur une pièce puis sur la destination, ou glissez-déposez</p>
143
  </div>
144
  </div>
145
 
146
  <div class="bg-gray-800 p-6 rounded-lg shadow-xl">
147
  <h3 class="text-xl font-semibold mb-3 text-sky-400">Informations</h3>
148
+ <p id="turnDisplay" class="mb-2">Tour: <span class="font-bold text-white">Blancs</span></p>
149
+ <p id="status" class="text-yellow-400 font-semibold mb-4 min-h-6"></p>
150
+
151
+ <div id="outcomeDisplay" class="mb-4 text-lg font-bold text-center"></div>
152
 
153
+ <div class="space-y-3">
154
  <div>
155
+ <p class="text-sm text-gray-400">Dernier coup:</p>
156
+ <p id="lastMove" class="text-gray-200 font-mono text-sm">-</p>
157
+ </div>
158
+ <div>
159
+ <p class="text-sm text-gray-400">Dernier coup IA:</p>
160
+ <p id="lastAIMove" class="text-gray-200 font-mono text-sm">-</p>
161
  </div>
 
 
 
 
 
 
 
 
 
 
162
  </div>
163
+
164
+ <div class="mt-6">
165
+ <h4 class="text-lg font-semibold mb-2 text-sky-400">Notation manuelle</h4>
166
+ <form id="moveForm" class="space-y-3">
167
+ <input type="text" id="moveInput"
168
+ class="w-full bg-gray-700 border border-gray-600 rounded-md py-2 px-3 focus:outline-none focus:ring-sky-500 focus:border-sky-500 text-white text-sm"
169
+ placeholder="e.g., e2e4, Nf3">
170
+ <button type="submit"
171
+ class="w-full bg-teal-600 hover:bg-teal-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline transition duration-150 ease-in-out">
172
+ Jouer
173
+ </button>
174
+ </form>
175
  </div>
176
  </div>
177
  </div>
178
  </div>
179
 
180
  <script>
181
+ // Configuration du jeu
182
+ let gameMode = 'pvp';
183
+ let playerColor = 'white';
184
+ let isPlayerTurn = true;
185
+ let gameBoard = {};
186
+ let selectedSquare = null;
187
+ let possibleMoves = [];
188
+ let lastMoveSquares = [];
189
+
190
+ // Éléments DOM
191
+ const chessBoardEl = document.getElementById('chessBoard');
192
  const moveForm = document.getElementById('moveForm');
193
  const moveInput = document.getElementById('moveInput');
194
  const statusDisplay = document.getElementById('status');
195
  const turnDisplay = document.getElementById('turnDisplay').querySelector('span');
196
  const outcomeDisplay = document.getElementById('outcomeDisplay');
197
+ const lastMoveDisplay = document.getElementById('lastMove');
198
  const lastAIMoveDisplay = document.getElementById('lastAIMove');
 
199
  const currentModeDisplay = document.getElementById('currentMode');
200
  const playerColorInfoDisplay = document.getElementById('playerColorInfo');
201
  const currentPlayerColorDisplay = document.getElementById('currentPlayerColor');
202
 
203
+ // Symboles des pièces d'échecs
204
+ const pieceSymbols = {
205
+ 'K': '♔', 'Q': '♕', 'R': '♖', 'B': '♗', 'N': '♘', 'P': '♙',
206
+ 'k': '♚', 'q': '♛', 'r': '♜', 'b': '♝', 'n': '♞', 'p': '♟'
207
+ };
208
 
209
+ // Position initiale FEN
210
+ let currentFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
 
 
 
 
 
 
 
 
 
 
 
211
 
212
+ function createChessBoard() {
213
+ chessBoardEl.innerHTML = '';
214
+
215
+ for (let rank = 8; rank >= 1; rank--) {
216
+ for (let file = 0; file < 8; file++) {
217
+ const fileChar = String.fromCharCode(97 + file); // a-h
218
+ const square = fileChar + rank;
219
+
220
+ const squareEl = document.createElement('div');
221
+ squareEl.className = `chess-square ${(rank + file) % 2 === 0 ? 'light' : 'dark'}`;
222
+ squareEl.dataset.square = square;
223
+
224
+ // Ajouter les coordonnées
225
+ if (file === 0) {
226
+ const rankCoord = document.createElement('div');
227
+ rankCoord.className = 'coordinates coord-rank';
228
+ rankCoord.textContent = rank;
229
+ squareEl.appendChild(rankCoord);
230
+ }
231
+ if (rank === 1) {
232
+ const fileCoord = document.createElement('div');
233
+ fileCoord.className = 'coordinates coord-file';
234
+ fileCoord.textContent = fileChar;
235
+ squareEl.appendChild(fileCoord);
236
+ }
237
+
238
+ // Event listeners pour les interactions
239
+ squareEl.addEventListener('click', handleSquareClick);
240
+ squareEl.addEventListener('dragover', handleDragOver);
241
+ squareEl.addEventListener('drop', handleDrop);
242
+
243
+ chessBoardEl.appendChild(squareEl);
244
+ }
245
  }
246
+ }
247
 
248
+ function updateBoardFromFEN(fen) {
249
+ const [boardPart] = fen.split(' ');
250
+ const ranks = boardPart.split('/');
251
+
252
+ gameBoard = {};
253
+
254
+ // Effacer toutes les pièces actuelles
255
+ document.querySelectorAll('.chess-piece').forEach(piece => piece.remove());
256
+
257
+ for (let rankIdx = 0; rankIdx < 8; rankIdx++) {
258
+ const rank = 8 - rankIdx;
259
+ const rankStr = ranks[rankIdx];
260
+ let fileIdx = 0;
261
+
262
+ for (let char of rankStr) {
263
+ if (isNaN(char)) {
264
+ // C'est une pièce
265
+ const file = String.fromCharCode(97 + fileIdx);
266
+ const square = file + rank;
267
+ gameBoard[square] = char;
268
+
269
+ const squareEl = document.querySelector(`[data-square="${square}"]`);
270
+ if (squareEl) {
271
+ const pieceEl = document.createElement('div');
272
+ pieceEl.className = 'chess-piece';
273
+ pieceEl.textContent = pieceSymbols[char];
274
+ pieceEl.draggable = true;
275
+ pieceEl.dataset.piece = char;
276
+ pieceEl.dataset.square = square;
277
+
278
+ pieceEl.addEventListener('dragstart', handleDragStart);
279
+ pieceEl.addEventListener('dragend', handleDragEnd);
280
+
281
+ squareEl.appendChild(pieceEl);
282
+ }
283
+ fileIdx++;
284
+ } else {
285
+ // C'est un nombre (cases vides)
286
+ fileIdx += parseInt(char);
287
+ }
288
+ }
289
  }
290
 
291
+ currentFEN = fen;
292
+ updateTurnDisplay();
293
  }
294
 
295
+ function updateTurnDisplay() {
296
+ const turn = currentFEN.split(' ')[1];
297
+ turnDisplay.textContent = turn === 'w' ? 'Blancs' : 'Noirs';
298
+
299
  if (gameMode === 'ai') {
300
  const aiIsWhite = (playerColor === 'black');
301
  const aiIsBlack = (playerColor === 'white');
302
+
303
+ if ((aiIsWhite && turn === 'w') || (aiIsBlack && turn === 'b')) {
304
  isPlayerTurn = false;
 
 
305
  statusDisplay.textContent = "L'IA réfléchit...";
306
+ // En mode IA, on pourrait désactiver les interactions ici
307
  } else {
308
  isPlayerTurn = true;
309
+ statusDisplay.textContent = vous de jouer";
310
+ }
311
+ } else {
312
+ isPlayerTurn = true;
313
+ statusDisplay.textContent = "";
314
+ }
315
+ }
316
+
317
+ function handleSquareClick(e) {
318
+ if (!isPlayerTurn) return;
319
+
320
+ const square = e.currentTarget.dataset.square;
321
+ const piece = gameBoard[square];
322
+
323
+ if (selectedSquare) {
324
+ if (selectedSquare === square) {
325
+ // Déselection
326
+ clearSelection();
327
+ } else if (possibleMoves.includes(square)) {
328
+ // Coup valide
329
+ makeMove(selectedSquare + square);
330
+ } else if (piece && isPlayerPiece(piece)) {
331
+ // Sélection d'une autre pièce
332
+ selectSquare(square);
333
+ } else {
334
+ clearSelection();
335
+ }
336
+ } else if (piece && isPlayerPiece(piece)) {
337
+ selectSquare(square);
338
+ }
339
+ }
340
+
341
+ function selectSquare(square) {
342
+ clearSelection();
343
+ selectedSquare = square;
344
+
345
+ const squareEl = document.querySelector(`[data-square="${square}"]`);
346
+ squareEl.classList.add('selected');
347
+
348
+ // Simuler les coups possibles (ici on devrait faire appel au serveur)
349
+ showPossibleMoves(square);
350
+ }
351
+
352
+ function clearSelection() {
353
+ selectedSquare = null;
354
+ possibleMoves = [];
355
+
356
+ document.querySelectorAll('.chess-square').forEach(sq => {
357
+ sq.classList.remove('selected', 'possible-move');
358
+ });
359
+ }
360
+
361
+ function showPossibleMoves(fromSquare) {
362
+ // Pour l'instant, on simule quelques coups possibles
363
+ // Dans une vraie implémentation, on ferait appel au serveur
364
+ const piece = gameBoard[fromSquare];
365
+ possibleMoves = getPossibleMovesForPiece(fromSquare, piece);
366
+
367
+ possibleMoves.forEach(square => {
368
+ const squareEl = document.querySelector(`[data-square="${square}"]`);
369
+ if (squareEl) {
370
+ squareEl.classList.add('possible-move');
371
+ }
372
+ });
373
+ }
374
+
375
+ function getPossibleMovesForPiece(square, piece) {
376
+ // Simulation basique - dans la vraie version, ceci viendrait du serveur
377
+ const moves = [];
378
+ const file = square.charCodeAt(0) - 97;
379
+ const rank = parseInt(square[1]);
380
+
381
+ // Exemple pour un pion
382
+ if (piece.toLowerCase() === 'p') {
383
+ const direction = piece === 'P' ? 1 : -1;
384
+ const newRank = rank + direction;
385
+ if (newRank >= 1 && newRank <= 8) {
386
+ const newSquare = String.fromCharCode(97 + file) + newRank;
387
+ if (!gameBoard[newSquare]) {
388
+ moves.push(newSquare);
389
+
390
+ // Double move from starting position
391
+ if ((piece === 'P' && rank === 2) || (piece === 'p' && rank === 7)) {
392
+ const doubleSquare = String.fromCharCode(97 + file) + (rank + 2 * direction);
393
+ if (!gameBoard[doubleSquare]) {
394
+ moves.push(doubleSquare);
395
+ }
396
+ }
397
  }
 
398
  }
 
 
 
 
399
  }
400
+
401
+ return moves;
402
  }
403
 
404
+ function isPlayerPiece(piece) {
405
+ if (gameMode === 'pvp') return true;
406
+
407
+ const isWhitePiece = piece === piece.toUpperCase();
408
+ return (playerColor === 'white' && isWhitePiece) ||
409
+ (playerColor === 'black' && !isWhitePiece);
410
+ }
411
 
412
+ // Gestion du drag & drop
413
+ function handleDragStart(e) {
414
+ if (!isPlayerTurn) {
415
+ e.preventDefault();
416
+ return;
417
+ }
418
+
419
+ const piece = e.target.dataset.piece;
420
+ if (!isPlayerPiece(piece)) {
421
+ e.preventDefault();
422
+ return;
423
+ }
424
+
425
+ e.target.classList.add('dragging');
426
+ e.dataTransfer.setData('text/plain', e.target.dataset.square);
427
+
428
+ // Sélectionner la case pour montrer les coups possibles
429
+ selectSquare(e.target.dataset.square);
430
+ }
431
+
432
+ function handleDragEnd(e) {
433
+ e.target.classList.remove('dragging');
434
+ }
435
+
436
+ function handleDragOver(e) {
437
  e.preventDefault();
438
+ }
 
439
 
440
+ function handleDrop(e) {
441
+ e.preventDefault();
442
+ const fromSquare = e.dataTransfer.getData('text/plain');
443
+ const toSquare = e.currentTarget.dataset.square;
444
+
445
+ if (fromSquare && toSquare && fromSquare !== toSquare) {
446
+ if (possibleMoves.includes(toSquare)) {
447
+ makeMove(fromSquare + toSquare);
448
+ }
449
+ }
450
+
451
+ clearSelection();
452
+ }
453
 
454
+ function highlightLastMove(move) {
455
+ // Effacer les anciens highlights
456
+ document.querySelectorAll('.chess-square').forEach(sq => {
457
+ sq.classList.remove('last-move');
458
+ });
459
+
460
+ if (move && move.length >= 4) {
461
+ const fromSquare = move.substring(0, 2);
462
+ const toSquare = move.substring(2, 4);
463
+
464
+ const fromEl = document.querySelector(`[data-square="${fromSquare}"]`);
465
+ const toEl = document.querySelector(`[data-square="${toSquare}"]`);
466
+
467
+ if (fromEl) fromEl.classList.add('last-move');
468
+ if (toEl) toEl.classList.add('last-move');
469
+
470
+ lastMoveSquares = [fromSquare, toSquare];
471
+ }
472
+ }
473
+
474
+ // Communication avec le serveur (adapté de votre code original)
475
+ async function makeMove(moveStr) {
476
+ statusDisplay.textContent = 'Traitement...';
477
+ clearSelection();
478
+
479
  try {
480
  const response = await fetch('/make_move', {
481
  method: 'POST',
482
  headers: { 'Content-Type': 'application/json' },
483
+ body: JSON.stringify({ move: moveStr })
484
  });
485
  const data = await response.json();
486
 
487
  if (data.error) {
488
  statusDisplay.textContent = `Erreur: ${data.error}`;
489
  } else {
490
+ updateBoardFromFEN(data.fen);
491
+ lastMoveDisplay.textContent = moveStr;
492
+ highlightLastMove(moveStr);
493
+
494
+ if (data.ai_move_uci) {
495
+ lastAIMoveDisplay.textContent = data.ai_move_uci;
496
+ highlightLastMove(data.ai_move_uci);
497
+ }
498
+
499
+ if (data.game_over) {
500
+ statusDisplay.textContent = "Partie terminée!";
501
+ outcomeDisplay.innerHTML = `<p class="text-green-400">${data.outcome}</p>`;
502
+ isPlayerTurn = false;
503
+ }
504
  }
505
  } catch (error) {
506
  console.error("Erreur lors de la communication:", error);
507
  statusDisplay.textContent = "Erreur de communication avec le serveur.";
508
  }
509
+ }
510
+
511
+ // Form de saisie manuelle
512
+ moveForm.addEventListener('submit', async (e) => {
513
+ e.preventDefault();
514
+ const move = moveInput.value.trim();
515
+ if (!move) return;
516
+
517
+ await makeMove(move);
518
+ moveInput.value = '';
519
  });
520
 
521
+ // Boutons de configuration
522
  document.getElementById('resetGame').addEventListener('click', async () => {
523
  statusDisplay.textContent = 'Réinitialisation...';
524
  try {
525
  const response = await fetch('/reset_game', { method: 'POST' });
526
  const data = await response.json();
527
+
528
+ updateBoardFromFEN(data.fen);
529
+ lastMoveDisplay.textContent = "-";
530
  lastAIMoveDisplay.textContent = "-";
531
+ outcomeDisplay.innerHTML = '';
532
+ clearSelection();
533
+
 
 
534
  currentModeDisplay.textContent = data.game_mode ? data.game_mode.toUpperCase() : 'PVP';
535
  if (data.game_mode === 'ai' && data.player_color) {
536
  currentPlayerColorDisplay.textContent = data.player_color.charAt(0).toUpperCase() + data.player_color.slice(1);
 
538
  } else {
539
  playerColorInfoDisplay.classList.add('hidden');
540
  }
541
+
542
+ gameMode = data.game_mode || 'pvp';
543
+ playerColor = data.player_color || 'white';
544
+ updateTurnDisplay();
545
+
546
  } catch (error) {
547
  console.error("Erreur lors de la réinitialisation:", error);
548
  statusDisplay.textContent = "Erreur lors de la réinitialisation.";
 
551
 
552
  async function setGameMode(mode, pColor = 'white') {
553
  statusDisplay.textContent = `Changement de mode vers ${mode.toUpperCase()}...`;
 
554
  try {
555
  const response = await fetch('/set_mode', {
556
  method: 'POST',
 
558
  body: JSON.stringify({ game_mode: mode, player_color: pColor })
559
  });
560
  const data = await response.json();
561
+
562
  if (data.error) {
563
  statusDisplay.textContent = `Erreur: ${data.error}`;
564
  } else {
565
  gameMode = data.game_mode;
566
  playerColor = data.player_color;
567
+
568
+ updateBoardFromFEN(data.fen);
569
+ clearSelection();
570
+
571
  currentModeDisplay.textContent = gameMode.toUpperCase();
572
  if (gameMode === 'ai') {
573
  currentPlayerColorDisplay.textContent = playerColor.charAt(0).toUpperCase() + playerColor.slice(1);
 
575
  } else {
576
  playerColorInfoDisplay.classList.add('hidden');
577
  }
578
+
579
+ statusDisplay.textContent = data.message || '';
580
  if (data.initial_ai_move_uci) {
581
  lastAIMoveDisplay.textContent = data.initial_ai_move_uci;
582
+ highlightLastMove(data.initial_ai_move_uci);
583
  }
584
  }
585
  } catch (error) {
 
592
  document.getElementById('setPvAIWhite').addEventListener('click', () => setGameMode('ai', 'white'));
593
  document.getElementById('setPvAIBlack').addEventListener('click', () => setGameMode('ai', 'black'));
594
 
595
+ // Initialisation
596
+ createChessBoard();
597
+ updateBoardFromFEN(currentFEN);
 
 
 
 
 
598
  </script>
599
  </body>
600
  </html>