AC-Angelo93 commited on
Commit
b8e2629
·
verified ·
1 Parent(s): 98b3e22

undefined - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +910 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Chess Tutor
3
- emoji: 📊
4
- colorFrom: red
5
- colorTo: red
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: chess-tutor
3
+ emoji: 🐳
4
+ colorFrom: pink
5
+ colorTo: gray
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,910 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Interactive Chess Game</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ .chess-board {
11
+ width: 100%;
12
+ max-width: 500px;
13
+ aspect-ratio: 1/1;
14
+ }
15
+ .square {
16
+ position: relative;
17
+ width: 12.5%;
18
+ height: 12.5%;
19
+ float: left;
20
+ display: flex;
21
+ justify-content: center;
22
+ align-items: center;
23
+ cursor: pointer;
24
+ transition: all 0.3s;
25
+ }
26
+ .square:hover {
27
+ opacity: 0.8;
28
+ transform: scale(1.05);
29
+ }
30
+ .light {
31
+ background-color: #f0d9b5;
32
+ background-image: linear-gradient(145deg, #f0d9b5 0%, #e6c99a 100%);
33
+ }
34
+ .dark {
35
+ background-color: #b58863;
36
+ background-image: linear-gradient(145deg, #b58863 0%, #9c6e4a 100%);
37
+ }
38
+ .selected {
39
+ background-color: rgba(0, 255, 0, 0.5) !important;
40
+ }
41
+ .possible-move {
42
+ position: absolute;
43
+ width: 30%;
44
+ height: 30%;
45
+ border-radius: 50%;
46
+ background-color: rgba(0, 0, 0, 0.3);
47
+ }
48
+ .piece {
49
+ font-size: 2.5rem;
50
+ width: 100%;
51
+ height: 100%;
52
+ display: flex;
53
+ justify-content: center;
54
+ align-items: center;
55
+ cursor: grab;
56
+ user-select: none;
57
+ }
58
+ .piece:active {
59
+ cursor: grabbing;
60
+ }
61
+ .white {
62
+ color: #f8f8f8;
63
+ text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
64
+ filter: drop-shadow(1px 1px 1px rgba(0,0,0,0.3));
65
+ }
66
+ .black {
67
+ color: #3a3a3a;
68
+ text-shadow: 1px 1px 2px rgba(255,255,255,0.5);
69
+ filter: drop-shadow(1px 1px 1px rgba(0,0,0,0.3));
70
+ }
71
+ @media (max-width: 640px) {
72
+ .piece {
73
+ font-size: 1.8rem;
74
+ }
75
+ }
76
+ </style>
77
+ </head>
78
+ <body class="bg-gray-100 min-h-screen">
79
+ <div class="container mx-auto px-4 py-8">
80
+ <header class="text-center mb-8">
81
+ <h1 class="text-4xl font-bold text-gray-800 mb-2">Interactive Chess Tutor</h1>
82
+ <p class="text-lg text-gray-600">Learn chess tactics while you play</p>
83
+ </header>
84
+
85
+ <div class="flex flex-col lg:flex-row gap-8 items-center lg:items-start justify-center">
86
+ <!-- Chess Board -->
87
+ <div class="bg-amber-900 p-6 rounded-lg shadow-xl border-4 border-amber-700">
88
+ <div class="flex flex-col">
89
+ <div class="flex justify-end pr-2 mb-1">
90
+ <div class="w-full max-w-[500px] flex justify-between px-[4.5%]">
91
+ <span class="text-gray-700 font-medium w-[12.5%] text-center">a</span>
92
+ <span class="text-gray-700 font-medium w-[12.5%] text-center">b</span>
93
+ <span class="text-gray-700 font-medium w-[12.5%] text-center">c</span>
94
+ <span class="text-gray-700 font-medium w-[12.5%] text-center">d</span>
95
+ <span class="text-gray-700 font-medium w-[12.5%] text-center">e</span>
96
+ <span class="text-gray-700 font-medium w-[12.5%] text-center">f</span>
97
+ <span class="text-gray-700 font-medium w-[12.5%] text-center">g</span>
98
+ <span class="text-gray-700 font-medium w-[12.5%] text-center">h</span>
99
+ </div>
100
+ </div>
101
+ <div class="flex">
102
+ <div class="flex flex-col justify-between pr-2">
103
+ <span class="text-gray-700 font-medium h-[12.5%] flex items-center justify-center">8</span>
104
+ <span class="text-gray-700 font-medium h-[12.5%] flex items-center justify-center">7</span>
105
+ <span class="text-gray-700 font-medium h-[12.5%] flex items-center justify-center">6</span>
106
+ <span class="text-gray-700 font-medium h-[12.5%] flex items-center justify-center">5</span>
107
+ <span class="text-gray-700 font-medium h-[12.5%] flex items-center justify-center">4</span>
108
+ <span class="text-gray-700 font-medium h-[12.5%] flex items-center justify-center">3</span>
109
+ <span class="text-gray-700 font-medium h-[12.5%] flex items-center justify-center">2</span>
110
+ <span class="text-gray-700 font-medium h-[12.5%] flex items-center justify-center">1</span>
111
+ </div>
112
+ <div id="chess-board" class="chess-board mx-auto border-4 border-amber-800"></div>
113
+ </div>
114
+ </div>
115
+ <div class="mt-4 flex justify-between items-center">
116
+ <button id="undo-btn" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded">
117
+ <i class="fas fa-undo mr-2"></i>Undo
118
+ </button>
119
+ <div id="turn-indicator" class="text-lg font-semibold">
120
+ White's turn
121
+ </div>
122
+ <button id="new-game-btn" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded">
123
+ <i class="fas fa-plus mr-2"></i>New Game
124
+ </button>
125
+ </div>
126
+ </div>
127
+
128
+ <!-- Game Info Panel -->
129
+ <div class="bg-white p-6 rounded-lg shadow-xl w-full max-w-md">
130
+ <div class="mb-6">
131
+ <h2 class="text-2xl font-bold text-gray-800 mb-4">Move Suggestions</h2>
132
+ <div id="suggestions" class="bg-gray-50 p-4 rounded-lg min-h-32">
133
+ <p class="text-gray-600">Make your first move to get suggestions</p>
134
+ </div>
135
+ </div>
136
+
137
+ <div class="mb-6">
138
+ <h2 class="text-2xl font-bold text-gray-800 mb-4">Game Statistics</h2>
139
+ <div class="grid grid-cols-2 gap-4">
140
+ <div class="bg-blue-50 p-3 rounded-lg">
141
+ <p class="text-sm text-blue-600">White Pieces</p>
142
+ <p id="white-captures" class="text-xl font-bold">0 captured</p>
143
+ </div>
144
+ <div class="bg-gray-800 p-3 rounded-lg">
145
+ <p class="text-sm text-gray-300">Black Pieces</p>
146
+ <p id="black-captures" class="text-xl font-bold text-white">0 captured</p>
147
+ </div>
148
+ </div>
149
+ </div>
150
+
151
+ <div>
152
+ <h2 class="text-2xl font-bold text-gray-800 mb-4">Chess Tactics</h2>
153
+ <div id="tactics-info" class="bg-gray-50 p-4 rounded-lg">
154
+ <div class="mb-4">
155
+ <h3 class="font-semibold text-lg text-gray-700">Fork</h3>
156
+ <p class="text-gray-600 text-sm">A single piece attacks two or more opponent pieces simultaneously.</p>
157
+ </div>
158
+ <div class="mb-4">
159
+ <h3 class="font-semibold text-lg text-gray-700">Pin</h3>
160
+ <p class="text-gray-600 text-sm">A piece is restricted from moving because doing so would expose a more valuable piece to capture.</p>
161
+ </div>
162
+ <div>
163
+ <h3 class="font-semibold text-lg text-gray-700">Skewer</h3>
164
+ <p class="text-gray-600 text-sm">Similar to a pin, but the more valuable piece is in front of the less valuable one.</p>
165
+ </div>
166
+ </div>
167
+ <button id="next-tactic-btn" class="mt-4 w-full bg-purple-500 hover:bg-purple-600 text-white px-4 py-2 rounded">
168
+ <i class="fas fa-random mr-2"></i>Show Another Tactic
169
+ </button>
170
+ </div>
171
+ </div>
172
+ </div>
173
+
174
+ <!-- Move History -->
175
+ <div class="mt-8 bg-white p-6 rounded-lg shadow-xl max-w-4xl mx-auto">
176
+ <h2 class="text-2xl font-bold text-gray-800 mb-4">Move History</h2>
177
+ <div id="move-history" class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-2">
178
+ <!-- Moves will be added here -->
179
+ </div>
180
+ </div>
181
+ </div>
182
+
183
+ <script>
184
+ document.addEventListener('DOMContentLoaded', function() {
185
+ // Chess game state
186
+ const gameState = {
187
+ board: [],
188
+ selectedSquare: null,
189
+ possibleMoves: [],
190
+ turn: 'white',
191
+ moveHistory: [],
192
+ capturedPieces: { white: [], black: [] },
193
+ currentTacticIndex: 0
194
+ };
195
+
196
+ // Chess pieces Unicode characters
197
+ const pieces = {
198
+ king: { white: '♔', black: '♚' },
199
+ queen: { white: '♕', black: '♛' },
200
+ rook: { white: '♖', black: '♜' },
201
+ bishop: { white: '♗', black: '♝' },
202
+ knight: { white: '♘', black: '♞' },
203
+ pawn: { white: '♙', black: '♟' }
204
+ };
205
+
206
+ // Chess tactics information
207
+ const tactics = [
208
+ {
209
+ name: "Fork",
210
+ description: "A single piece attacks two or more opponent pieces simultaneously.",
211
+ example: "A knight attacking both the queen and rook at the same time."
212
+ },
213
+ {
214
+ name: "Pin",
215
+ description: "A piece is restricted from moving because doing so would expose a more valuable piece to capture.",
216
+ example: "A bishop pinning a knight to the king."
217
+ },
218
+ {
219
+ name: "Skewer",
220
+ description: "Similar to a pin, but the more valuable piece is in front of the less valuable one.",
221
+ example: "A rook skewering the queen with the king behind it."
222
+ },
223
+ {
224
+ name: "Discovered Attack",
225
+ description: "Moving a piece reveals an attack by another piece.",
226
+ example: "Moving a pawn to reveal a bishop's attack on the queen."
227
+ },
228
+ {
229
+ name: "Zwischenzug",
230
+ description: "An 'in-between move' that interrupts the expected sequence of play.",
231
+ example: "Instead of recapturing immediately, making a threatening move first."
232
+ },
233
+ {
234
+ name: "En Passant",
235
+ description: "A special pawn capture that can occur immediately after a pawn moves two squares.",
236
+ example: "Capturing a pawn that just moved two squares as if it had moved only one."
237
+ },
238
+ {
239
+ name: "Castling",
240
+ description: "A special move involving the king and a rook for defensive purposes.",
241
+ example: "Moving the king two squares toward a rook, then the rook to the other side."
242
+ }
243
+ ];
244
+
245
+ // Initialize the board
246
+ function initBoard() {
247
+ const boardElement = document.getElementById('chess-board');
248
+ boardElement.innerHTML = '';
249
+
250
+ // Create initial board setup
251
+ gameState.board = [
252
+ ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'],
253
+ ['p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'],
254
+ ['', '', '', '', '', '', '', ''],
255
+ ['', '', '', '', '', '', '', ''],
256
+ ['', '', '', '', '', '', '', ''],
257
+ ['', '', '', '', '', '', '', ''],
258
+ ['P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'],
259
+ ['R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R']
260
+ ];
261
+
262
+ // Create board squares
263
+ for (let row = 0; row < 8; row++) {
264
+ for (let col = 0; col < 8; col++) {
265
+ const square = document.createElement('div');
266
+ square.className = `square ${(row + col) % 2 === 0 ? 'light' : 'dark'}`;
267
+ square.dataset.row = row;
268
+ square.dataset.col = col;
269
+
270
+ const piece = gameState.board[row][col];
271
+ if (piece) {
272
+ const pieceElement = document.createElement('div');
273
+ pieceElement.className = `piece ${piece === piece.toLowerCase() ? 'black' : 'white'}`;
274
+
275
+ // Set the appropriate Unicode character
276
+ const pieceType = piece.toLowerCase();
277
+ const color = piece === piece.toLowerCase() ? 'black' : 'white';
278
+
279
+ if (pieceType === 'k') pieceElement.textContent = pieces.king[color];
280
+ else if (pieceType === 'q') pieceElement.textContent = pieces.queen[color];
281
+ else if (pieceType === 'r') pieceElement.textContent = pieces.rook[color];
282
+ else if (pieceType === 'b') pieceElement.textContent = pieces.bishop[color];
283
+ else if (pieceType === 'n') pieceElement.textContent = pieces.knight[color];
284
+ else if (pieceType === 'p') pieceElement.textContent = pieces.pawn[color];
285
+
286
+ square.appendChild(pieceElement);
287
+ }
288
+
289
+ square.addEventListener('click', () => handleSquareClick(row, col));
290
+ boardElement.appendChild(square);
291
+ }
292
+ }
293
+
294
+ // Reset game state
295
+ gameState.selectedSquare = null;
296
+ gameState.possibleMoves = [];
297
+ gameState.turn = 'white';
298
+ gameState.moveHistory = [];
299
+ gameState.capturedPieces = { white: [], black: [] };
300
+
301
+ // Update UI
302
+ updateTurnIndicator();
303
+ updateCapturedPieces();
304
+ clearMoveHistory();
305
+ updateSuggestions();
306
+ }
307
+
308
+ // Handle square click
309
+ function handleSquareClick(row, col) {
310
+ const piece = gameState.board[row][col];
311
+
312
+ // If no square is selected and the clicked square has a piece of the current turn's color
313
+ if (!gameState.selectedSquare && piece &&
314
+ ((gameState.turn === 'white' && piece === piece.toUpperCase()) ||
315
+ (gameState.turn === 'black' && piece === piece.toLowerCase()))) {
316
+
317
+ // Select the square
318
+ gameState.selectedSquare = { row, col };
319
+ highlightSquare(row, col, true);
320
+
321
+ // Calculate possible moves
322
+ gameState.possibleMoves = calculatePossibleMoves(row, col);
323
+ highlightPossibleMoves();
324
+
325
+ return;
326
+ }
327
+
328
+ // If a square is already selected
329
+ if (gameState.selectedSquare) {
330
+ const { row: selectedRow, col: selectedCol } = gameState.selectedSquare;
331
+
332
+ // Check if the clicked square is a possible move
333
+ const isPossibleMove = gameState.possibleMoves.some(move =>
334
+ move.row === row && move.col === col
335
+ );
336
+
337
+ if (isPossibleMove) {
338
+ // Move the piece
339
+ movePiece(selectedRow, selectedCol, row, col);
340
+ }
341
+
342
+ // Deselect the square
343
+ highlightSquare(selectedRow, selectedCol, false);
344
+ clearPossibleMoves();
345
+ gameState.selectedSquare = null;
346
+ gameState.possibleMoves = [];
347
+ }
348
+ }
349
+
350
+ // Calculate possible moves for a piece
351
+ function calculatePossibleMoves(row, col) {
352
+ const piece = gameState.board[row][col].toLowerCase();
353
+ const color = gameState.board[row][col] === gameState.board[row][col].toLowerCase() ? 'black' : 'white';
354
+ const moves = [];
355
+
356
+ // Pawn moves
357
+ if (piece === 'p') {
358
+ const direction = color === 'white' ? -1 : 1;
359
+ const startRow = color === 'white' ? 6 : 1;
360
+
361
+ // Forward move
362
+ if (row + direction >= 0 && row + direction < 8 &&
363
+ gameState.board[row + direction][col] === '') {
364
+ moves.push({ row: row + direction, col });
365
+
366
+ // Double move from starting position
367
+ if (row === startRow && gameState.board[row + 2 * direction][col] === '') {
368
+ moves.push({ row: row + 2 * direction, col });
369
+ }
370
+ }
371
+
372
+ // Captures
373
+ for (const captureCol of [col - 1, col + 1]) {
374
+ if (captureCol >= 0 && captureCol < 8 && row + direction >= 0 && row + direction < 8) {
375
+ const targetPiece = gameState.board[row + direction][captureCol];
376
+ if (targetPiece !== '' &&
377
+ ((color === 'white' && targetPiece === targetPiece.toLowerCase()) ||
378
+ (color === 'black' && targetPiece === targetPiece.toUpperCase()))) {
379
+ moves.push({ row: row + direction, col: captureCol });
380
+ }
381
+ }
382
+ }
383
+ }
384
+
385
+ // Rook moves (and queen's rook-like moves)
386
+ if (piece === 'r' || piece === 'q') {
387
+ // Horizontal and vertical moves
388
+ const directions = [[0, 1], [1, 0], [0, -1], [-1, 0]];
389
+ for (const [dr, dc] of directions) {
390
+ for (let i = 1; i < 8; i++) {
391
+ const newRow = row + i * dr;
392
+ const newCol = col + i * dc;
393
+
394
+ if (newRow < 0 || newRow >= 8 || newCol < 0 || newCol >= 8) break;
395
+
396
+ const targetPiece = gameState.board[newRow][newCol];
397
+ if (targetPiece === '') {
398
+ moves.push({ row: newRow, col: newCol });
399
+ } else {
400
+ if ((color === 'white' && targetPiece === targetPiece.toLowerCase()) ||
401
+ (color === 'black' && targetPiece === targetPiece.toUpperCase())) {
402
+ moves.push({ row: newRow, col: newCol });
403
+ }
404
+ break;
405
+ }
406
+ }
407
+ }
408
+ }
409
+
410
+ // Bishop moves (and queen's bishop-like moves)
411
+ if (piece === 'b' || piece === 'q') {
412
+ // Diagonal moves
413
+ const directions = [[1, 1], [1, -1], [-1, 1], [-1, -1]];
414
+ for (const [dr, dc] of directions) {
415
+ for (let i = 1; i < 8; i++) {
416
+ const newRow = row + i * dr;
417
+ const newCol = col + i * dc;
418
+
419
+ if (newRow < 0 || newRow >= 8 || newCol < 0 || newCol >= 8) break;
420
+
421
+ const targetPiece = gameState.board[newRow][newCol];
422
+ if (targetPiece === '') {
423
+ moves.push({ row: newRow, col: newCol });
424
+ } else {
425
+ if ((color === 'white' && targetPiece === targetPiece.toLowerCase()) ||
426
+ (color === 'black' && targetPiece === targetPiece.toUpperCase())) {
427
+ moves.push({ row: newRow, col: newCol });
428
+ }
429
+ break;
430
+ }
431
+ }
432
+ }
433
+ }
434
+
435
+ // Knight moves
436
+ if (piece === 'n') {
437
+ const knightMoves = [
438
+ [2, 1], [2, -1], [-2, 1], [-2, -1],
439
+ [1, 2], [1, -2], [-1, 2], [-1, -2]
440
+ ];
441
+
442
+ for (const [dr, dc] of knightMoves) {
443
+ const newRow = row + dr;
444
+ const newCol = col + dc;
445
+
446
+ if (newRow >= 0 && newRow < 8 && newCol >= 0 && newCol < 8) {
447
+ const targetPiece = gameState.board[newRow][newCol];
448
+ if (targetPiece === '' ||
449
+ ((color === 'white' && targetPiece === targetPiece.toLowerCase()) ||
450
+ (color === 'black' && targetPiece === targetPiece.toUpperCase()))) {
451
+ moves.push({ row: newRow, col: newCol });
452
+ }
453
+ }
454
+ }
455
+ }
456
+
457
+ // King moves
458
+ if (piece === 'k') {
459
+ for (let dr = -1; dr <= 1; dr++) {
460
+ for (let dc = -1; dc <= 1; dc++) {
461
+ if (dr === 0 && dc === 0) continue;
462
+
463
+ const newRow = row + dr;
464
+ const newCol = col + dc;
465
+
466
+ if (newRow >= 0 && newRow < 8 && newCol >= 0 && newCol < 8) {
467
+ const targetPiece = gameState.board[newRow][newCol];
468
+ if (targetPiece === '' ||
469
+ ((color === 'white' && targetPiece === targetPiece.toLowerCase()) ||
470
+ (color === 'black' && targetPiece === targetPiece.toUpperCase()))) {
471
+ moves.push({ row: newRow, col: newCol });
472
+ }
473
+ }
474
+ }
475
+ }
476
+
477
+ // Castling (simplified - doesn't check for checks or moved pieces)
478
+ if (row === (color === 'white' ? 7 : 0)) {
479
+ // Kingside
480
+ if (gameState.board[row][5] === '' && gameState.board[row][6] === '' &&
481
+ gameState.board[row][7].toLowerCase() === 'r') {
482
+ moves.push({ row, col: 6, isCastle: true, rookCol: 7, newRookCol: 5 });
483
+ }
484
+ // Queenside
485
+ if (gameState.board[row][3] === '' && gameState.board[row][2] === '' &&
486
+ gameState.board[row][1] === '' && gameState.board[row][0].toLowerCase() === 'r') {
487
+ moves.push({ row, col: 2, isCastle: true, rookCol: 0, newRookCol: 3 });
488
+ }
489
+ }
490
+ }
491
+
492
+ return moves;
493
+ }
494
+
495
+ // Move a piece
496
+ function movePiece(fromRow, fromCol, toRow, toCol) {
497
+ const piece = gameState.board[fromRow][fromCol];
498
+ const targetPiece = gameState.board[toRow][toCol];
499
+
500
+ // Record the move
501
+ const moveNotation = getMoveNotation(fromRow, fromCol, toRow, toCol);
502
+ gameState.moveHistory.push({
503
+ turn: gameState.turn,
504
+ from: { row: fromRow, col: fromCol },
505
+ to: { row: toRow, col: toCol },
506
+ piece,
507
+ captured: targetPiece,
508
+ notation: moveNotation
509
+ });
510
+
511
+ // Handle capture
512
+ if (targetPiece !== '') {
513
+ const capturedColor = targetPiece === targetPiece.toLowerCase() ? 'black' : 'white';
514
+ gameState.capturedPieces[capturedColor].push(targetPiece);
515
+ updateCapturedPieces();
516
+ }
517
+
518
+ // Handle castling
519
+ const move = gameState.possibleMoves.find(m => m.row === toRow && m.col === toCol);
520
+ if (move && move.isCastle) {
521
+ // Move the rook
522
+ gameState.board[toRow][move.newRookCol] = gameState.board[toRow][move.rookCol];
523
+ gameState.board[toRow][move.rookCol] = '';
524
+
525
+ // Update the board display for the rook
526
+ updateSquare(toRow, move.rookCol);
527
+ updateSquare(toRow, move.newRookCol);
528
+ }
529
+
530
+ // Move the piece
531
+ gameState.board[toRow][toCol] = piece;
532
+ gameState.board[fromRow][fromCol] = '';
533
+
534
+ // Update the board display
535
+ updateSquare(fromRow, fromCol);
536
+ updateSquare(toRow, toCol);
537
+
538
+ // Switch turns
539
+ gameState.turn = gameState.turn === 'white' ? 'black' : 'white';
540
+ updateTurnIndicator();
541
+
542
+ // Update move history
543
+ updateMoveHistory();
544
+
545
+ // Update suggestions
546
+ updateSuggestions();
547
+ }
548
+
549
+ // Update a square on the board
550
+ function updateSquare(row, col) {
551
+ const boardElement = document.getElementById('chess-board');
552
+ const squareIndex = row * 8 + col;
553
+ const square = boardElement.children[squareIndex];
554
+
555
+ // Clear the square
556
+ square.innerHTML = '';
557
+
558
+ // Set the appropriate class for the square
559
+ square.className = `square ${(row + col) % 2 === 0 ? 'light' : 'dark'}`;
560
+
561
+ // Add the piece if there is one
562
+ const piece = gameState.board[row][col];
563
+ if (piece) {
564
+ const pieceElement = document.createElement('div');
565
+ pieceElement.className = `piece ${piece === piece.toLowerCase() ? 'black' : 'white'}`;
566
+
567
+ // Set the appropriate Unicode character
568
+ const pieceType = piece.toLowerCase();
569
+ const color = piece === piece.toLowerCase() ? 'black' : 'white';
570
+
571
+ if (pieceType === 'k') pieceElement.textContent = pieces.king[color];
572
+ else if (pieceType === 'q') pieceElement.textContent = pieces.queen[color];
573
+ else if (pieceType === 'r') pieceElement.textContent = pieces.rook[color];
574
+ else if (pieceType === 'b') pieceElement.textContent = pieces.bishop[color];
575
+ else if (pieceType === 'n') pieceElement.textContent = pieces.knight[color];
576
+ else if (pieceType === 'p') pieceElement.textContent = pieces.pawn[color];
577
+
578
+ square.appendChild(pieceElement);
579
+ }
580
+
581
+ // Reattach the click handler
582
+ square.addEventListener('click', () => handleSquareClick(row, col));
583
+ }
584
+
585
+ // Highlight a square
586
+ function highlightSquare(row, col, highlight) {
587
+ const boardElement = document.getElementById('chess-board');
588
+ const squareIndex = row * 8 + col;
589
+ const square = boardElement.children[squareIndex];
590
+
591
+ if (highlight) {
592
+ square.classList.add('selected');
593
+ } else {
594
+ square.classList.remove('selected');
595
+ }
596
+ }
597
+
598
+ // Highlight possible moves
599
+ function highlightPossibleMoves() {
600
+ clearPossibleMoves();
601
+
602
+ for (const move of gameState.possibleMoves) {
603
+ const boardElement = document.getElementById('chess-board');
604
+ const squareIndex = move.row * 8 + move.col;
605
+ const square = boardElement.children[squareIndex];
606
+
607
+ const moveMarker = document.createElement('div');
608
+ moveMarker.className = 'possible-move';
609
+ square.appendChild(moveMarker);
610
+ }
611
+ }
612
+
613
+ // Clear possible move highlights
614
+ function clearPossibleMoves() {
615
+ const boardElement = document.getElementById('chess-board');
616
+ const moveMarkers = boardElement.querySelectorAll('.possible-move');
617
+
618
+ for (const marker of moveMarkers) {
619
+ marker.remove();
620
+ }
621
+ }
622
+
623
+ // Update turn indicator
624
+ function updateTurnIndicator() {
625
+ const turnIndicator = document.getElementById('turn-indicator');
626
+ turnIndicator.textContent = `${gameState.turn.charAt(0).toUpperCase() + gameState.turn.slice(1)}'s turn`;
627
+ turnIndicator.className = `text-lg font-semibold ${gameState.turn === 'white' ? 'text-gray-800' : 'text-gray-800'}`;
628
+ }
629
+
630
+ // Update captured pieces display
631
+ function updateCapturedPieces() {
632
+ const whiteCaptures = document.getElementById('white-captures');
633
+ const blackCaptures = document.getElementById('black-captures');
634
+
635
+ whiteCaptures.textContent = `${gameState.capturedPieces.black.length} captured`;
636
+ blackCaptures.textContent = `${gameState.capturedPieces.white.length} captured`;
637
+ }
638
+
639
+ // Update move history
640
+ function updateMoveHistory() {
641
+ const moveHistoryElement = document.getElementById('move-history');
642
+
643
+ // Clear existing moves
644
+ moveHistoryElement.innerHTML = '';
645
+
646
+ // Add moves in pairs (white and black)
647
+ for (let i = 0; i < gameState.moveHistory.length; i += 2) {
648
+ const whiteMove = gameState.moveHistory[i];
649
+ const blackMove = gameState.moveHistory[i + 1];
650
+
651
+ const movePair = document.createElement('div');
652
+ movePair.className = 'bg-gray-100 p-2 rounded flex items-center justify-between';
653
+
654
+ const moveNumber = document.createElement('span');
655
+ moveNumber.className = 'text-gray-500 text-sm';
656
+ moveNumber.textContent = `${Math.floor(i / 2) + 1}.`;
657
+
658
+ const whiteMoveSpan = document.createElement('span');
659
+ whiteMoveSpan.className = 'font-medium';
660
+ whiteMoveSpan.textContent = whiteMove.notation;
661
+
662
+ movePair.appendChild(moveNumber);
663
+ movePair.appendChild(whiteMoveSpan);
664
+
665
+ if (blackMove) {
666
+ const blackMoveSpan = document.createElement('span');
667
+ blackMoveSpan.className = 'font-medium';
668
+ blackMoveSpan.textContent = blackMove.notation;
669
+ movePair.appendChild(blackMoveSpan);
670
+ }
671
+
672
+ moveHistoryElement.appendChild(movePair);
673
+ }
674
+ }
675
+
676
+ // Clear move history
677
+ function clearMoveHistory() {
678
+ const moveHistoryElement = document.getElementById('move-history');
679
+ moveHistoryElement.innerHTML = '';
680
+ }
681
+
682
+ // Get algebraic notation for a move
683
+ function getMoveNotation(fromRow, fromCol, toRow, toCol) {
684
+ const piece = gameState.board[fromRow][fromCol].toLowerCase();
685
+ const file = String.fromCharCode(97 + fromCol);
686
+ const rank = 8 - fromRow;
687
+ const capture = gameState.board[toRow][toCol] !== '' ? 'x' : '';
688
+ const destFile = String.fromCharCode(97 + toCol);
689
+ const destRank = 8 - toRow;
690
+
691
+ // For pawns
692
+ if (piece === 'p') {
693
+ if (capture) {
694
+ return `${file}${capture}${destFile}${destRank}`;
695
+ } else {
696
+ return `${destFile}${destRank}`;
697
+ }
698
+ }
699
+
700
+ // For other pieces
701
+ const pieceSymbol = piece === 'n' ? 'N' :
702
+ piece === 'b' ? 'B' :
703
+ piece === 'r' ? 'R' :
704
+ piece === 'q' ? 'Q' :
705
+ piece === 'k' ? 'K' : '';
706
+
707
+ return `${pieceSymbol}${capture}${destFile}${destRank}`;
708
+ }
709
+
710
+ // Update move suggestions
711
+ function updateSuggestions() {
712
+ const suggestionsElement = document.getElementById('suggestions');
713
+
714
+ if (gameState.moveHistory.length === 0) {
715
+ suggestionsElement.innerHTML = `
716
+ <p class="text-gray-600">Suggested first moves:</p>
717
+ <ul class="mt-2 ml-4 list-disc">
718
+ <li>e4 (King's Pawn)</li>
719
+ <li>d4 (Queen's Pawn)</li>
720
+ <li>Nf3 (King's Knight)</li>
721
+ </ul>
722
+ `;
723
+ return;
724
+ }
725
+
726
+ // Simple suggestion logic (in a real app, this would use a chess engine)
727
+ const lastMove = gameState.moveHistory[gameState.moveHistory.length - 1];
728
+ const color = gameState.turn;
729
+
730
+ // Find all pieces of the current color
731
+ const pieces = [];
732
+ for (let row = 0; row < 8; row++) {
733
+ for (let col = 0; col < 8; col++) {
734
+ const piece = gameState.board[row][col];
735
+ if (piece &&
736
+ ((color === 'white' && piece === piece.toUpperCase()) ||
737
+ (color === 'black' && piece === piece.toLowerCase()))) {
738
+ pieces.push({ row, col, piece });
739
+ }
740
+ }
741
+ }
742
+
743
+ // Find possible moves for all pieces
744
+ const allMoves = [];
745
+ for (const piece of pieces) {
746
+ const moves = calculatePossibleMoves(piece.row, piece.col);
747
+ for (const move of moves) {
748
+ allMoves.push({
749
+ from: { row: piece.row, col: piece.col },
750
+ to: { row: move.row, col: move.col },
751
+ piece: piece.piece
752
+ });
753
+ }
754
+ }
755
+
756
+ // Filter for captures
757
+ const captures = allMoves.filter(move =>
758
+ gameState.board[move.to.row][move.to.col] !== ''
759
+ );
760
+
761
+ // Filter for checks (simplified)
762
+ const checks = allMoves.filter(move => {
763
+ // Simulate the move
764
+ const originalPiece = gameState.board[move.to.row][move.to.col];
765
+ gameState.board[move.to.row][move.to.col] = move.piece;
766
+ gameState.board[move.from.row][move.from.col] = '';
767
+
768
+ // Check if the opponent's king is under attack
769
+ let kingFound = false;
770
+ for (let row = 0; row < 8; row++) {
771
+ for (let col = 0; col < 8; col++) {
772
+ const piece = gameState.board[row][col];
773
+ if (piece.toLowerCase() === 'k' &&
774
+ ((color === 'white' && piece === piece.toLowerCase()) ||
775
+ (color === 'black' && piece === piece.toUpperCase()))) {
776
+ kingFound = true;
777
+ break;
778
+ }
779
+ }
780
+ }
781
+
782
+ // Undo the move
783
+ gameState.board[move.from.row][move.from.col] = move.piece;
784
+ gameState.board[move.to.row][move.to.col] = originalPiece;
785
+
786
+ return kingFound;
787
+ });
788
+
789
+ // Generate suggestions
790
+ let suggestionsHTML = '';
791
+
792
+ if (checks.length > 0) {
793
+ suggestionsHTML += `<p class="text-green-600 font-medium">Check opportunities:</p>`;
794
+ suggestionsHTML += `<ul class="mt-1 ml-4 list-disc">`;
795
+ for (let i = 0; i < Math.min(2, checks.length); i++) {
796
+ const move = checks[i];
797
+ const notation = getMoveNotation(move.from.row, move.from.col, move.to.row, move.to.col);
798
+ suggestionsHTML += `<li>${notation}</li>`;
799
+ }
800
+ suggestionsHTML += `</ul>`;
801
+ }
802
+
803
+ if (captures.length > 0) {
804
+ suggestionsHTML += `<p class="mt-2 text-blue-600 font-medium">Capture opportunities:</p>`;
805
+ suggestionsHTML += `<ul class="mt-1 ml-4 list-disc">`;
806
+ for (let i = 0; i < Math.min(3, captures.length); i++) {
807
+ const move = captures[i];
808
+ const notation = getMoveNotation(move.from.row, move.from.col, move.to.row, move.to.col);
809
+ suggestionsHTML += `<li>${notation}</li>`;
810
+ }
811
+ suggestionsHTML += `</ul>`;
812
+ }
813
+
814
+ if (suggestionsHTML === '') {
815
+ // Suggest developing pieces
816
+ const developingMoves = allMoves.filter(move => {
817
+ const piece = move.piece.toLowerCase();
818
+ return (piece === 'n' || piece === 'b') &&
819
+ gameState.board[move.to.row][move.to.col] === '';
820
+ });
821
+
822
+ if (developingMoves.length > 0) {
823
+ suggestionsHTML += `<p class="text-purple-600 font-medium">Develop your pieces:</p>`;
824
+ suggestionsHTML += `<ul class="mt-1 ml-4 list-disc">`;
825
+ for (let i = 0; i < Math.min(3, developingMoves.length); i++) {
826
+ const move = developingMoves[i];
827
+ const notation = getMoveNotation(move.from.row, move.from.col, move.to.row, move.to.col);
828
+ suggestionsHTML += `<li>${notation}</li>`;
829
+ }
830
+ suggestionsHTML += `</ul>`;
831
+ } else {
832
+ suggestionsHTML = `<p class="text-gray-600">No obvious suggestions. Consider improving your position.</p>`;
833
+ }
834
+ }
835
+
836
+ suggestionsElement.innerHTML = suggestionsHTML;
837
+ }
838
+
839
+ // Show next tactic
840
+ function showNextTactic() {
841
+ gameState.currentTacticIndex = (gameState.currentTacticIndex + 1) % tactics.length;
842
+ const tactic = tactics[gameState.currentTacticIndex];
843
+
844
+ const tacticsInfo = document.getElementById('tactics-info');
845
+ tacticsInfo.innerHTML = `
846
+ <div class="mb-4">
847
+ <h3 class="font-semibold text-lg text-gray-700">${tactic.name}</h3>
848
+ <p class="text-gray-600 text-sm">${tactic.description}</p>
849
+ <p class="text-gray-500 text-xs mt-1 italic">Example: ${tactic.example}</p>
850
+ </div>
851
+ `;
852
+ }
853
+
854
+ // Undo last move
855
+ function undoLastMove() {
856
+ if (gameState.moveHistory.length === 0) return;
857
+
858
+ const lastMove = gameState.moveHistory.pop();
859
+
860
+ // Move the piece back
861
+ gameState.board[lastMove.from.row][lastMove.from.col] = lastMove.piece;
862
+ gameState.board[lastMove.to.row][lastMove.to.col] = lastMove.captured || '';
863
+
864
+ // Handle castling
865
+ if (lastMove.notation.includes('O-O')) {
866
+ const rookCol = lastMove.to.col > lastMove.from.col ? 5 : 3;
867
+ const originalRookCol = lastMove.to.col > lastMove.from.col ? 7 : 0;
868
+
869
+ gameState.board[lastMove.from.row][originalRookCol] =
870
+ lastMove.piece === 'K' ? 'R' : 'r';
871
+ gameState.board[lastMove.from.row][rookCol] = '';
872
+
873
+ updateSquare(lastMove.from.row, rookCol);
874
+ updateSquare(lastMove.from.row, originalRookCol);
875
+ }
876
+
877
+ // Update captured pieces if needed
878
+ if (lastMove.captured) {
879
+ const capturedColor = lastMove.captured === lastMove.captured.toLowerCase() ? 'black' : 'white';
880
+ gameState.capturedPieces[capturedColor].pop();
881
+ updateCapturedPieces();
882
+ }
883
+
884
+ // Update the board display
885
+ updateSquare(lastMove.from.row, lastMove.from.col);
886
+ updateSquare(lastMove.to.row, lastMove.to.col);
887
+
888
+ // Switch turns back
889
+ gameState.turn = lastMove.turn;
890
+ updateTurnIndicator();
891
+
892
+ // Update move history
893
+ updateMoveHistory();
894
+
895
+ // Update suggestions
896
+ updateSuggestions();
897
+ }
898
+
899
+ // Event listeners
900
+ document.getElementById('new-game-btn').addEventListener('click', initBoard);
901
+ document.getElementById('next-tactic-btn').addEventListener('click', showNextTactic);
902
+ document.getElementById('undo-btn').addEventListener('click', undoLastMove);
903
+
904
+ // Initialize the board
905
+ initBoard();
906
+ showNextTactic();
907
+ });
908
+ </script>
909
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=AC-Angelo93/chess-tutor" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
910
+ </html>