sekopst commited on
Commit
d7f47c6
·
verified ·
1 Parent(s): 73539c5

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +1343 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Serkanspace
3
- emoji: 👁
4
- colorFrom: yellow
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: serkanspace
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: blue
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,1343 @@
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>Premium Sudoku Pro</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
+ /* Custom styles for Sudoku board */
11
+ .cell {
12
+ width: 50px;
13
+ height: 50px;
14
+ font-size: 1.5rem;
15
+ text-align: center;
16
+ border: 1px solid #cbd5e0;
17
+ position: relative;
18
+ transition: all 0.2s ease;
19
+ }
20
+
21
+ .cell:focus {
22
+ outline: none;
23
+ background-color: #ebf8ff;
24
+ }
25
+
26
+ .cell:nth-child(3n) {
27
+ border-right: 2px solid #4a5568;
28
+ }
29
+
30
+ .cell:nth-child(9n) {
31
+ border-right: 1px solid #cbd5e0;
32
+ }
33
+
34
+ .row:nth-child(3n) .cell {
35
+ border-bottom: 2px solid #4a5568;
36
+ }
37
+
38
+ .row:nth-child(9n) .cell {
39
+ border-bottom: 1px solid #cbd5e0;
40
+ }
41
+
42
+ .given {
43
+ font-weight: bold;
44
+ color: #2d3748;
45
+ background-color: #f7fafc;
46
+ }
47
+
48
+ .error {
49
+ color: #e53e3e;
50
+ animation: shake 0.5s;
51
+ }
52
+
53
+ @keyframes shake {
54
+ 0%, 100% { transform: translateX(0); }
55
+ 10%, 30%, 50%, 70%, 90% { transform: translateX(-2px); }
56
+ 20%, 40%, 60%, 80% { transform: translateX(2px); }
57
+ }
58
+
59
+ .highlight {
60
+ background-color: #bee3f8;
61
+ }
62
+
63
+ .selected {
64
+ background-color: #90cdf4;
65
+ }
66
+
67
+ .same-number {
68
+ background-color: #d6bcfa;
69
+ }
70
+
71
+ .peers {
72
+ background-color: #e9d8fd;
73
+ }
74
+
75
+ .conflict {
76
+ background-color: #fed7d7;
77
+ }
78
+
79
+ .notes-grid {
80
+ display: grid;
81
+ grid-template-columns: repeat(3, 1fr);
82
+ grid-template-rows: repeat(3, 1fr);
83
+ width: 100%;
84
+ height: 100%;
85
+ position: absolute;
86
+ top: 0;
87
+ left: 0;
88
+ font-size: 0.7rem;
89
+ color: #6b46c1;
90
+ }
91
+
92
+ .notes-corner {
93
+ position: absolute;
94
+ font-size: 0.7rem;
95
+ color: #6b46c1;
96
+ }
97
+
98
+ .corner-number {
99
+ position: absolute;
100
+ width: 10px;
101
+ height: 10px;
102
+ }
103
+
104
+ .cell-value {
105
+ z-index: 2;
106
+ position: relative;
107
+ }
108
+
109
+ .highlight-mode {
110
+ box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
111
+ }
112
+
113
+ .stats-bar {
114
+ height: 6px;
115
+ border-radius: 3px;
116
+ }
117
+
118
+ @media (max-width: 640px) {
119
+ .cell {
120
+ width: 35px;
121
+ height: 35px;
122
+ font-size: 1.2rem;
123
+ }
124
+
125
+ .notes-grid {
126
+ font-size: 0.5rem;
127
+ }
128
+
129
+ .notes-corner {
130
+ font-size: 0.5rem;
131
+ }
132
+ }
133
+ </style>
134
+ </head>
135
+ <body class="bg-gray-100 min-h-screen flex flex-col items-center justify-center p-4">
136
+ <div class="max-w-6xl w-full bg-white rounded-xl shadow-xl p-6">
137
+ <header class="text-center mb-6">
138
+ <h1 class="text-4xl font-bold text-indigo-700 mb-2 flex items-center justify-center">
139
+ <i class="fas fa-th mr-3"></i> Premium Sudoku Pro
140
+ </h1>
141
+ <div class="flex flex-wrap justify-center items-center gap-4">
142
+ <div class="flex items-center">
143
+ <span class="mr-2 text-gray-700 font-medium">Difficulty:</span>
144
+ <select id="difficulty" class="border rounded px-3 py-1 bg-white shadow-sm">
145
+ <option value="easy">Easy</option>
146
+ <option value="medium">Medium</option>
147
+ <option value="hard">Hard</option>
148
+ <option value="expert">Expert</option>
149
+ <option value="insane">Insane</option>
150
+ </select>
151
+ </div>
152
+ <button id="new-game" class="bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700 transition flex items-center shadow-md">
153
+ <i class="fas fa-sync-alt mr-2"></i>New Game
154
+ </button>
155
+ <div class="flex items-center bg-indigo-50 px-3 py-1 rounded-lg">
156
+ <span class="mr-2 text-gray-700 font-medium">Theme:</span>
157
+ <button id="theme-light" class="w-6 h-6 rounded-full bg-white border-2 border-gray-300 mx-1 shadow-sm"></button>
158
+ <button id="theme-dark" class="w-6 h-6 rounded-full bg-gray-800 border-2 border-gray-700 mx-1 shadow-sm"></button>
159
+ <button id="theme-blue" class="w-6 h-6 rounded-full bg-blue-500 border-2 border-blue-700 mx-1 shadow-sm"></button>
160
+ </div>
161
+ <button id="fullscreen-btn" class="text-gray-700 hover:text-indigo-700 p-2 rounded-lg hover:bg-gray-100 transition">
162
+ <i class="fas fa-expand"></i>
163
+ </button>
164
+ </div>
165
+ </header>
166
+
167
+ <div class="flex flex-col lg:flex-row items-center lg:items-start justify-center gap-8">
168
+ <div class="flex-1">
169
+ <div class="bg-white p-3 rounded-xl border-2 border-gray-300 shadow-inner relative">
170
+ <div id="board" class="grid grid-cols-9 gap-0"></div>
171
+ <div class="absolute top-2 right-2 flex space-x-2">
172
+ <button id="undo-btn" class="text-gray-500 hover:text-indigo-700" title="Undo">
173
+ <i class="fas fa-undo"></i>
174
+ </button>
175
+ <button id="redo-btn" class="text-gray-500 hover:text-indigo-700" title="Redo">
176
+ <i class="fas fa-redo"></i>
177
+ </button>
178
+ </div>
179
+ </div>
180
+
181
+ <div class="mt-4 grid grid-cols-3 gap-4">
182
+ <div class="bg-gray-50 p-3 rounded-lg shadow text-center">
183
+ <div class="text-sm text-gray-600 mb-1">Time</div>
184
+ <div id="timer" class="font-mono text-xl font-bold">00:00</div>
185
+ </div>
186
+ <div class="bg-gray-50 p-3 rounded-lg shadow text-center">
187
+ <div class="text-sm text-gray-600 mb-1">Mistakes</div>
188
+ <div id="mistakes" class="text-xl font-bold">0/3</div>
189
+ <div class="stats-bar bg-gray-200 mt-1">
190
+ <div id="mistakes-bar" class="stats-bar bg-red-500 h-full w-0"></div>
191
+ </div>
192
+ </div>
193
+ <div class="bg-gray-50 p-3 rounded-lg shadow text-center">
194
+ <div class="text-sm text-gray-600 mb-1">Hints</div>
195
+ <div id="hints" class="text-xl font-bold">3</div>
196
+ <div class="stats-bar bg-gray-200 mt-1">
197
+ <div id="hints-bar" class="stats-bar bg-green-500 h-full w-full"></div>
198
+ </div>
199
+ </div>
200
+ </div>
201
+ </div>
202
+
203
+ <div class="flex flex-col space-y-6 w-full lg:w-80">
204
+ <div class="bg-gray-50 p-5 rounded-xl shadow-lg">
205
+ <h2 class="text-xl font-semibold text-gray-800 mb-3 flex items-center">
206
+ <i class="fas fa-gamepad mr-2"></i> Controls
207
+ </h2>
208
+ <div class="grid grid-cols-3 gap-2 mb-4">
209
+ <button class="number-btn bg-indigo-100 hover:bg-indigo-200 text-indigo-800 font-bold py-4 rounded-lg transition transform hover:scale-105 active:scale-95 shadow-sm" data-number="1">1</button>
210
+ <button class="number-btn bg-indigo-100 hover:bg-indigo-200 text-indigo-800 font-bold py-4 rounded-lg transition transform hover:scale-105 active:scale-95 shadow-sm" data-number="2">2</button>
211
+ <button class="number-btn bg-indigo-100 hover:bg-indigo-200 text-indigo-800 font-bold py-4 rounded-lg transition transform hover:scale-105 active:scale-95 shadow-sm" data-number="3">3</button>
212
+ <button class="number-btn bg-indigo-100 hover:bg-indigo-200 text-indigo-800 font-bold py-4 rounded-lg transition transform hover:scale-105 active:scale-95 shadow-sm" data-number="4">4</button>
213
+ <button class="number-btn bg-indigo-100 hover:bg-indigo-200 text-indigo-800 font-bold py-4 rounded-lg transition transform hover:scale-105 active:scale-95 shadow-sm" data-number="5">5</button>
214
+ <button class="number-btn bg-indigo-100 hover:bg-indigo-200 text-indigo-800 font-bold py-4 rounded-lg transition transform hover:scale-105 active:scale-95 shadow-sm" data-number="6">6</button>
215
+ <button class="number-btn bg-indigo-100 hover:bg-indigo-200 text-indigo-800 font-bold py-4 rounded-lg transition transform hover:scale-105 active:scale-95 shadow-sm" data-number="7">7</button>
216
+ <button class="number-btn bg-indigo-100 hover:bg-indigo-200 text-indigo-800 font-bold py-4 rounded-lg transition transform hover:scale-105 active:scale-95 shadow-sm" data-number="8">8</button>
217
+ <button class="number-btn bg-indigo-100 hover:bg-indigo-200 text-indigo-800 font-bold py-4 rounded-lg transition transform hover:scale-105 active:scale-95 shadow-sm" data-number="9">9</button>
218
+ </div>
219
+ <div class="flex space-x-3">
220
+ <button id="erase-btn" class="flex-1 bg-red-100 hover:bg-red-200 text-red-800 font-bold py-3 rounded-lg transition flex items-center justify-center shadow-sm">
221
+ <i class="fas fa-eraser mr-2"></i>Erase
222
+ </button>
223
+ <button id="notes-btn" class="flex-1 bg-yellow-100 hover:bg-yellow-200 text-yellow-800 font-bold py-3 rounded-lg transition flex items-center justify-center shadow-sm">
224
+ <i class="fas fa-pencil-alt mr-2"></i>Notes
225
+ </button>
226
+ <button id="hint-btn" class="flex-1 bg-green-100 hover:bg-green-200 text-green-800 font-bold py-3 rounded-lg transition flex items-center justify-center shadow-sm">
227
+ <i class="fas fa-lightbulb mr-2"></i>Hint
228
+ </button>
229
+ </div>
230
+
231
+ <div class="mt-4 pt-3 border-t border-gray-200">
232
+ <h3 class="text-sm font-medium text-gray-700 mb-2">Notes Style:</h3>
233
+ <div class="flex space-x-2">
234
+ <button data-note-style="grid" class="note-style-btn px-2 py-1 text-xs rounded border bg-white hover:bg-gray-50 transition active:bg-gray-100">
235
+ Grid
236
+ </button>
237
+ <button data-note-style="corner" class="note-style-btn px-2 py-1 text-xs rounded border bg-white hover:bg-gray-50 transition active:bg-gray-100">
238
+ Corners
239
+ </button>
240
+ <button data-note-style="hidden" class="note-style-btn px-2 py-1 text-xs rounded border bg-white hover:bg-gray-50 transition active:bg-gray-100">
241
+ Hidden
242
+ </button>
243
+ </div>
244
+ </div>
245
+
246
+ <div class="mt-3 pt-3 border-t border-gray-200">
247
+ <h3 class="text-sm font-medium text-gray-700 mb-2">Highlight:</h3>
248
+ <div class="flex flex-wrap gap-2">
249
+ <button data-highlight="number" class="highlight-btn px-2 py-1 text-xs rounded border bg-white hover:bg-gray-50 transition active:bg-gray-100">
250
+ Number
251
+ </button>
252
+ <button data-highlight="peers" class="highlight-btn px-2 py-1 text-xs rounded border bg-white hover:bg-gray-50 transition active:bg-gray-100">
253
+ Peers
254
+ </button>
255
+ <button data-highlight="conflicts" class="highlight-btn px-2 py-1 text-xs rounded border bg-white hover:bg-gray-50 transition active:bg-gray-100">
256
+ Conflicts
257
+ </button>
258
+ </div>
259
+ </div>
260
+ </div>
261
+
262
+ <div class="bg-gray-50 p-5 rounded-xl shadow-lg">
263
+ <h2 class="text-xl font-semibold text-gray-800 mb-3 flex items-center">
264
+ <i class="fas fa-chart-line mr-2"></i> Statistics
265
+ </h2>
266
+ <div class="space-y-3">
267
+ <div>
268
+ <div class="flex justify-between text-sm text-gray-600 mb-1">
269
+ <span>Games Played:</span>
270
+ <span id="games-played">0</span>
271
+ </div>
272
+ <div class="stats-bar bg-gray-200">
273
+ <div id="games-played-bar" class="stats-bar bg-indigo-500 h-full w-0"></div>
274
+ </div>
275
+ </div>
276
+ <div>
277
+ <div class="flex justify-between text-sm text-gray-600 mb-1">
278
+ <span>Win Rate:</span>
279
+ <span id="win-rate">0%</span>
280
+ </div>
281
+ <div class="stats-bar bg-gray-200">
282
+ <div id="win-rate-bar" class="stats-bar bg-green-500 h-full w-0"></div>
283
+ </div>
284
+ </div>
285
+ <div>
286
+ <div class="flex justify-between text-sm text-gray-600 mb-1">
287
+ <span>Average Time:</span>
288
+ <span id="average-time">00:00</span>
289
+ </div>
290
+ <div class="stats-bar bg-gray-200">
291
+ <div id="average-time-bar" class="stats-bar bg-blue-500 h-full w-0"></div>
292
+ </div>
293
+ </div>
294
+ <div>
295
+ <div class="flex justify-between text-sm text-gray-600 mb-1">
296
+ <span>Best Time:</span>
297
+ <span id="best-time">--:--</span>
298
+ </div>
299
+ <div class="stats-bar bg-gray-200">
300
+ <div id="best-time-bar" class="stats-bar bg-yellow-500 h-full w-0"></div>
301
+ </div>
302
+ </div>
303
+ </div>
304
+ </div>
305
+ </div>
306
+ </div>
307
+
308
+ <div id="message" class="mt-6 text-center hidden">
309
+ <div id="message-content" class="inline-block px-8 py-4 rounded-xl font-bold text-white shadow-lg"></div>
310
+ </div>
311
+
312
+ <div id="help-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 hidden z-50">
313
+ <div class="bg-white rounded-xl shadow-2xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
314
+ <div class="p-6">
315
+ <div class="flex justify-between items-center mb-4">
316
+ <h2 class="text-2xl font-bold text-indigo-700">Sudoku Pro Help</h2>
317
+ <button id="close-help" class="text-gray-500 hover:text-gray-700">
318
+ <i class="fas fa-times"></i>
319
+ </button>
320
+ </div>
321
+
322
+ <div class="space-y-4">
323
+ <div>
324
+ <h3 class="text-lg font-semibold mb-2">How to Play</h3>
325
+ <p class="text-gray-700">
326
+ Fill the 9×9 grid with digits so that each column, each row, and each of the nine 3×3 subgrids
327
+ that compose the grid (also called "boxes", "blocks", or "regions") contains all of the digits from 1 to 9.
328
+ </p>
329
+ </div>
330
+
331
+ <div>
332
+ <h3 class="text-lg font-semibold mb-2">Controls</h3>
333
+ <ul class="list-disc pl-5 space-y-2 text-gray-700">
334
+ <li><strong>Number Buttons</strong>: Fill the selected cell</li>
335
+ <li><strong>Notes Mode</strong>: Toggle to enter small candidate numbers</li>
336
+ <li><strong>Erase</strong>: Clear the selected cell</li>
337
+ <li><strong>Hint</strong>: Reveals the correct number (limited)</li>
338
+ <li><strong>Undo/Redo</strong>: Revert or replay your moves</li>
339
+ </ul>
340
+ </div>
341
+
342
+ <div>
343
+ <h3 class="text-lg font-semibold mb-2">Advanced Features</h3>
344
+ <ul class="list-disc pl-5 space-y-2 text-gray-700">
345
+ <li><strong>Notes Styles</strong>: Choose between grid, corner, or hidden notes</li>
346
+ <li><strong>Highlighting</strong>: Visualize number conflicts and relationships</li>
347
+ <li><strong>Statistics</strong>: Track your performance over time</li>
348
+ <li><strong>Multiple Themes</strong>: Light, dark, and blue color schemes</li>
349
+ </ul>
350
+ </div>
351
+ </div>
352
+
353
+ <div class="mt-6 pt-4 border-t border-gray-200 flex justify-end">
354
+ <button id="ok-help" class="bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700 transition">
355
+ Got It!
356
+ </button>
357
+ </div>
358
+ </div>
359
+ </div>
360
+ </div>
361
+ </div>
362
+
363
+ <div class="absolute bottom-4 right-4">
364
+ <button id="help-btn" class="w-12 h-12 bg-indigo-600 text-white rounded-full shadow-lg hover:bg-indigo-700 transition flex items-center justify-center">
365
+ <i class="fas fa-question text-xl"></i>
366
+ </button>
367
+ </div>
368
+
369
+ <script>
370
+ document.addEventListener('DOMContentLoaded', () => {
371
+ // Enhanced game state
372
+ const gameState = {
373
+ board: Array(9).fill().map(() => Array(9).fill(0)),
374
+ solution: Array(9).fill().map(() => Array(9).fill(0)),
375
+ givenCells: Array(9).fill().map(() => Array(9).fill(false)),
376
+ notes: Array(9).fill().map(() => Array(9).fill().map(() => new Set())),
377
+ selectedCell: null,
378
+ notesMode: false,
379
+ noteStyle: 'grid', // 'grid' or 'corner' or 'hidden'
380
+ highlightMode: 'number', // 'number' or 'peers' or 'conflicts'
381
+ mistakes: 0,
382
+ hints: 3,
383
+ timer: 0,
384
+ timerInterval: null,
385
+ gameOver: false,
386
+ movesHistory: [],
387
+ historyPointer: -1,
388
+ stats: {
389
+ gamesPlayed: 0,
390
+ gamesWon: 0,
391
+ totalTime: 0,
392
+ bestTime: null,
393
+ difficultyStats: {
394
+ easy: { played: 0, won: 0, bestTime: null },
395
+ medium: { played: 0, won: 0, bestTime: null },
396
+ hard: { played: 0, won: 0, bestTime: null },
397
+ expert: { played: 0, won: 0, bestTime: null },
398
+ insane: { played: 0, won: 0, bestTime: null }
399
+ }
400
+ },
401
+ currentTheme: 'light'
402
+ };
403
+
404
+ // DOM elements
405
+ const boardElement = document.getElementById('board');
406
+ const numberButtons = document.querySelectorAll('.number-btn');
407
+ const eraseButton = document.getElementById('erase-btn');
408
+ const notesButton = document.getElementById('notes-btn');
409
+ const hintButton = document.getElementById('hint-btn');
410
+ const newGameButton = document.getElementById('new-game');
411
+ const difficultySelect = document.getElementById('difficulty');
412
+ const timerElement = document.getElementById('timer');
413
+ const mistakesElement = document.getElementById('mistakes');
414
+ const hintsElement = document.getElementById('hints');
415
+ const messageElement = document.getElementById('message');
416
+ const messageContent = document.getElementById('message-content');
417
+ const noteStyleButtons = document.querySelectorAll('.note-style-btn');
418
+ const highlightButtons = document.querySelectorAll('.highlight-btn');
419
+ const undoButton = document.getElementById('undo-btn');
420
+ const redoButton = document.getElementById('redo-btn');
421
+ const statsElements = {
422
+ gamesPlayed: document.getElementById('games-played'),
423
+ winRate: document.getElementById('win-rate'),
424
+ averageTime: document.getElementById('average-time'),
425
+ bestTime: document.getElementById('best-time')
426
+ };
427
+ const statBars = {
428
+ gamesPlayed: document.getElementById('games-played-bar'),
429
+ winRate: document.getElementById('win-rate-bar'),
430
+ averageTime: document.getElementById('average-time-bar'),
431
+ bestTime: document.getElementById('best-time-bar'),
432
+ mistakes: document.getElementById('mistakes-bar'),
433
+ hints: document.getElementById('hints-bar')
434
+ };
435
+ const helpButton = document.getElementById('help-btn');
436
+ const helpModal = document.getElementById('help-modal');
437
+ const closeHelpButton = document.getElementById('close-help');
438
+ const okHelpButton = document.getElementById('ok-help');
439
+ const themeButtons = {
440
+ light: document.getElementById('theme-light'),
441
+ dark: document.getElementById('theme-dark'),
442
+ blue: document.getElementById('theme-blue')
443
+ };
444
+ const fullscreenButton = document.getElementById('fullscreen-btn');
445
+
446
+ // Initialize the game
447
+ initGame();
448
+
449
+ // Load statistics from localStorage
450
+ loadStatistics();
451
+
452
+ // Update statistics display
453
+ updateStatisticsDisplay();
454
+
455
+ // Event listeners
456
+ numberButtons.forEach(button => {
457
+ button.addEventListener('click', () => {
458
+ if (gameState.selectedCell && !gameState.gameOver) {
459
+ const number = parseInt(button.dataset.number);
460
+ if (gameState.notesMode) {
461
+ toggleNote(gameState.selectedCell.row, gameState.selectedCell.col, number);
462
+ } else {
463
+ fillCell(gameState.selectedCell.row, gameState.selectedCell.col, number);
464
+ }
465
+ }
466
+ });
467
+ });
468
+
469
+ eraseButton.addEventListener('click', () => {
470
+ if (gameState.selectedCell && !gameState.givenCells[gameState.selectedCell.row][gameState.selectedCell.col] && !gameState.gameOver) {
471
+ eraseCell(gameState.selectedCell.row, gameState.selectedCell.col);
472
+ }
473
+ });
474
+
475
+ notesButton.addEventListener('click', () => {
476
+ if (!gameState.gameOver) {
477
+ gameState.notesMode = !gameState.notesMode;
478
+ notesButton.classList.toggle('bg-yellow-300', gameState.notesMode);
479
+ updateSelectedCell();
480
+ }
481
+ });
482
+
483
+ hintButton.addEventListener('click', () => {
484
+ if (gameState.selectedCell && gameState.hints > 0 && !gameState.gameOver) {
485
+ giveHint(gameState.selectedCell.row, gameState.selectedCell.col);
486
+ }
487
+ });
488
+
489
+ newGameButton.addEventListener('click', initGame);
490
+
491
+ noteStyleButtons.forEach(button => {
492
+ button.addEventListener('click', () => {
493
+ gameState.noteStyle = button.dataset.noteStyle;
494
+ noteStyleButtons.forEach(btn =>
495
+ btn.classList.toggle('bg-indigo-100', btn.dataset.noteStyle === gameState.noteStyle)
496
+ );
497
+ renderBoard();
498
+ if (gameState.selectedCell) {
499
+ selectCell(gameState.selectedCell.row, gameState.selectedCell.col);
500
+ }
501
+ });
502
+ });
503
+
504
+ highlightButtons.forEach(button => {
505
+ button.addEventListener('click', () => {
506
+ gameState.highlightMode = button.dataset.highlight;
507
+ highlightButtons.forEach(btn =>
508
+ btn.classList.toggle('bg-indigo-100', btn.dataset.highlight === gameState.highlightMode)
509
+ );
510
+ if (gameState.selectedCell && gameState.board[gameState.selectedCell.row][gameState.selectedCell.col] !== 0) {
511
+ highlightNumbers(gameState.board[gameState.selectedCell.row][gameState.selectedCell.col]);
512
+ }
513
+ });
514
+ });
515
+
516
+ undoButton.addEventListener('click', undoMove);
517
+ redoButton.addEventListener('click', redoMove);
518
+
519
+ helpButton.addEventListener('click', () => {
520
+ helpModal.classList.remove('hidden');
521
+ });
522
+
523
+ closeHelpButton.addEventListener('click', () => {
524
+ helpModal.classList.add('hidden');
525
+ });
526
+
527
+ okHelpButton.addEventListener('click', () => {
528
+ helpModal.classList.add('hidden');
529
+ });
530
+
531
+ themeButtons.light.addEventListener('click', () => setTheme('light'));
532
+ themeButtons.dark.addEventListener('click', () => setTheme('dark'));
533
+ themeButtons.blue.addEventListener('click', () => setTheme('blue'));
534
+
535
+ fullscreenButton.addEventListener('click', toggleFullscreen);
536
+
537
+ // Keyboard support
538
+ document.addEventListener('keydown', (e) => {
539
+ if (gameState.gameOver) return;
540
+
541
+ if (gameState.selectedCell) {
542
+ const { row, col } = gameState.selectedCell;
543
+
544
+ // Number keys 1-9
545
+ if (e.key >= '1' && e.key <= '9') {
546
+ const number = parseInt(e.key);
547
+ if (gameState.notesMode) {
548
+ toggleNote(row, col, number);
549
+ } else {
550
+ fillCell(row, col, number);
551
+ }
552
+ }
553
+ // Delete or Backspace
554
+ else if ((e.key === 'Delete' || e.key === 'Backspace') && !gameState.givenCells[row][col]) {
555
+ eraseCell(row, col);
556
+ }
557
+ // Arrow keys for navigation
558
+ else if (e.key === 'ArrowUp' && row > 0) {
559
+ selectCell(row - 1, col);
560
+ } else if (e.key === 'ArrowDown' && row < 8) {
561
+ selectCell(row + 1, col);
562
+ } else if (e.key === 'ArrowLeft' && col > 0) {
563
+ selectCell(row, col - 1);
564
+ } else if (e.key === 'ArrowRight' && col < 8) {
565
+ selectCell(row, col + 1);
566
+ }
567
+ // Space for notes mode
568
+ else if (e.key === ' ') {
569
+ gameState.notesMode = !gameState.notesMode;
570
+ notesButton.classList.toggle('bg-yellow-300', gameState.notesMode);
571
+ updateSelectedCell();
572
+ }
573
+ // Z for undo (with Ctrl for Windows or Cmd for Mac)
574
+ else if ((e.ctrlKey || e.metaKey) && e.key === 'z') {
575
+ undoMove();
576
+ }
577
+ // Y for redo (with Ctrl for Windows or Cmd for Mac)
578
+ else if ((e.ctrlKey || e.metaKey) && e.key === 'y') {
579
+ redoMove();
580
+ }
581
+ }
582
+ });
583
+
584
+ // Initialize the game
585
+ function initGame() {
586
+ // Reset game state
587
+ gameState.board = Array(9).fill().map(() => Array(9).fill(0));
588
+ gameState.solution = Array(9).fill().map(() => Array(9).fill(0));
589
+ gameState.givenCells = Array(9).fill().map(() => Array(9).fill(false));
590
+ gameState.notes = Array(9).fill().map(() => Array(9).fill().map(() => new Set()));
591
+ gameState.selectedCell = null;
592
+ gameState.notesMode = false;
593
+ gameState.mistakes = 0;
594
+ gameState.hints = 3;
595
+ gameState.timer = 0;
596
+ gameState.gameOver = false;
597
+ gameState.movesHistory = [];
598
+ gameState.historyPointer = -1;
599
+
600
+ // Reset UI
601
+ notesButton.classList.remove('bg-yellow-300');
602
+ messageElement.classList.add('hidden');
603
+ updateTimer();
604
+ mistakesElement.textContent = '0/3';
605
+ hintsElement.textContent = '3';
606
+ statBars.mistakes.style.width = '0%';
607
+ statBars.hints.style.width = '100%';
608
+
609
+ // Stop any existing timer
610
+ if (gameState.timerInterval) {
611
+ clearInterval(gameState.timerInterval);
612
+ }
613
+
614
+ // Generate a new puzzle
615
+ generatePuzzle(difficultySelect.value);
616
+
617
+ // Render the board
618
+ renderBoard();
619
+
620
+ // Start the timer
621
+ gameState.timerInterval = setInterval(() => {
622
+ gameState.timer++;
623
+ updateTimer();
624
+ }, 1000);
625
+ }
626
+
627
+ // Generate a Sudoku puzzle
628
+ function generatePuzzle(difficulty) {
629
+ // First generate a complete solution
630
+ generateSolution(0, 0);
631
+
632
+ // Then remove numbers based on difficulty
633
+ let cellsToRemove;
634
+ switch (difficulty) {
635
+ case 'easy':
636
+ cellsToRemove = 40 + Math.floor(Math.random() * 5); // 40-44
637
+ break;
638
+ case 'medium':
639
+ cellsToRemove = 45 + Math.floor(Math.random() * 5); // 45-49
640
+ break;
641
+ case 'hard':
642
+ cellsToRemove = 50 + Math.floor(Math.random() * 5); // 50-54
643
+ break;
644
+ case 'expert':
645
+ cellsToRemove = 55 + Math.floor(Math.random() * 5); // 55-59
646
+ break;
647
+ case 'insane':
648
+ cellsToRemove = 60 + Math.floor(Math.random() * 4); // 60-63
649
+ break;
650
+ default:
651
+ cellsToRemove = 45;
652
+ }
653
+
654
+ // Copy solution to board
655
+ for (let i = 0; i < 9; i++) {
656
+ for (let j = 0; j < 9; j++) {
657
+ gameState.board[i][j] = gameState.solution[i][j];
658
+ }
659
+ }
660
+
661
+ // Remove cells in a way that maintains puzzle uniqueness
662
+ let removed = 0;
663
+ const positions = [];
664
+ for (let i = 0; i < 9; i++) {
665
+ for (let j = 0; j < 9; j++) {
666
+ positions.push({row: i, col: j});
667
+ }
668
+ }
669
+ shuffleArray(positions);
670
+
671
+ // Use a more sophisticated removal algorithm that checks for uniqueness
672
+ for (const pos of positions) {
673
+ if (removed >= cellsToRemove) break;
674
+
675
+ const {row, col} = pos;
676
+ if (gameState.board[row][col] !== 0) {
677
+ const temp = gameState.board[row][col];
678
+ gameState.board[row][col] = 0;
679
+
680
+ // Check if the puzzle still has a unique solution
681
+ if (!hasUniqueSolution()) {
682
+ gameState.board[row][col] = temp;
683
+ } else {
684
+ removed++;
685
+ gameState.givenCells[row][col] = false;
686
+ }
687
+ }
688
+ }
689
+
690
+ // Mark the remaining cells as given
691
+ for (let i = 0; i < 9; i++) {
692
+ for (let j = 0; j < 9; j++) {
693
+ if (gameState.board[i][j] !== 0) {
694
+ gameState.givenCells[i][j] = true;
695
+ }
696
+ }
697
+ }
698
+ }
699
+
700
+ // Check if the current puzzle has a unique solution
701
+ function hasUniqueSolution() {
702
+ const tempBoard = JSON.parse(JSON.stringify(gameState.board));
703
+ let solutions = 0;
704
+
705
+ function countSolutions(r, c) {
706
+ if (r === 9) {
707
+ solutions++;
708
+ return;
709
+ }
710
+
711
+ if (c === 9) {
712
+ countSolutions(r + 1, 0);
713
+ return;
714
+ }
715
+
716
+ if (tempBoard[r][c] !== 0) {
717
+ countSolutions(r, c + 1);
718
+ return;
719
+ }
720
+
721
+ for (let num = 1; num <= 9 && solutions < 2; num++) {
722
+ if (isValid(tempBoard, r, c, num)) {
723
+ tempBoard[r][c] = num;
724
+ countSolutions(r, c + 1);
725
+ if (solutions < 2) tempBoard[r][c] = 0;
726
+ }
727
+ }
728
+ }
729
+
730
+ countSolutions(0, 0);
731
+ return solutions === 1;
732
+ }
733
+
734
+ // Generate a complete Sudoku solution
735
+ function generateSolution(row, col) {
736
+ if (row === 9) {
737
+ return true;
738
+ }
739
+
740
+ if (col === 9) {
741
+ return generateSolution(row + 1, 0);
742
+ }
743
+
744
+ if (gameState.solution[row][col] !== 0) {
745
+ return generateSolution(row, col + 1);
746
+ }
747
+
748
+ // Try numbers 1-9 in random order
749
+ const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
750
+ shuffleArray(numbers);
751
+
752
+ for (const num of numbers) {
753
+ if (isValid(gameState.solution, row, col, num)) {
754
+ gameState.solution[row][col] = num;
755
+ if (generateSolution(row, col + 1)) {
756
+ return true;
757
+ }
758
+ gameState.solution[row][col] = 0;
759
+ }
760
+ }
761
+
762
+ return false;
763
+ }
764
+
765
+ // Check if a number can be placed in a cell
766
+ function isValid(grid, row, col, num) {
767
+ // Check row
768
+ for (let i = 0; i < 9; i++) {
769
+ if (grid[row][i] === num) return false;
770
+ }
771
+
772
+ // Check column
773
+ for (let i = 0; i < 9; i++) {
774
+ if (grid[i][col] === num) return false;
775
+ }
776
+
777
+ // Check 3x3 box
778
+ const boxRow = Math.floor(row / 3) * 3;
779
+ const boxCol = Math.floor(col / 3) * 3;
780
+ for (let i = 0; i < 3; i++) {
781
+ for (let j = 0; j < 3; j++) {
782
+ if (grid[boxRow + i][boxCol + j] === num) return false;
783
+ }
784
+ }
785
+
786
+ return true;
787
+ }
788
+
789
+ // Shuffle an array
790
+ function shuffleArray(array) {
791
+ for (let i = array.length - 1; i > 0; i--) {
792
+ const j = Math.floor(Math.random() * (i + 1));
793
+ [array[i], array[j]] = [array[j], array[i]];
794
+ }
795
+ }
796
+
797
+ // Render the Sudoku board
798
+ function renderBoard() {
799
+ boardElement.innerHTML = '';
800
+ for (let i = 0; i < 9; i++) {
801
+ const rowElement = document.createElement('div');
802
+ rowElement.className = 'row flex';
803
+ for (let j = 0; j < 9; j++) {
804
+ const cell = document.createElement('input');
805
+ cell.className = 'cell';
806
+ cell.type = 'text';
807
+ cell.maxLength = 1;
808
+ cell.dataset.row = i;
809
+ cell.dataset.col = j;
810
+
811
+ if (gameState.board[i][j] !== 0) {
812
+ const cellValue = document.createElement('div');
813
+ cellValue.className = 'cell-value w-full h-full flex items-center justify-center';
814
+ cellValue.textContent = gameState.board[i][j];
815
+ cell.appendChild(cellValue);
816
+
817
+ if (gameState.givenCells[i][j]) {
818
+ cell.classList.add('given');
819
+ cell.readOnly = true;
820
+ }
821
+ } else if (gameState.notes[i][j].size > 0 && gameState.noteStyle !== 'hidden') {
822
+ if (gameState.noteStyle === 'grid') {
823
+ const notesContainer = document.createElement('div');
824
+ notesContainer.className = 'notes-grid';
825
+
826
+ for (let n = 1; n <= 9; n++) {
827
+ const note = document.createElement('div');
828
+ note.className = 'flex items-center justify-center';
829
+ note.textContent = gameState.notes[i][j].has(n) ? n : '';
830
+ notesContainer.appendChild(note);
831
+ }
832
+
833
+ cell.appendChild(notesContainer);
834
+ } else if (gameState.noteStyle === 'corner') {
835
+ const notesContainer = document.createElement('div');
836
+ notesContainer.className = 'notes-corner w-full h-full';
837
+
838
+ const positions = {
839
+ 1: { top: '2px', left: '2px' },
840
+ 2: { top: '2px', left: '50%', transform: 'translateX(-50%)' },
841
+ 3: { top: '2px', right: '2px' },
842
+ 4: { top: '50%', left: '2px', transform: 'translateY(-50%)' },
843
+ 5: { top: '50%', left: '50%', transform: 'translate(-50%, -50%)' },
844
+ 6: { top: '50%', right: '2px', transform: 'translateY(-50%)' },
845
+ 7: { bottom: '2px', left: '2px' },
846
+ 8: { bottom: '2px', left: '50%', transform: 'translateX(-50%)' },
847
+ 9: { bottom: '2px', right: '2px' }
848
+ };
849
+
850
+ for (let n = 1; n <= 9; n++) {
851
+ if (gameState.notes[i][j].has(n)) {
852
+ const note = document.createElement('div');
853
+ note.className = 'notes-corner absolute text-xs';
854
+ note.textContent = n;
855
+ Object.assign(note.style, positions[n]);
856
+ notesContainer.appendChild(note);
857
+ }
858
+ }
859
+
860
+ cell.appendChild(notesContainer);
861
+ }
862
+ }
863
+
864
+ cell.addEventListener('click', () => selectCell(i, j));
865
+ cell.addEventListener('focus', () => selectCell(i, j));
866
+
867
+ rowElement.appendChild(cell);
868
+ }
869
+ boardElement.appendChild(rowElement);
870
+ }
871
+ }
872
+
873
+ // Select a cell
874
+ function selectCell(row, col) {
875
+ // Deselect previous cell
876
+ if (gameState.selectedCell) {
877
+ const prevCell = document.querySelector(`.cell[data-row="${gameState.selectedCell.row}"][data-col="${gameState.selectedCell.col}"]`);
878
+ if (prevCell) {
879
+ prevCell.classList.remove('selected', 'highlight-mode');
880
+ // Remove highlights
881
+ removeHighlights();
882
+ }
883
+ }
884
+
885
+ // Select new cell
886
+ gameState.selectedCell = { row, col };
887
+ const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
888
+ if (cell) {
889
+ cell.classList.add('selected', 'highlight-mode');
890
+ cell.focus();
891
+
892
+ // Highlight based on current mode
893
+ if (gameState.board[row][col] !== 0) {
894
+ highlightNumbers(gameState.board[row][col]);
895
+ } else if (gameState.highlightMode === 'peers') {
896
+ highlightPeers(row, col);
897
+ }
898
+ }
899
+ }
900
+
901
+ // Update the selected cell appearance
902
+ function updateSelectedCell() {
903
+ if (gameState.selectedCell) {
904
+ const cell = document.querySelector(`.cell[data-row="${gameState.selectedCell.row}"][data-col="${gameState.selectedCell.col}"]`);
905
+ if (cell) {
906
+ if (gameState.notesMode) {
907
+ cell.classList.add('border-yellow-400', 'border-2');
908
+ } else {
909
+ cell.classList.remove('border-yellow-400', 'border-2');
910
+ }
911
+ }
912
+ }
913
+ }
914
+
915
+ // Fill a cell with a number
916
+ function fillCell(row, col, number) {
917
+ if (gameState.givenCells[row][col] || gameState.gameOver) return;
918
+
919
+ // Save current state for undo/redo
920
+ saveMove();
921
+
922
+ // Check if the number is valid
923
+ const isValidMove = number === gameState.solution[row][col];
924
+
925
+ // Check for conflicts with current board (not just solution)
926
+ const isConflict = hasConflict(row, col, number);
927
+
928
+ // Update the board
929
+ gameState.board[row][col] = isConflict ? 0 : number;
930
+
931
+ // Clear any notes for this cell
932
+ gameState.notes[row][col].clear();
933
+
934
+ // Render the board
935
+ renderBoard();
936
+ selectCell(row, col);
937
+
938
+ // Highlight the number if not in conflicts mode
939
+ if (gameState.highlightMode !== 'conflicts') {
940
+ highlightNumbers(number);
941
+ }
942
+
943
+ // Check for mistakes
944
+ if (!isValidMove) {
945
+ gameState.mistakes++;
946
+ mistakesElement.textContent = `${gameState.mistakes}/3`;
947
+ statBars.mistakes.style.width = `${(gameState.mistakes / 3) * 100}%`;
948
+
949
+ // Show error and highlight conflicts
950
+ const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
951
+ if (cell) {
952
+ cell.classList.add('error');
953
+ setTimeout(() => {
954
+ cell.classList.remove('error');
955
+ }, 1000);
956
+ }
957
+
958
+ // Check if game over
959
+ if (gameState.mistakes >= 3) {
960
+ gameState.gameOver = true;
961
+ showMessage('Game Over! Too many mistakes.', 'bg-red-500');
962
+ clearInterval(gameState.timerInterval);
963
+
964
+ // Update statistics
965
+ gameState.stats.gamesPlayed++;
966
+ gameState.stats.totalTime += gameState.timer;
967
+ updateDifficultyStats(false);
968
+ saveStatistics();
969
+ updateStatisticsDisplay();
970
+ }
971
+ } else {
972
+ // Check if puzzle is complete
973
+ if (isPuzzleComplete()) {
974
+ gameState.gameOver = true;
975
+ showMessage('Congratulations! You solved the puzzle!', 'bg-green-500');
976
+ clearInterval(gameState.timerInterval);
977
+
978
+ // Update statistics
979
+ gameState.stats.gamesPlayed++;
980
+ gameState.stats.gamesWon++;
981
+ gameState.stats.totalTime += gameState.timer;
982
+
983
+ // Update best time if applicable
984
+ const currentDifficulty = difficultySelect.value;
985
+ if (!gameState.stats.bestTime || gameState.timer < gameState.stats.bestTime) {
986
+ gameState.stats.bestTime = gameState.timer;
987
+ }
988
+
989
+ if (!gameState.stats.difficultyStats[currentDifficulty].bestTime ||
990
+ gameState.timer < gameState.stats.difficultyStats[currentDifficulty].bestTime) {
991
+ gameState.stats.difficultyStats[currentDifficulty].bestTime = gameState.timer;
992
+ }
993
+
994
+ updateDifficultyStats(true);
995
+ saveStatistics();
996
+ updateStatisticsDisplay();
997
+ }
998
+ }
999
+ }
1000
+
1001
+ // Check if a number would conflict with current board
1002
+ function hasConflict(row, col, number) {
1003
+ if (number === 0) return false;
1004
+
1005
+ // Check row
1006
+ for (let i = 0; i < 9; i++) {
1007
+ if (i !== col && gameState.board[row][i] === number) return true;
1008
+ }
1009
+
1010
+ // Check column
1011
+ for (let i = 0; i < 9; i++) {
1012
+ if (i !== row && gameState.board[i][col] === number) return true;
1013
+ }
1014
+
1015
+ // Check 3x3 box
1016
+ const boxRow = Math.floor(row / 3) * 3;
1017
+ const boxCol = Math.floor(col / 3) * 3;
1018
+ for (let i = 0; i < 3; i++) {
1019
+ for (let j = 0; j < 3; j++) {
1020
+ const r = boxRow + i;
1021
+ const c = boxCol + j;
1022
+ if (r !== row && c !== col && gameState.board[r][c] === number) return true;
1023
+ }
1024
+ }
1025
+
1026
+ return false;
1027
+ }
1028
+
1029
+ // Save current move to history
1030
+ function saveMove() {
1031
+ // If we're not at the end of history, discard future moves
1032
+ if (gameState.historyPointer < gameState.movesHistory.length - 1) {
1033
+ gameState.movesHistory = gameState.movesHistory.slice(0, gameState.historyPointer + 1);
1034
+ }
1035
+
1036
+ // Save current state
1037
+ gameState.movesHistory.push({
1038
+ board: JSON.parse(JSON.stringify(gameState.board)),
1039
+ notes: JSON.parse(JSON.stringify(gameState.notes.map(row => row.map(col => [...col])))),
1040
+ selectedCell: gameState.selectedCell ? {...gameState.selectedCell} : null
1041
+ });
1042
+
1043
+ gameState.historyPointer = gameState.movesHistory.length - 1;
1044
+
1045
+ // Update undo/redo button states
1046
+ updateUndoRedoButtons();
1047
+ }
1048
+
1049
+ // Undo the last move
1050
+ function undoMove() {
1051
+ if (gameState.historyPointer < 0) return;
1052
+
1053
+ gameState.historyPointer--;
1054
+
1055
+ if (gameState.historyPointer >= 0) {
1056
+ const state = gameState.movesHistory[gameState.historyPointer];
1057
+ gameState.board = JSON.parse(JSON.stringify(state.board));
1058
+ gameState.notes = state.notes.map(row => row.map(col => new Set(col)));
1059
+
1060
+ if (state.selectedCell) {
1061
+ gameState.selectedCell = {...state.selectedCell};
1062
+ }
1063
+ } else {
1064
+ // If we're undoing the very first move, reset the board
1065
+ gameState.board = Array(9).fill().map(() => Array(9).fill(0));
1066
+ gameState.notes = Array(9).fill().map(() => Array(9).fill().map(() => new Set()));
1067
+ gameState.selectedCell = null;
1068
+ }
1069
+
1070
+ renderBoard();
1071
+ if (gameState.selectedCell) {
1072
+ selectCell(gameState.selectedCell.row, gameState.selectedCell.col);
1073
+ }
1074
+
1075
+ updateUndoRedoButtons();
1076
+ }
1077
+
1078
+ // Redo the last undone move
1079
+ function redoMove() {
1080
+ if (gameState.historyPointer >= gameState.movesHistory.length - 1) return;
1081
+
1082
+ gameState.historyPointer++;
1083
+ const state = gameState.movesHistory[gameState.historyPointer];
1084
+ gameState.board = JSON.parse(JSON.stringify(state.board));
1085
+ gameState.notes = state.notes.map(row => row.map(col => new Set(col)));
1086
+
1087
+ if (state.selectedCell) {
1088
+ gameState.selectedCell = {...state.selectedCell};
1089
+ }
1090
+
1091
+ renderBoard();
1092
+ if (gameState.selectedCell) {
1093
+ selectCell(gameState.selectedCell.row, gameState.selectedCell.col);
1094
+ }
1095
+
1096
+ updateUndoRedoButtons();
1097
+ }
1098
+
1099
+ // Update undo/redo button states
1100
+ function updateUndoRedoButtons() {
1101
+ undoButton.disabled = gameState.historyPointer < 0;
1102
+ undoButton.classList.toggle('text-gray-400', gameState.historyPointer < 0);
1103
+ undoButton.classList.toggle('text-gray-700', gameState.historyPointer >= 0);
1104
+
1105
+ redoButton.disabled = gameState.historyPointer >= gameState.movesHistory.length - 1;
1106
+ redoButton.classList.toggle('text-gray-400', gameState.historyPointer >= gameState.movesHistory.length - 1);
1107
+ redoButton.classList.toggle('text-gray-700', gameState.historyPointer < gameState.movesHistory.length - 1);
1108
+ }
1109
+
1110
+ // Erase a cell
1111
+ function eraseCell(row, col) {
1112
+ if (gameState.givenCells[row][col] || gameState.gameOver) return;
1113
+
1114
+ // Save current state for undo/redo
1115
+ saveMove();
1116
+
1117
+ gameState.board[row][col] = 0;
1118
+ renderBoard();
1119
+ selectCell(row, col);
1120
+ }
1121
+
1122
+ // Toggle a note for a cell
1123
+ function toggleNote(row, col, number) {
1124
+ if (gameState.givenCells[row][col] || gameState.gameOver) return;
1125
+
1126
+ // Save current state for undo/redo
1127
+ saveMove();
1128
+
1129
+ if (gameState.notes[row][col].has(number)) {
1130
+ gameState.notes[row][col].delete(number);
1131
+ } else {
1132
+ gameState.notes[row][col].add(number);
1133
+ }
1134
+
1135
+ renderBoard();
1136
+ selectCell(row, col);
1137
+ }
1138
+
1139
+ // Give a hint
1140
+ function giveHint(row, col) {
1141
+ if (gameState.givenCells[row][col] || gameState.board[row][col] !== 0 || gameState.hints <= 0 || gameState.gameOver) return;
1142
+
1143
+ gameState.hints--;
1144
+ hintsElement.textContent = gameState.hints;
1145
+
1146
+ // Calculate width percentage for hints bar
1147
+ const percentage = (gameState.hints / 3) * 100;
1148
+ statBars.hints.style.width = `${percentage}%`;
1149
+
1150
+ // Flash the cell
1151
+ const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
1152
+ if (cell) {
1153
+ cell.classList.add('highlight');
1154
+ setTimeout(() => {
1155
+ cell.classList.remove('highlight');
1156
+ }, 1000);
1157
+ }
1158
+
1159
+ // Fill the cell after a delay to show the highlight
1160
+ setTimeout(() => {
1161
+ fillCell(row, col, gameState.solution[row][col]);
1162
+ }, 300);
1163
+ }
1164
+
1165
+ // Highlight all instances of a number and its peers
1166
+ function highlightNumbers(number) {
1167
+ removeHighlights();
1168
+ if (number === 0) return;
1169
+
1170
+ const cells = document.querySelectorAll('.cell');
1171
+ cells.forEach(cell => {
1172
+ const row = parseInt(cell.dataset.row);
1173
+ const col = parseInt(cell.dataset.col);
1174
+
1175
+ if (gameState.board[row][col] === number) {
1176
+ cell.classList.add('same-number');
1177
+ } else if (gameState.highlightMode === 'peers' && isPeer(row, col, gameState.selectedCell.row, gameState.selectedCell.col)) {
1178
+ cell.classList.add('peers');
1179
+ } else if (gameState.highlightMode === 'conflicts' && gameState.board[row][col] !== 0 && hasConflict(row, col, gameState.board[row][col])) {
1180
+ cell.classList.add('conflict');
1181
+ }
1182
+ });
1183
+ }
1184
+
1185
+ // Highlight peers of a cell
1186
+ function highlightPeers(row, col) {
1187
+ removeHighlights();
1188
+
1189
+ const cells = document.querySelectorAll('.cell');
1190
+ cells.forEach(cell => {
1191
+ const cellRow = parseInt(cell.dataset.row);
1192
+ const cellCol = parseInt(cell.dataset.col);
1193
+
1194
+ if (isPeer(cellRow, cellCol, row, col)) {
1195
+ cell.classList.add('peers');
1196
+ }
1197
+ });
1198
+ }
1199
+
1200
+ // Check if two cells are peers (same row, column, or box)
1201
+ function isPeer(row1, col1, row2, col2) {
1202
+ // Same row or column
1203
+ if (row1 === row2 || col1 === col2) return true;
1204
+
1205
+ // Same 3x3 box
1206
+ const boxRow1 = Math.floor(row1 / 3);
1207
+ const boxCol1 = Math.floor(col1 / 3);
1208
+ const boxRow2 = Math.floor(row2 / 3);
1209
+ const boxCol2 = Math.floor(col2 / 3);
1210
+
1211
+ return boxRow1 === boxRow2 && boxCol1 === boxCol2;
1212
+ }
1213
+
1214
+ // Remove all number highlights
1215
+ function removeHighlights() {
1216
+ const cells = document.querySelectorAll('.cell');
1217
+ cells.forEach(cell => {
1218
+ cell.classList.remove('same-number', 'peers', 'conflict');
1219
+ });
1220
+ }
1221
+
1222
+ // Check if the puzzle is complete
1223
+ function isPuzzleComplete() {
1224
+ for (let i = 0; i < 9; i++) {
1225
+ for (let j = 0; j < 9; j++) {
1226
+ if (gameState.board[i][j] !== gameState.solution[i][j]) {
1227
+ return false;
1228
+ }
1229
+ }
1230
+ }
1231
+ return true;
1232
+ }
1233
+
1234
+ // Update the timer display
1235
+ function updateTimer() {
1236
+ const minutes = Math.floor(gameState.timer / 60).toString().padStart(2, '0');
1237
+ const seconds = (gameState.timer % 60).toString().padStart(2, '0');
1238
+ timerElement.textContent = `${minutes}:${seconds}`;
1239
+ }
1240
+
1241
+ // Show a message
1242
+ function showMessage(text, className) {
1243
+ messageContent.textContent = text;
1244
+ messageContent.className = `inline-block px-8 py-4 rounded-xl font-bold text-white shadow-lg ${className}`;
1245
+ messageElement.classList.remove('hidden');
1246
+ }
1247
+
1248
+ // Update difficulty-specific statistics
1249
+ function updateDifficultyStats(isWin) {
1250
+ const difficulty = difficultySelect.value;
1251
+ gameState.stats.difficultyStats[difficulty].played++;
1252
+
1253
+ if (isWin) {
1254
+ gameState.stats.difficultyStats[difficulty].won++;
1255
+ }
1256
+ }
1257
+
1258
+ // Save statistics to localStorage
1259
+ function saveStatistics() {
1260
+ localStorage.setItem('sudokuStats', JSON.stringify(gameState.stats));
1261
+ }
1262
+
1263
+ // Load statistics from localStorage
1264
+ function loadStatistics() {
1265
+ const savedStats = localStorage.getItem('sudokuStats');
1266
+ if (savedStats) {
1267
+ gameState.stats = JSON.parse(savedStats);
1268
+ }
1269
+ }
1270
+
1271
+ // Update statistics display
1272
+ function updateStatisticsDisplay() {
1273
+ statsElements.gamesPlayed.textContent = gameState.stats.gamesPlayed;
1274
+ statBars.gamesPlayed.style.width = `${Math.min(gameState.stats.gamesPlayed * 10, 100)}%`;
1275
+
1276
+ const winRate = gameState.stats.gamesPlayed > 0 ?
1277
+ Math.round((gameState.stats.gamesWon / gameState.stats.gamesPlayed) * 100) : 0;
1278
+ statsElements.winRate.textContent = `${winRate}%`;
1279
+ statBars.winRate.style.width = `${winRate}%`;
1280
+
1281
+ const avgTime = gameState.stats.gamesPlayed > 0 ?
1282
+ Math.floor(gameState.stats.totalTime / gameState.stats.gamesPlayed) : 0;
1283
+ const avgMinutes = Math.floor(avgTime / 60).toString().padStart(2, '0');
1284
+ const avgSeconds = (avgTime % 60).toString().padStart(2, '0');
1285
+ statsElements.averageTime.textContent = `${avgMinutes}:${avgSeconds}`;
1286
+ statBars.averageTime.style.width = `${Math.min(avgTime * 100 / 1800, 100)}%`; // Cap at 30 minutes
1287
+
1288
+ if (gameState.stats.bestTime) {
1289
+ const bestMinutes = Math.floor(gameState.stats.bestTime / 60).toString().padStart(2, '0');
1290
+ const bestSeconds = (gameState.stats.bestTime % 60).toString().padStart(2, '0');
1291
+ statsElements.bestTime.textContent = `${bestMinutes}:${bestSeconds}`;
1292
+ statBars.bestTime.style.width = `${Math.max(100 - (gameState.stats.bestTime * 100 / 900), 5)}%`; // Invert scale (5% min)
1293
+ } else {
1294
+ statsElements.bestTime.textContent = "--:--";
1295
+ statBars.bestTime.style.width = "0%";
1296
+ }
1297
+ }
1298
+
1299
+ // Set theme for the game
1300
+ function setTheme(theme) {
1301
+ gameState.currentTheme = theme;
1302
+ document.body.classList.remove('theme-light', 'theme-dark', 'theme-blue');
1303
+ document.body.classList.add(`theme-${theme}`);
1304
+
1305
+ // Update theme button highlights
1306
+ Object.keys(themeButtons).forEach(key => {
1307
+ themeButtons[key].classList.toggle('ring-2', key === theme);
1308
+ themeButtons[key].classList.toggle('ring-indigo-500', key === theme);
1309
+ });
1310
+
1311
+ // Save theme preference
1312
+ localStorage.setItem('sudokuTheme', theme);
1313
+ }
1314
+
1315
+ // Toggle fullscreen mode
1316
+ function toggleFullscreen() {
1317
+ if (!document.fullscreenElement) {
1318
+ document.documentElement.requestFullscreen().catch(err => {
1319
+ console.error(`Error attempting to enable fullscreen: ${err.message}`);
1320
+ });
1321
+ } else {
1322
+ if (document.exitFullscreen) {
1323
+ document.exitFullscreen();
1324
+ }
1325
+ }
1326
+ }
1327
+
1328
+ // Load saved theme
1329
+ const savedTheme = localStorage.getItem('sudokuTheme') || 'light';
1330
+ setTheme(savedTheme);
1331
+
1332
+ // Set initial note style and highlight mode buttons
1333
+ noteStyleButtons.forEach(btn =>
1334
+ btn.classList.toggle('bg-indigo-100', btn.dataset.noteStyle === gameState.noteStyle)
1335
+ );
1336
+
1337
+ highlightButtons.forEach(btn =>
1338
+ btn.classList.toggle('bg-indigo-100', btn.dataset.highlight === gameState.highlightMode)
1339
+ );
1340
+ });
1341
+ </script>
1342
+ <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=sekopst/serkanspace" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
1343
+ </html>