ZMaxAIru commited on
Commit
fde372b
·
verified ·
1 Parent(s): 98fb3ce

Add 1 files

Browse files
Files changed (1) hide show
  1. index.html +821 -1148
index.html CHANGED
@@ -3,1334 +3,1007 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Пиксельный Майнинг</title>
7
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
  <style>
9
  :root {
10
  --bg-color: #1a1a2e;
11
- --primary-color: #4ecca3;
12
- --secondary-color: #eeeeee;
13
- --danger-color: #f05454;
14
- --silver-color: #c0c0c0;
15
- --gold-color: #ffd700;
16
- --diamond-color: #b9f2ff;
17
- --canvas-bg: #16213e;
18
- --card-bg: #0f3460;
19
  --text-color: #e6e6e6;
 
 
 
 
 
20
  }
21
-
22
  * {
23
  margin: 0;
24
  padding: 0;
25
  box-sizing: border-box;
26
- font-family: 'Courier New', monospace;
27
  }
28
-
29
  body {
 
30
  background-color: var(--bg-color);
31
  color: var(--text-color);
32
  min-height: 100vh;
33
  overflow-x: hidden;
34
  }
35
-
36
  .container {
37
- max-width: 1200px;
 
38
  margin: 0 auto;
39
- padding: 20px;
40
  }
41
-
42
  header {
43
  display: flex;
44
  justify-content: space-between;
45
  align-items: center;
46
- padding: 15px 0;
47
- border-bottom: 2px solid var(--primary-color);
48
- margin-bottom: 20px;
49
  }
50
-
51
  .logo {
52
- display: flex;
53
- align-items: center;
54
- gap: 10px;
55
- font-size: 24px;
56
- font-weight: bold;
57
- color: var(--primary-color);
58
- }
59
-
60
- .logo img {
61
- width: 40px;
62
- height: 40px;
63
- }
64
-
65
- .nav-tabs {
66
- display: flex;
67
- gap: 15px;
68
- }
69
-
70
- .tab {
71
- padding: 8px 15px;
72
- border-radius: 20px;
73
- cursor: pointer;
74
- transition: all 0.3s;
75
  font-weight: bold;
 
 
76
  }
77
-
78
- .tab:hover {
79
- background-color: var(--card-bg);
80
- }
81
-
82
- .tab.active {
83
- background-color: var(--primary-color);
84
- color: var(--bg-color);
85
- }
86
-
87
- .user-profile {
88
  display: flex;
89
  align-items: center;
90
- gap: 10px;
91
  }
92
-
93
- .avatar {
94
- width: 40px;
95
- height: 40px;
96
  border-radius: 50%;
97
- border: 2px solid var(--primary-color);
98
- }
99
-
100
- .pix-balance {
101
- background-color: var(--card-bg);
102
- padding: 5px 10px;
103
- border-radius: 20px;
104
- font-weight: bold;
105
- display: flex;
106
- align-items: center;
107
- gap: 5px;
108
- }
109
-
110
- .pix-balance i {
111
- color: var(--gold-color);
112
- }
113
-
114
- .main-content {
115
- display: flex;
116
- flex-direction: column;
117
- gap: 30px;
118
  }
119
-
120
- .section-title {
121
- font-size: 22px;
122
- margin-bottom: 15px;
123
- color: var(--primary-color);
124
- display: flex;
125
- align-items: center;
126
- gap: 10px;
127
- }
128
-
129
- /* Mining Section */
130
  .mining-section {
131
- background-color: var(--card-bg);
132
- border-radius: 15px;
133
- padding: 20px;
134
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
135
- }
136
-
137
- .block-info {
138
  display: flex;
139
- justify-content: space-between;
 
140
  margin-bottom: 20px;
141
- flex-wrap: wrap;
142
- gap: 15px;
143
- }
144
-
145
- .info-card {
146
- background-color: var(--bg-color);
147
- padding: 15px;
148
- border-radius: 10px;
149
- flex: 1;
150
- min-width: 150px;
151
- }
152
-
153
- .info-card h3 {
154
- font-size: 14px;
155
- margin-bottom: 5px;
156
- color: var(--primary-color);
157
  }
158
-
159
- .info-card p {
160
- font-size: 18px;
161
- font-weight: bold;
162
- }
163
-
164
- .pixel-canvas-container {
165
  position: relative;
166
- margin: 20px auto;
167
- max-width: 600px;
 
 
 
168
  }
169
-
170
  .pixel-canvas {
171
  display: grid;
172
- background-color: var(--canvas-bg);
173
- border: 4px solid var(--primary-color);
174
- border-radius: 5px;
175
- overflow: hidden;
176
  }
177
-
178
  .pixel {
179
- aspect-ratio: 1/1;
180
- background-color: #2b2b3a;
181
- border: 1px solid rgba(0, 0, 0, 0.1);
182
  cursor: pointer;
183
- transition: all 0.2s;
184
- position: relative;
185
- overflow: hidden;
186
  }
187
-
188
  .pixel:hover {
189
- filter: brightness(1.2);
190
  }
191
-
192
- .pixel.mined {
193
- background-color: #4ecca3;
194
  }
195
-
196
- .pixel.silver {
197
- background-color: var(--silver-color);
198
  }
199
-
200
- .pixel.gold {
201
- background-color: var(--gold-color);
202
  }
203
-
204
- .pixel.diamond {
205
- background-color: var(--diamond-color);
206
  }
207
-
208
- .pixel-value {
209
- position: absolute;
210
- bottom: 2px;
211
- right: 2px;
212
- font-size: 8px;
213
- font-weight: bold;
214
- color: #000;
215
- opacity: 0.7;
216
  }
217
-
218
- .mine-button-container {
 
 
 
219
  display: flex;
220
- justify-content: center;
221
- margin-top: 20px;
222
  }
223
-
224
- .mine-btn {
225
- background-color: var(--primary-color);
226
- color: var(--bg-color);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  border: none;
228
- padding: 15px 30px;
229
- font-size: 18px;
230
- border-radius: 50px;
231
- cursor: pointer;
232
- transition: all 0.3s;
233
  font-weight: bold;
 
 
 
234
  display: flex;
235
  align-items: center;
236
- gap: 10px;
237
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
  }
239
-
240
- .mine-btn:hover {
241
- transform: translateY(-3px);
242
- box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
 
 
243
  }
244
-
245
- .mine-btn:active {
246
- transform: translateY(0);
 
 
 
247
  }
248
-
249
- .mine-btn i {
250
- animation: pulse 2s infinite;
 
 
251
  }
252
-
253
- @keyframes pulse {
254
- 0% { transform: scale(1); }
255
- 50% { transform: scale(1.2); }
256
- 100% { transform: scale(1); }
257
  }
258
-
259
- /* Leaderboard Section */
260
- .leaderboard-section {
261
- background-color: var(--card-bg);
262
- border-radius: 15px;
263
- padding: 20px;
264
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
265
  }
266
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
  .leaderboard {
268
  width: 100%;
269
  border-collapse: collapse;
270
  }
271
-
272
- .leaderboard th, .leaderboard td {
273
- padding: 12px 15px;
274
  text-align: left;
275
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  }
277
-
278
- .leaderboard th {
279
- color: var(--primary-color);
280
- font-size: 16px;
281
  }
282
-
283
- .leaderboard tr:hover {
284
- background-color: rgba(255, 255, 255, 0.05);
 
285
  }
286
-
287
- .leaderboard .rank {
288
- font-weight: bold;
289
- width: 50px;
290
  }
291
-
292
- .leaderboard .user {
293
  display: flex;
294
- align-items: center;
295
- gap: 10px;
 
296
  }
297
-
298
- .leaderboard .user-avatar {
299
- width: 30px;
300
- height: 30px;
301
- border-radius: 50%;
302
  }
303
-
304
- .leaderboard .pix {
305
- display: flex;
306
- align-items: center;
307
- gap: 5px;
308
- color: var(--gold-color);
309
- font-weight: bold;
310
  }
311
-
312
- /* Blocks History */
313
- .history-section {
314
- background-color: var(--card-bg);
315
- border-radius: 15px;
316
- padding: 20px;
317
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
318
- margin-bottom: 50px;
 
 
 
 
 
 
319
  }
320
-
321
- .blocks-grid {
 
 
 
 
322
  display: grid;
323
- grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
324
- gap: 15px;
325
- margin-top: 20px;
326
  }
327
-
328
- .block-card {
329
- background-color: var(--bg-color);
330
- border-radius: 10px;
331
- overflow: hidden;
 
 
 
332
  cursor: pointer;
333
- transition: all 0.3s;
334
- position: relative;
335
- }
336
-
337
- .block-card:hover {
338
- transform: translateY(-5px);
339
- box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);
340
- }
341
-
342
- .block-preview {
343
- width: 100%;
344
- height: 150px;
345
- }
346
-
347
- .block-info-overlay {
348
- position: absolute;
349
- bottom: 0;
350
- left: 0;
351
- right: 0;
352
- background: rgba(0, 0, 0, 0.7);
353
- padding: 10px;
354
- }
355
-
356
- .block-number {
357
- font-size: 14px;
358
- font-weight: bold;
359
- color: var(--primary-color);
360
- }
361
-
362
- .block-creator {
363
- font-size: 12px;
364
- display: -webkit-box;
365
- -webkit-line-clamp: 1;
366
- -webkit-box-orient: vertical;
367
  overflow: hidden;
368
- text-overflow: ellipsis;
369
  }
370
-
371
- /* Modal */
372
- .modal {
373
- display: none;
374
- position: fixed;
375
- top: 0;
376
- left: 0;
377
  width: 100%;
378
  height: 100%;
379
- background-color: rgba(0, 0, 0, 0.8);
380
- z-index: 1000;
381
- justify-content: center;
382
- align-items: center;
383
  }
384
-
385
- .modal-content {
386
- background-color: var(--card-bg);
387
- border-radius: 15px;
388
- padding: 30px;
389
- max-width: 500px;
390
- width: 90%;
391
- position: relative;
392
- max-height: 90vh;
393
- overflow-y: auto;
394
  }
395
-
396
- .close-modal {
397
- position: absolute;
398
- top: 15px;
399
- right: 15px;
400
- font-size: 24px;
401
- cursor: pointer;
402
- color: var(--text-color);
403
- transition: all 0.3s;
404
- }
405
-
406
- .close-modal:hover {
407
- color: var(--danger-color);
408
- }
409
-
410
- .modal-title {
411
- font-size: 24px;
412
- margin-bottom: 20px;
413
- color: var(--primary-color);
414
- text-align: center;
415
  }
416
-
417
- /* Responsive */
418
- @media (max-width: 768px) {
419
- .nav-tabs {
420
- display: none;
421
  }
422
 
423
- .user-profile {
424
- margin-left: auto;
425
- }
426
-
427
- .block-info {
428
- flex-direction: column;
429
- }
430
-
431
- .info-card {
432
- min-width: 100%;
433
- }
434
-
435
- .leaderboard th, .leaderboard td {
436
- padding: 10px 12px;
437
- font-size: 14px;
438
  }
439
-
440
- .blocks-grid {
441
- grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
442
  }
443
  }
444
-
445
- /* Pixel animation */
446
- @keyframes reveal {
447
- 0% { transform: scale(0); opacity: 0; }
448
- 100% { transform: scale(1); opacity: 1; }
449
- }
450
-
451
- .pixel.reveal {
452
- animation: reveal 0.3s ease-out;
453
- }
454
-
455
- /* Badges */
456
- .badge {
457
- padding: 2px 5px;
458
- border-radius: 3px;
459
- font-size: 10px;
460
- font-weight: bold;
461
- margin-left: 5px;
462
- }
463
-
464
- .badge.silver {
465
- background-color: var(--silver-color);
466
- color: #000;
467
- }
468
-
469
- .badge.gold {
470
- background-color: var(--gold-color);
471
- color: #000;
472
- }
473
-
474
- .badge.diamond {
475
- background-color: var(--diamond-color);
476
- color: #000;
477
- }
478
-
479
- /* Notification */
480
- .notification {
481
- position: fixed;
482
- bottom: 20px;
483
- right: 20px;
484
- background-color: var(--primary-color);
485
- color: var(--bg-color);
486
- padding: 15px 20px;
487
- border-radius: 10px;
488
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
489
- transform: translateY(100px);
490
- opacity: 0;
491
- transition: all 0.3s;
492
- z-index: 100;
493
- }
494
-
495
- .notification.show {
496
- transform: translateY(0);
497
- opacity: 1;
498
- }
499
-
500
- .notification-title {
501
- font-weight: bold;
502
- margin-bottom: 5px;
503
- }
504
-
505
- /* Tutorial tooltip */
506
- .tooltip {
507
- position: relative;
508
- display: inline-block;
509
- margin-left: 10px;
510
- }
511
-
512
- .tooltip .tooltip-text {
513
- visibility: hidden;
514
- width: 200px;
515
- background-color: var(--bg-color);
516
- color: var(--text-color);
517
- text-align: center;
518
- border-radius: 6px;
519
- padding: 10px;
520
- position: absolute;
521
- z-index: 1;
522
- bottom: 125%;
523
- left: 50%;
524
- margin-left: -100px;
525
- opacity: 0;
526
- transition: opacity 0.3s;
527
- border: 1px solid var(--primary-color);
528
- }
529
-
530
- .tooltip:hover .tooltip-text {
531
- visibility: visible;
532
- opacity: 1;
533
- }
534
  </style>
535
  </head>
536
  <body>
537
  <div class="container">
538
  <header>
539
- <div class="logo">
540
- <i class="fas fa-cubes"></i>
541
- <span>Пиксельный Майнинг</span>
 
542
  </div>
543
- <div class="nav-tabs">
544
- <div class="tab active" onclick="showTab('mining')">
545
- <i class="fas fa-digging"></i> Майнинг
546
- </div>
547
- <div class="tab" onclick="showTab('profile')">
548
- <i class="fas fa-user"></i> Профиль
549
- </div>
550
- <div class="tab" onclick="showTab('wallet')">
551
- <i class="fas fa-wallet"></i> Кошелек
552
- </div>
553
- <div class="tab" onclick="showTab('leaderboard')">
554
- <i class="fas fa-trophy"></i> Топ игроков
555
- </div>
556
- <div class="tab" onclick="showTab('history')">
557
- <i class="fas fa-history"></i> История блоков
558
- </div>
559
  </div>
560
- <div class="user-profile">
561
- <div class="pix-balance">
562
- <i class="fas fa-coins"></i>
563
- <span id="userBalance">1,240</span> ПИКС
 
 
 
 
 
 
 
 
 
564
  </div>
565
- <img src="https://i.pravatar.cc/150?img=5" alt="Avatar" class="avatar">
 
 
 
 
566
  </div>
567
- </header>
568
-
569
- <div class="main-content">
570
- <div id="mining-tab" class="tab-content">
571
- <section class="mining-section">
572
- <h2 class="section-title">
573
- <i class="fas fa-digging"></i> Текущий Блок
574
- <div class="tooltip">
575
- <i class="fas fa-question-circle"></i>
576
- <span class="tooltip-text">Добывайте пиксели вместе с другими игроками! Чем больше пикселей вы откроете, тем больше очков получите.</span>
577
- </div>
578
- </h2>
579
-
580
- <div class="block-info">
581
- <div class="info-card">
582
- <h3>Текущий Блок</h3>
583
- <p>#42</p>
584
- </div>
585
- <div class="info-card">
586
- <h3>Прогресс</h3>
587
- <p>76%</p>
588
- </div>
589
- <div class="info-card">
590
- <h3>Пикселей</h3>
591
- <p>76/100</p>
592
- </div>
593
- <div class="info-card">
594
- <h3>Создатель</h3>
595
- <p>-</p>
596
- </div>
597
  </div>
598
-
599
- <div class="pixel-canvas-container">
600
- <div id="pixelCanvas" class="pixel-canvas"></div>
601
  </div>
602
-
603
- <div class="mine-button-container">
604
- <button id="mineBtn" class="mine-btn" onclick="minePixel()">
605
- <i class="fas fa-hammer"></i> Добыть Пиксель
606
- </button>
607
  </div>
608
- </section>
609
-
610
- <section class="leaderboard-section">
611
- <h2 class="section-title"><i class="fas fa-trophy"></i> Топ Блока</h2>
612
- <div class="table-responsive">
613
- <table class="leaderboard">
614
- <thead>
615
- <tr>
616
- <th class="rank">#</th>
617
- <th>Игрок</th>
618
- <th>Пикселей</th>
619
- <th>Очки</th>
620
- </tr>
621
- </thead>
622
- <tbody id="blockLeaderboard">
623
- <!-- Filled by JS -->
624
- </tbody>
625
- </table>
626
  </div>
627
- </section>
628
- </div>
629
-
630
- <div id="leaderboard-tab" class="tab-content" style="display: none;">
631
- <section class="leaderboard-section">
632
- <h2 class="section-title"><i class="fas fa-trophy"></i> Топ Игроков</h2>
633
- <div class="table-responsive">
634
- <table class="leaderboard">
635
- <thead>
636
- <tr>
637
- <th class="rank">#</th>
638
- <th>Игрок</th>
639
- <th>Блоков</th>
640
- <th>Очки</th>
641
- </tr>
642
- </thead>
643
- <tbody id="globalLeaderboard">
644
- <!-- Filled by JS -->
645
- </tbody>
646
- </table>
647
  </div>
648
- </section>
649
- </div>
650
-
651
- <div id="history-tab" class="tab-content" style="display: none;">
652
- <section class="history-section">
653
- <h2 class="section-title"><i class="fas fa-history"></i> История Блоков</h2>
654
- <div class="blocks-grid" id="blocksGrid">
655
- <!-- Filled by JS -->
656
  </div>
657
- </section>
658
- </div>
659
- </div>
660
- </div>
661
-
662
- <!-- Block Details Modal -->
663
- <div id="blockModal" class="modal">
664
- <div class="modal-content">
665
- <span class="close-modal" onclick="closeModal('blockModal')">&times;</span>
666
- <h2 class="modal-title">Блок #<span id="modalBlockNumber"></span></h2>
667
-
668
- <div class="block-info" style="margin-bottom: 20px;">
669
- <div class="info-card">
670
- <h3>Создатель</h3>
671
- <p id="modalCreator">Username</p>
672
- </div>
673
- <div class="info-card">
674
- <h3>Дата завершения</h3>
675
- <p id="modalDate">01.01.2023</p>
676
- </div>
677
- <div class="info-card">
678
- <h3>Всего пикселей</h3>
679
- <p id="modalPixels">100</p>
680
- </div>
681
- <div class="info-card">
682
- <h3>Редкие пиксели</h3>
683
- <p id="modalRare">3</p>
684
  </div>
685
  </div>
686
-
687
- <div style="text-align: center; margin: 20px 0;">
688
- <div id="modalBlockPreview" class="pixel-canvas" style="display: inline-grid; width: 200px;"></div>
689
- </div>
690
-
691
- <div class="section-title" style="margin-top: 30px;">
692
- <i class="fas fa-crown"></i> Топ игроков блока
693
- </div>
694
-
695
- <div class="table-responsive">
696
  <table class="leaderboard">
697
  <thead>
698
  <tr>
699
- <th class="rank">#</th>
700
- <th>Игрок</th>
701
- <th>Пикселей</th>
702
  <th>Очки</th>
703
  </tr>
704
  </thead>
705
- <tbody id="modalLeaderboard">
706
- <!-- Filled by JS -->
707
  </tbody>
708
  </table>
709
  </div>
710
-
711
- <div style="text-align: center; margin-top: 20px;">
712
- <button class="mine-btn" style="padding: 10px 20px;">
713
- <i class="fas fa-user-astronaut"></i> Сделать Аватаром
714
- </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
715
  </div>
716
  </div>
717
- </div>
718
-
719
- <!-- Notification -->
720
- <div id="notification" class="notification">
721
- <div class="notification-title">Пиксель добыт!</div>
722
- <div class="notification-message">Вы получили <span class="pix-earned">15</span> ПИКС</div>
723
  </div>
724
 
725
  <script>
726
- // Game configuration
727
- const config = {
728
- currentBlock: 42,
729
- blockSize: 100,
730
- minedPixels: 76,
731
- baseReward: 10,
732
- rewards: {
733
- common: 10,
734
- silver: 30,
735
- gold: 75,
736
- diamond: 200
737
- },
738
- rareChances: {
739
- silver: 0.1,
740
- gold: 0.025,
741
- diamond: 0.005
742
- }
743
- };
744
-
745
- // Sample data
746
- const sampleUsers = [
747
- { id: 1, name: "User1", avatar: "https://i.pravatar.cc/150?img=1", pixels: 24, points: 420 },
748
- { id: 2, name: "User2", avatar: "https://i.pravatar.cc/150?img=2", pixels: 18, points: 315 },
749
- { id: 3, name: "User3", avatar: "https://i.pravatar.cc/150?img=3", pixels: 12, points: 210 },
750
- { id: 4, name: "User4", avatar: "https://i.pravatar.cc/150?img=4", pixels: 10, points: 175 },
751
- { id: 5, name: "User5", avatar: "https://i.pravatar.cc/150?img=5", pixels: 8, points: 140 }
752
- ];
753
-
754
- const globalLeaders = [
755
- { id: 1, name: "TopMiner", avatar: "https://i.pravatar.cc/150?img=6", blocks: 12, points: 12450 },
756
- { id: 2, name: "PixMaster", avatar: "https://i.pravatar.cc/150?img=7", blocks: 9, points: 9860 },
757
- { id: 3, name: "BlockCreator", avatar: "https://i.pravatar.cc/150?img=8", blocks: 7, points: 7820 },
758
- { id: 4, name: "User5", avatar: "https://i.pravatar.cc/150?img=5", blocks: 6, points: 6540 },
759
- { id: 5, name: "Newbie", avatar: "https://i.pravatar.cc/150?img=9", blocks: 5, points: 5210 },
760
- { id: 6, name: "ProMiner", avatar: "https://i.pravatar.cc/150?img=10", blocks: 4, points: 4320 },
761
- { id: 7, name: "PixelKing", avatar: "https://i.pravatar.cc/150?img=11", blocks: 3, points: 3210 },
762
- { id: 8, name: "DiamondHunter", avatar: "https://i.pravatar.cc/150?img=12", blocks: 2, points: 2450 },
763
- { id: 9, name: "SilverCollector", avatar: "https://i.pravatar.cc/150?img=13", blocks: 2, points: 1980 },
764
- { id: 10, name: "LuckyOne", avatar: "https://i.pravatar.cc/150?img=14", blocks: 1, points: 1250 }
765
- ];
766
-
767
- const blocksHistory = [
768
- { id: 41, creator: "TopMiner", date: "20.05.2023", pixels: 90, rare: 4, image: generateFakeBlock(10, 9, 41) },
769
- { id: 40, creator: "PixMaster", date: "18.05.2023", pixels: 81, rare: 3, image: generateFakeBlock(9, 9, 40) },
770
- { id: 39, creator: "BlockCreator", date: "15.05.2023", pixels: 64, rare: 2, image: generateFakeBlock(8, 8, 39) },
771
- { id: 38, creator: "User5", date: "12.05.2023", pixels: 49, rare: 1, image: generateFakeBlock(7, 7, 38) },
772
- { id: 37, creator: "ProMiner", date: "10.05.2023", pixels: 36, rare: 1, image: generateFakeBlock(6, 6, 37) },
773
- { id: 36, creator: "Newbie", date: "08.05.2023", pixels: 25, rare: 0, image: generateFakeBlock(5, 5, 36) }
774
- ];
775
-
776
- // Generate a fake block image for the history
777
- function generateFakeBlock(width, height, seed) {
778
- const types = ['common', 'silver', 'gold', 'diamond'];
779
- const pixels = [];
780
 
781
- // Simple seeded random function
782
- function random(min, max) {
783
- const x = Math.sin(seed++) * 10000;
784
- return Math.floor((x - Math.floor(x)) * (max - min + 1) + min);
785
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
786
 
787
- for (let y = 0; y < height; y++) {
788
- const row = [];
789
- for (let x = 0; x < width; x++) {
790
- const rand = random(0, 100);
791
- let type = 'common';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
792
 
793
- if (rand > 99) type = 'diamond';
794
- else if (rand > 97) type = 'gold';
795
- else if (rand > 90) type = 'silver';
 
 
 
796
 
797
- row.push(type);
 
 
 
 
 
 
 
798
  }
799
- pixels.push(row);
800
- }
801
-
802
- return {
803
- width,
804
- height,
805
- pixels
806
  };
807
- }
808
-
809
- // Current state
810
- let currentUser = {
811
- id: 5,
812
- name: "User5",
813
- avatar: "https://i.pravatar.cc/150?img=5",
814
- balance: 1240,
815
- mined: 0,
816
- points: 0
817
- };
818
-
819
- let blockPixels = generateInitialBlock(10, 10);
820
-
821
- // Initialize the game
822
- document.addEventListener('DOMContentLoaded', function() {
823
- renderCanvas();
824
- renderBlockLeaderboard();
825
- renderGlobalLeaderboard();
826
- renderBlocksHistory();
827
-
828
- // Simulate some mined pixels (for demo purposes)
829
- setTimeout(() => {
830
- simulateMinedPixels();
831
- }, 1000);
832
- });
833
-
834
- // Generate initial block with given width and height
835
- function generateInitialBlock(width, height) {
836
- const pixels = [];
837
- const total = width * height;
838
- const minedCount = Math.floor(total * 0.76); // 76% mined
839
 
840
- // Create empty pixel grid
841
- for (let y = 0; y < height; y++) {
842
- const row = [];
843
- for (let x = 0; x < width; x++) {
844
- row.push({
845
- x,
846
- y,
847
- mined: false,
848
- type: 'common',
849
- miner: null,
850
- value: 0
851
- });
852
- }
853
- pixels.push(row);
854
- }
855
-
856
- // Mark some pixels as mined (for demo)
857
- for (let i = 0; i < minedCount; i++) {
858
- const x = Math.floor(Math.random() * width);
859
- const y = Math.floor(Math.random() * height);
860
 
861
- if (!pixels[y][x].mined) {
862
- const rand = Math.random() * 100;
863
- let type = 'common';
864
- let value = config.rewards.common;
865
-
866
- if (rand > 99.5) {
867
- type = 'diamond';
868
- value = config.rewards.diamond;
869
- } else if (rand > 97.5) {
870
- type = 'gold';
871
- value = config.rewards.gold;
872
- } else if (rand > 90) {
873
- type = 'silver';
874
- value = config.rewards.silver;
875
- }
876
 
877
- const miner = sampleUsers[Math.floor(Math.random() * sampleUsers.length)];
 
 
 
878
 
879
- pixels[y][x] = {
880
- x,
881
- y,
882
- mined: true,
883
- type,
884
- miner,
885
- value
886
- };
887
- } else {
888
- i--; // Try again if this pixel is already mined
889
  }
890
  }
891
 
892
- return pixels;
893
- }
894
-
895
- // Render the pixel canvas
896
- function renderCanvas() {
897
- const canvas = document.getElementById('pixelCanvas');
898
- canvas.innerHTML = '';
899
-
900
- const width = blockPixels[0].length;
901
- const height = blockPixels.length;
902
-
903
- canvas.style.gridTemplateColumns = `repeat(${width}, 1fr)`;
904
- canvas.style.gridTemplateRows = `repeat(${height}, 1fr)`;
 
 
 
 
 
 
 
905
 
906
- blockPixels.forEach(row => {
907
- row.forEach(pixel => {
908
- const pixelEl = document.createElement('div');
909
- pixelEl.className = 'pixel' + (pixel.mined ? ` mined ${pixel.type}` : '');
 
 
910
 
911
- if (pixel.mined) {
912
- pixelEl.innerHTML = `<div class="pixel-value">${pixel.value}</div>`;
913
-
914
- if (pixel.miner.id === currentUser.id) {
915
- pixelEl.style.boxShadow = 'inset 0 0 0 2px var(--primary-color)';
916
- }
917
- }
918
-
919
- canvas.appendChild(pixelEl);
920
- });
921
- });
922
- }
923
-
924
- // Simulate some mined pixels (for demo)
925
- function simulateMinedPixels() {
926
- const width = blockPixels[0].length;
927
- const height = blockPixels.length;
928
- const unmined = [];
929
-
930
- // Find all unmined pixels
931
- for (let y = 0; y < height; y++) {
932
- for (let x = 0; x < width; x++) {
933
- if (!blockPixels[y][x].mined) {
934
- unmined.push({x, y});
935
- }
936
  }
937
- }
938
-
939
- // "Mine" a few pixels
940
- for (let i = 0; i < 3; i++) {
941
- if (unmined.length === 0) break;
942
-
943
- const idx = Math.floor(Math.random() * unmined.length);
944
- const {x, y} = unmined[idx];
945
- unmined.splice(idx, 1);
946
 
947
- const rand = Math.random() * 100;
948
- let type = 'common';
949
- let value = config.rewards.common;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
950
 
951
- if (rand > 99.5) {
952
- type = 'diamond';
953
- value = config.rewards.diamond;
954
- } else if (rand > 97.5) {
955
- type = 'gold';
956
- value = config.rewards.gold;
957
- } else if (rand > 90) {
958
- type = 'silver';
959
- value = config.rewards.silver;
 
 
 
 
 
 
960
  }
961
 
962
- const miner = sampleUsers[Math.floor(Math.random() * sampleUsers.length)];
963
- blockPixels[y][x] = {x, y, mined: true, type, miner, value};
 
 
964
 
965
- // Animate the pixel
966
- setTimeout(() => {
967
- const canvas = document.getElementById('pixelCanvas');
968
- const pixelEl = canvas.children[y * width + x];
969
- pixelEl.className = 'pixel mined ' + type + ' reveal';
970
- pixelEl.innerHTML = `<div class="pixel-value">${value}</div>`;
971
-
972
- if (miner.id === currentUser.id) {
973
- pixelEl.style.boxShadow = 'inset 0 0 0 2px var(--primary-color)';
974
- showNotification(`Вы получили ${value} ПИКС!`, type);
975
- }
976
- }, i * 300);
977
  }
978
 
979
- // Update progress
980
- updateProgress();
981
- }
982
-
983
- // Mine a pixel
984
- function minePixel() {
985
- const btn = document.getElementById('mineBtn');
986
- btn.disabled = true;
987
- btn.innerHTML = '<i class="fas fa-circle-notch fa-spin"></i> Добыча...';
988
-
989
- // Simulate mining delay
990
- setTimeout(() => {
991
- const width = blockPixels[0].length;
992
- const height = blockPixels.length;
993
- const unmined = [];
994
-
995
- // Find all unmined pixels
996
- for (let y = 0; y < height; y++) {
997
- for (let x = 0; x < width; x++) {
998
- if (!blockPixels[y][x].mined) {
999
- unmined.push({x, y});
1000
- }
1001
- }
1002
- }
1003
-
1004
- if (unmined.length === 0) {
1005
- // Block is completed
1006
- alert('Этот блок уже полностью добыт!');
1007
- btn.disabled = false;
1008
- btn.innerHTML = '<i class="fas fa-hammer"></i> Добыть Пиксель';
1009
- return;
1010
- }
1011
 
1012
- // Randomly select a pixel to mine
1013
- const idx = Math.floor(Math.random() * unmined.length);
1014
- const {x, y} = unmined[idx];
 
 
 
1015
 
1016
- // Determine pixel type
1017
- const rand = Math.random() * 100;
1018
- let type = 'common';
1019
- let value = config.rewards.common;
1020
 
1021
- if (rand > 99.5) {
1022
- type = 'diamond';
1023
- value = config.rewards.diamond;
1024
- } else if (rand > 97.5) {
1025
- type = 'gold';
1026
- value = config.rewards.gold;
1027
- } else if (rand > 90) {
1028
- type = 'silver';
1029
- value = config.rewards.silver;
1030
- }
1031
 
1032
- // Mine the pixel
1033
- blockPixels[y][x] = {
1034
- x,
1035
- y,
1036
- mined: true,
1037
- type,
1038
- miner: currentUser,
1039
- value
1040
- };
1041
 
1042
- // Update user stats
1043
- currentUser.balance += value;
1044
- currentUser.mined++;
1045
- currentUser.points += value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1046
 
1047
- // Update UI
1048
- updateUserBalance();
1049
- updateProgress();
1050
 
1051
- const canvas = document.getElementById('pixelCanvas');
1052
- const pixelEl = canvas.children[y * width + x];
1053
- pixelEl.className = 'pixel mined ' + type + ' reveal';
1054
- pixelEl.innerHTML = `<div class="pixel-value">${value}</div>`;
1055
- pixelEl.style.boxShadow = 'inset 0 0 0 2px var(--primary-color)';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1056
 
1057
- // Show notification
1058
- showNotification(`Вы получили ${value} ПИКС!`, type);
 
1059
 
1060
- // Update leaderboard
1061
- renderBlockLeaderboard();
1062
 
1063
- // Re-enable button
1064
- btn.disabled = false;
1065
- btn.innerHTML = '<i class="fas fa-hammer"></i> Добыть Пиксель';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1066
 
1067
- // Check if block is completed
1068
- if (unmined.length === 1) { // We just mined the last one
1069
- setTimeout(() => {
1070
- alert(`Блок #${config.currentBlock} завершен! Вы заработали ${currentUser.points} ПИКС в этом блоке.`);
1071
- // Here you would typically move to the next block
1072
- }, 500);
1073
  }
1074
-
1075
- }, 1000);
1076
- }
1077
-
1078
- // Update mining progress
1079
- function updateProgress() {
1080
- const width = blockPixels[0].length;
1081
- const height = blockPixels.length;
1082
- let mined = 0;
1083
 
1084
- for (let y = 0; y < height; y++) {
1085
- for (let x = 0; x < width; x++) {
1086
- if (blockPixels[y][x].mined) mined++;
 
 
 
 
 
 
 
 
 
 
 
 
1087
  }
1088
  }
1089
 
1090
- config.minedPixels = mined;
1091
- // In a real app, you would update the progress display here
1092
- }
1093
-
1094
- // Update user balance display
1095
- function updateUserBalance() {
1096
- document.getElementById('userBalance').textContent = currentUser.balance.toLocaleString();
1097
- }
1098
-
1099
- // Render block leaderboard
1100
- function renderBlockLeaderboard() {
1101
- const leaderboard = document.getElementById('blockLeaderboard');
1102
- leaderboard.innerHTML = '';
1103
-
1104
- // Sort users by points in this block
1105
- const sortedUsers = [...sampleUsers].sort((a, b) => b.points - a.points);
1106
-
1107
- sortedUsers.forEach((user, index) => {
1108
- const row = document.createElement('tr');
1109
 
1110
- if (user.id === currentUser.id) {
1111
- row.style.backgroundColor = 'rgba(78, 204, 163, 0.1)';
 
1112
  }
1113
 
1114
- row.innerHTML = `
1115
- <td class="rank">${index + 1}</td>
1116
- <td class="user">
1117
- <img src="${user.avatar}" alt="${user.name}" class="user-avatar">
1118
- ${user.name} ${user.id === currentUser.id ? '<span style="color: var(--primary-color)">(Вы)</span>' : ''}
1119
- </td>
1120
- <td>${user.pixels}</td>
1121
- <td class="pix">
1122
- <i class="fas fa-coins"></i> ${user.points}
1123
- </td>
1124
- `;
1125
-
1126
- leaderboard.appendChild(row);
1127
- });
1128
- }
1129
-
1130
- // Render global leaderboard
1131
- function renderGlobalLeaderboard() {
1132
- const leaderboard = document.getElementById('globalLeaderboard');
1133
- leaderboard.innerHTML = '';
 
 
1134
 
1135
- globalLeaders.forEach((user, index) => {
1136
- const row = document.createElement('tr');
 
1137
 
1138
- if (user.id === currentUser.id) {
1139
- row.style.backgroundColor = 'rgba(78, 204, 163, 0.1)';
 
 
 
1140
  }
1141
 
1142
- row.innerHTML = `
1143
- <td class="rank">${index + 1}</td>
1144
- <td class="user">
1145
- <img src="${user.avatar}" alt="${user.name}" class="user-avatar">
1146
- ${user.name} ${user.id === currentUser.id ? '<span style="color: var(--primary-color)">(Вы)</span>' : ''}
1147
- </td>
1148
- <td>${user.blocks}</td>
1149
- <td class="pix">
1150
- <i class="fas fa-coins"></i> ${user.points.toLocaleString()}
1151
- </td>
1152
- `;
1153
-
1154
- leaderboard.appendChild(row);
1155
- });
1156
- }
1157
-
1158
- // Render blocks history
1159
- function renderBlocksHistory() {
1160
- const grid = document.getElementById('blocksGrid');
1161
- grid.innerHTML = '';
1162
-
1163
- blocksHistory.forEach(block => {
1164
- const blockEl = document.createElement('div');
1165
- blockEl.className = 'block-card';
1166
- blockEl.onclick = () => openBlockModal(block);
1167
 
1168
- // Create block preview
1169
- const preview = document.createElement('div');
1170
- preview.className = 'block-preview';
 
1171
 
1172
- // Create a small grid to show the block
1173
- const blockGrid = document.createElement('div');
1174
- blockGrid.className = 'pixel-canvas';
1175
- blockGrid.style.width = '100%';
1176
- blockGrid.style.height = '100%';
1177
- blockGrid.style.gridTemplateColumns = `repeat(${block.image.width}, 1fr)`;
1178
- blockGrid.style.gridTemplateRows = `repeat(${block.image.height}, 1fr)`;
 
 
 
 
 
 
 
 
 
 
 
 
 
1179
 
1180
- // Add pixels to the grid
1181
- block.image.pixels.forEach((row, y) => {
1182
- row.forEach((type, x) => {
1183
- const pixel = document.createElement('div');
1184
- pixel.className = 'pixel mined ' + type;
1185
- pixel.style.width = '100%';
1186
- pixel.style.height = '100%';
1187
- blockGrid.appendChild(pixel);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1188
  });
1189
  });
1190
 
1191
- preview.appendChild(blockGrid);
1192
- blockEl.appendChild(preview);
1193
-
1194
- // Add block info
1195
- const info = document.createElement('div');
1196
- info.className = 'block-info-overlay';
1197
- info.innerHTML = `
1198
- <div class="block-number">Блок #${block.id}</div>
1199
- <div class="block-creator">Создатель: ${block.creator}</div>
1200
- `;
1201
- blockEl.appendChild(info);
1202
-
1203
- grid.appendChild(blockEl);
1204
- });
1205
- }
1206
-
1207
- // Show notification
1208
- function showNotification(message, type) {
1209
- const notif = document.getElementById('notification');
1210
- const messageEl = notif.querySelector('.notification-message');
1211
-
1212
- notif.style.backgroundColor = type === 'diamond' ? 'var(--diamond-color)' :
1213
- type === 'gold' ? 'var(--gold-color)' :
1214
- type === 'silver' ? 'var(--silver-color)' : 'var(--primary-color)';
1215
-
1216
- if (type === 'diamond' || type === 'gold' || type === 'silver') {
1217
- notif.style.color = '#000';
1218
- messageEl.innerHTML = `Редкий пиксель! ${message} <span class="badge ${type}">${type === 'silver' ? 'Серебро' : type === 'gold' ? 'Золото' : 'Бриллиант'}</span>`;
1219
- } else {
1220
- notif.style.color = '#fff';
1221
- messageEl.innerHTML = message;
1222
- }
1223
-
1224
- notif.classList.add('show');
1225
-
1226
- setTimeout(() => {
1227
- notif.classList.remove('show');
1228
- }, 3000);
1229
- }
1230
-
1231
- // Tab switching
1232
- function showTab(tabName) {
1233
- // Hide all tabs
1234
- document.querySelectorAll('.tab-content').forEach(tab => {
1235
- tab.style.display = 'none';
1236
- });
1237
-
1238
- // Deactivate all tab buttons
1239
- document.querySelectorAll('.tab').forEach(tab => {
1240
- tab.classList.remove('active');
1241
- });
1242
-
1243
- // Show selected tab
1244
- document.getElementById(`${tabName}-tab`).style.display = 'block';
1245
-
1246
- // Activate selected tab button
1247
- const tabButtons = document.querySelectorAll('.tab');
1248
- let found = false;
1249
-
1250
- tabButtons.forEach(tab => {
1251
- if (tab.textContent.includes(tabName === 'mining' ? 'Майнинг' :
1252
- tabName === 'profile' ? 'Профиль' :
1253
- tabName === 'wallet' ? 'Кошелек' :
1254
- tabName === 'leaderboard' ? 'Топ' :
1255
- 'История')) {
1256
- tab.classList.add('active');
1257
- found = true;
1258
- }
1259
- });
1260
-
1261
- if (!found && tabButtons.length > 0) {
1262
- tabButtons[0].classList.add('active');
1263
- }
1264
-
1265
- // For leaderboard tab, ensure it's rendered (in case of updates)
1266
- if (tabName === 'leaderboard') {
1267
- renderGlobalLeaderboard();
1268
- }
1269
- }
1270
-
1271
- // Open block modal
1272
- function openBlockModal(block) {
1273
- const modal = document.getElementById('blockModal');
1274
- const preview = document.getElementById('modalBlockPreview');
1275
-
1276
- // Set block info
1277
- document.getElementById('modalBlockNumber').textContent = block.id;
1278
- document.getElementById('modalCreator').textContent = block.creator;
1279
- document.getElementById('modalDate').textContent = block.date;
1280
- document.getElementById('modalPixels').textContent = block.pixels;
1281
- document.getElementById('modalRare').textContent = block.rare;
1282
-
1283
- // Create block preview
1284
- preview.innerHTML = '';
1285
- preview.style.gridTemplateColumns = `repeat(${block.image.width}, 1fr)`;
1286
- preview.style.gridTemplateRows = `repeat(${block.image.height}, 1fr)`;
1287
-
1288
- block.image.pixels.forEach((row, y) => {
1289
- row.forEach((type, x) => {
1290
- const pixel = document.createElement('div');
1291
- pixel.className = 'pixel mined ' + type;
1292
- pixel.style.width = '100%';
1293
- pixel.style.height = '100%';
1294
- preview.appendChild(pixel);
1295
  });
1296
- });
1297
-
1298
- // Update leaderboard in modal (mock data)
1299
- const modalLeaderboard = document.getElementById('modalLeaderboard');
1300
- modalLeaderboard.innerHTML = '';
1301
-
1302
- const topMiners = [
1303
- { name: block.creator, avatar: "https://i.pravatar.cc/150?img=6", pixels: Math.floor(block.pixels * 0.3), points: 1250 },
1304
- { name: "OtherUser", avatar: "https://i.pravatar.cc/150?img=3", pixels: Math.floor(block.pixels * 0.2), points: 850 },
1305
- { name: "PixCollector", avatar: "https://i.pravatar.cc/150?img=7", pixels: Math.floor(block.pixels * 0.15), points: 640 },
1306
- { name: "Miner123", avatar: "https://i.pravatar.cc/150?img=4", pixels: Math.floor(block.pixels * 0.1), points: 420 }
1307
- ];
1308
 
1309
- topMiners.forEach((user, index) => {
1310
- const row = document.createElement('tr');
 
 
 
 
 
1311
 
1312
- row.innerHTML = `
1313
- <td class="rank">${index + 1}</td>
1314
- <td class="user">
1315
- <img src="${user.avatar}" alt="${user.name}" class="user-avatar">
1316
- ${user.name}
1317
- </td>
1318
- <td>${user.pixels}</td>
1319
- <td class="pix">
1320
- <i class="fas fa-coins"></i> ${user.points}
1321
- </td>
1322
- `;
1323
 
1324
- modalLeaderboard.appendChild(row);
1325
- });
 
 
 
 
 
1326
 
1327
- modal.style.display = 'flex';
1328
- }
1329
-
1330
- // Close modal
1331
- function closeModal(modalId) {
1332
- document.getElementById(modalId).style.display = 'none';
1333
- }
1334
  </script>
1335
- <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: absolute; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">This website has been generated by <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
1336
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Пиксельный майнинг</title>
7
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
  <style>
9
  :root {
10
  --bg-color: #1a1a2e;
11
+ --panel-bg: #16213e;
 
 
 
 
 
 
 
12
  --text-color: #e6e6e6;
13
+ --accent-color: #4cc9f0;
14
+ --rare-silver: #c0c0c0;
15
+ --rare-gold: #ffd700;
16
+ --rare-diamond: #b9f2ff;
17
+ --pixel-size: 16px;
18
  }
19
+
20
  * {
21
  margin: 0;
22
  padding: 0;
23
  box-sizing: border-box;
24
+ -webkit-tap-highlight-color: transparent;
25
  }
26
+
27
  body {
28
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
29
  background-color: var(--bg-color);
30
  color: var(--text-color);
31
  min-height: 100vh;
32
  overflow-x: hidden;
33
  }
34
+
35
  .container {
36
+ max-width: 100%;
37
+ padding: 12px;
38
  margin: 0 auto;
 
39
  }
40
+
41
  header {
42
  display: flex;
43
  justify-content: space-between;
44
  align-items: center;
45
+ padding: 8px 12px;
46
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
47
+ margin-bottom: 12px;
48
  }
49
+
50
  .logo {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  font-weight: bold;
52
+ font-size: 1.2rem;
53
+ color: var(--accent-color);
54
  }
55
+
56
+ .user-info {
 
 
 
 
 
 
 
 
 
57
  display: flex;
58
  align-items: center;
59
+ gap: 8px;
60
  }
61
+
62
+ .user-avatar {
63
+ width: 32px;
64
+ height: 32px;
65
  border-radius: 50%;
66
+ background-color: var(--panel-bg);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  }
68
+
 
 
 
 
 
 
 
 
 
 
69
  .mining-section {
 
 
 
 
 
 
 
70
  display: flex;
71
+ flex-direction: column;
72
+ gap: 16px;
73
  margin-bottom: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  }
75
+
76
+ .canvas-container {
 
 
 
 
 
77
  position: relative;
78
+ margin: 0 auto;
79
+ border: 2px solid var(--panel-bg);
80
+ border-radius: 8px;
81
+ overflow: hidden;
82
+ background-color: #111;
83
  }
84
+
85
  .pixel-canvas {
86
  display: grid;
87
+ grid-template-columns: repeat(var(--cols), var(--pixel-size));
88
+ grid-template-rows: repeat(var(--rows), var(--pixel-size));
 
 
89
  }
90
+
91
  .pixel {
92
+ background-color: #222;
93
+ border: 1px solid #111;
 
94
  cursor: pointer;
95
+ transition: background-color 0.2s;
 
 
96
  }
97
+
98
  .pixel:hover {
99
+ opacity: 0.9;
100
  }
101
+
102
+ .pixel-mined {
103
+ background-color: #444;
104
  }
105
+
106
+ .pixel-special {
107
+ position: relative;
108
  }
109
+
110
+ .pixel-silver {
111
+ background-color: var(--rare-silver);
112
  }
113
+
114
+ .pixel-gold {
115
+ background-color: var(--rare-gold);
116
  }
117
+
118
+ .pixel-diamond {
119
+ background-color: var(--rare-diamond);
 
 
 
 
 
 
120
  }
121
+
122
+ .mining-info {
123
+ background-color: var(--panel-bg);
124
+ border-radius: 8px;
125
+ padding: 12px;
126
  display: flex;
127
+ flex-direction: column;
128
+ gap: 12px;
129
  }
130
+
131
+ .block-info {
132
+ display: flex;
133
+ justify-content: space-between;
134
+ }
135
+
136
+ .progress-container {
137
+ background-color: #333;
138
+ border-radius: 999px;
139
+ height: 8px;
140
+ margin-top: 4px;
141
+ overflow: hidden;
142
+ }
143
+
144
+ .progress-bar {
145
+ height: 100%;
146
+ background-color: var(--accent-color);
147
+ border-radius: 999px;
148
+ transition: width 0.3s;
149
+ }
150
+
151
+ .mining-button {
152
+ background-color: var(--accent-color);
153
+ color: #111;
154
  border: none;
155
+ border-radius: 8px;
156
+ padding: 12px;
 
 
 
157
  font-weight: bold;
158
+ font-size: 1rem;
159
+ cursor: pointer;
160
+ transition: transform 0.2s, background-color 0.2s;
161
  display: flex;
162
  align-items: center;
163
+ justify-content: center;
164
+ gap: 8px;
165
+ }
166
+
167
+ .mining-button:active {
168
+ transform: scale(0.98);
169
+ background-color: #3aa8d8;
170
+ }
171
+
172
+ .mining-button i {
173
+ font-size: 1.2rem;
174
+ }
175
+
176
+ .stats-section {
177
+ background-color: var(--panel-bg);
178
+ border-radius: 8px;
179
+ padding: 12px;
180
+ margin-bottom: 16px;
181
+ }
182
+
183
+ .stats-grid {
184
+ display: grid;
185
+ grid-template-columns: 1fr 1fr;
186
+ gap: 12px;
187
+ margin-top: 12px;
188
  }
189
+
190
+ .stat-item {
191
+ background-color: rgba(0, 0, 0, 0.2);
192
+ border-radius: 6px;
193
+ padding: 8px;
194
+ text-align: center;
195
  }
196
+
197
+ .stat-value {
198
+ font-size: 1.2rem;
199
+ font-weight: bold;
200
+ margin-top: 4px;
201
+ color: var(--accent-color);
202
  }
203
+
204
+ .tab-container {
205
+ display: flex;
206
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
207
+ margin-bottom: 12px;
208
  }
209
+
210
+ .tab {
211
+ padding: 8px 12px;
212
+ cursor: pointer;
213
+ position: relative;
214
  }
215
+
216
+ .tab.active {
217
+ color: var(--accent-color);
 
 
 
 
218
  }
219
+
220
+ .tab.active::after {
221
+ content: '';
222
+ position: absolute;
223
+ bottom: -1px;
224
+ left: 0;
225
+ width: 100%;
226
+ height: 2px;
227
+ background-color: var(--accent-color);
228
+ }
229
+
230
+ .tab-content {
231
+ display: none;
232
+ }
233
+
234
+ .tab-content.active {
235
+ display: block;
236
+ }
237
+
238
  .leaderboard {
239
  width: 100%;
240
  border-collapse: collapse;
241
  }
242
+
243
+ .leaderboard th {
 
244
  text-align: left;
245
+ padding: 8px 12px;
246
+ background-color: rgba(0, 0, 0, 0.2);
247
+ }
248
+
249
+ .leaderboard td {
250
+ padding: 8px 12px;
251
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
252
+ }
253
+
254
+ .leaderboard tr:last-child td {
255
+ border-bottom: none;
256
+ }
257
+
258
+ .medal {
259
+ width: 18px;
260
+ height: 18px;
261
+ border-radius: 50%;
262
+ display: inline-flex;
263
+ align-items: center;
264
+ justify-content: center;
265
+ margin-right: 6px;
266
  }
267
+
268
+ .medal-gold {
269
+ background-color: var(--rare-gold);
270
+ color: #333;
271
  }
272
+
273
+ .medal-silver {
274
+ background-color: var(--rare-silver);
275
+ color: #333;
276
  }
277
+
278
+ .medal-bronze {
279
+ background-color: #cd7f32;
280
+ color: #333;
281
  }
282
+
283
+ .history-item {
284
  display: flex;
285
+ justify-content: space-between;
286
+ padding: 8px 0;
287
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
288
  }
289
+
290
+ .history-item:last-child {
291
+ border-bottom: none;
 
 
292
  }
293
+
294
+ .rare-icon {
295
+ font-size: 0.8rem;
296
+ margin-left: 4px;
 
 
 
297
  }
298
+
299
+ .pixel-tooltip {
300
+ position: absolute;
301
+ top: -30px;
302
+ left: 50%;
303
+ transform: translateX(-50%);
304
+ background-color: rgba(0, 0, 0, 0.8);
305
+ padding: 4px 8px;
306
+ border-radius: 4px;
307
+ font-size: 0.8rem;
308
+ white-space: nowrap;
309
+ z-index: 10;
310
+ opacity: 0;
311
+ transition: opacity 0.2s;
312
  }
313
+
314
+ .pixel:hover .pixel-tooltip {
315
+ opacity: 1;
316
+ }
317
+
318
+ .avatar-grid {
319
  display: grid;
320
+ grid-template-columns: repeat(3, 1fr);
321
+ gap: 8px;
322
+ margin-top: 12px;
323
  }
324
+
325
+ .avatar-item {
326
+ aspect-ratio: 1/1;
327
+ background-color: #333;
328
+ border-radius: 8px;
329
+ display: flex;
330
+ align-items: center;
331
+ justify-content: center;
332
  cursor: pointer;
333
+ transition: transform 0.2s;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  overflow: hidden;
 
335
  }
336
+
337
+ .avatar-item img {
 
 
 
 
 
338
  width: 100%;
339
  height: 100%;
340
+ object-fit: cover;
 
 
 
341
  }
342
+
343
+ .avatar-item:active {
344
+ transform: scale(0.95);
 
 
 
 
 
 
 
345
  }
346
+
347
+ .avatar-item.selected {
348
+ border: 2px solid var(--accent-color);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
  }
350
+
351
+ @media (min-width: 500px) {
352
+ :root {
353
+ --pixel-size: 20px;
 
354
  }
355
 
356
+ .container {
357
+ max-width: 500px;
 
 
 
 
 
 
 
 
 
 
 
 
 
358
  }
359
+
360
+ .stats-grid {
361
+ grid-template-columns: repeat(4, 1fr);
362
  }
363
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
  </style>
365
  </head>
366
  <body>
367
  <div class="container">
368
  <header>
369
+ <div class="logo">PixelMiner</div>
370
+ <div class="user-info">
371
+ <span id="user-coins">0 PX</span>
372
+ <div class="user-avatar" id="user-avatar"></div>
373
  </div>
374
+ </header>
375
+
376
+ <div class="mining-section">
377
+ <div class="canvas-container">
378
+ <div class="pixel-canvas" id="pixel-canvas"></div>
 
 
 
 
 
 
 
 
 
 
 
379
  </div>
380
+
381
+ <div class="mining-info">
382
+ <div class="block-info">
383
+ <div>
384
+ <div>Блок #<span id="current-block">1</span></div>
385
+ <div class="progress-container">
386
+ <div class="progress-bar" id="block-progress" style="width: 0%"></div>
387
+ </div>
388
+ </div>
389
+ <div style="text-align: right;">
390
+ <div>Пикселей: <span id="mined-pixels">0</span>/<span id="total-pixels">0</span></div>
391
+ <div>Игроков: <span id="online-players">0</span></div>
392
+ </div>
393
  </div>
394
+
395
+ <button class="mining-button" id="mine-button">
396
+ <i class="fas fa-hammer"></i>
397
+ <span>Добыть пиксель</span>
398
+ </button>
399
  </div>
400
+ </div>
401
+
402
+ <div class="tab-container">
403
+ <div class="tab active" data-tab="stats">Статистика</div>
404
+ <div class="tab" data-tab="leaderboard">Лидеры</div>
405
+ <div class="tab" data-tab="history">История</div>
406
+ <div class="tab" data-tab="profile">Про��иль</div>
407
+ </div>
408
+
409
+ <div class="tab-content active" id="stats-content">
410
+ <div class="stats-section">
411
+ <div class="stats-grid">
412
+ <div class="stat-item">
413
+ <div>Ваши очки</div>
414
+ <div class="stat-value" id="user-score">0</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
415
  </div>
416
+ <div class="stat-item">
417
+ <div>Добыто</div>
418
+ <div class="stat-value" id="user-mined">0</div>
419
  </div>
420
+ <div class="stat-item">
421
+ <div>Серебряных</div>
422
+ <div class="stat-value" id="user-silver">0</div>
 
 
423
  </div>
424
+ <div class="stat-item">
425
+ <div>Золотых</div>
426
+ <div class="stat-value" id="user-gold">0</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
427
  </div>
428
+ <div class="stat-item">
429
+ <div>Бриллиантов</div>
430
+ <div class="stat-value" id="user-diamond">0</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  </div>
432
+ <div class="stat-item">
433
+ <div>Создано блоков</div>
434
+ <div class="stat-value" id="user-created">0</div>
435
+ </div>
436
+ <div class="stat-item">
437
+ <div>Место в топе</div>
438
+ <div class="stat-value" id="user-rank">-</div>
 
439
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
440
  </div>
441
  </div>
442
+ </div>
443
+
444
+ <div class="tab-content" id="leaderboard-content">
445
+ <div class="stats-section">
 
 
 
 
 
 
446
  <table class="leaderboard">
447
  <thead>
448
  <tr>
449
+ <th>#</th>
450
+ <th>Имя</th>
 
451
  <th>Очки</th>
452
  </tr>
453
  </thead>
454
+ <tbody id="leaderboard-body">
455
+ <!-- заполняется JS -->
456
  </tbody>
457
  </table>
458
  </div>
459
+ </div>
460
+
461
+ <div class="tab-content" id="history-content">
462
+ <div class="stats-section">
463
+ <div id="history-list">
464
+ <!-- заполняется JS -->
465
+ </div>
466
+ </div>
467
+ </div>
468
+
469
+ <div class="tab-content" id="profile-content">
470
+ <div class="stats-section">
471
+ <div>
472
+ <div style="text-align: center; margin-bottom: 12px;">
473
+ <div class="user-avatar" style="width: 80px; height: 80px; margin: 0 auto 8px;" id="profile-avatar"></div>
474
+ <div id="profile-name">Имя пользователя</div>
475
+ <div style="font-size: 0.9rem; color: var(--accent-color);" id="profile-pix">0 PX</div>
476
+ </div>
477
+
478
+ <div style="margin-top: 16px;">Созданные блоки:</div>
479
+ <div class="avatar-grid" id="avatar-grid">
480
+ <!-- заполняется JS -->
481
+ </div>
482
+ </div>
483
  </div>
484
  </div>
 
 
 
 
 
 
485
  </div>
486
 
487
  <script>
488
+ // Инициализация игры
489
+ document.addEventListener('DOMContentLoaded', function() {
490
+ // Конфигурация
491
+ const config = {
492
+ cols: 16,
493
+ rows: 16,
494
+ blockSizeIncrease: 2,
495
+ initialBlockSize: 16*16,
496
+ baseScore: 1,
497
+ rareChances: {
498
+ silver: 0.1,
499
+ gold: 0.03,
500
+ diamond: 0.01
501
+ },
502
+ rareScores: {
503
+ silver: 5,
504
+ gold: 15,
505
+ diamond: 50
506
+ },
507
+ updateInterval: 2000,
508
+ fakePlayers: 25
509
+ };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
 
511
+ // Состояние игры
512
+ const state = {
513
+ currentBlock: 1,
514
+ canvasSize: config.initialBlockSize,
515
+ minedPixels: 0,
516
+ onlinePlayers: 0,
517
+ playerData: {
518
+ score: 0,
519
+ mined: 0,
520
+ silver: 0,
521
+ gold: 0,
522
+ diamond: 0,
523
+ createdBlocks: 0,
524
+ pix: 0,
525
+ avatar: '',
526
+ name: 'Игрок'
527
+ },
528
+ leaderboard: [],
529
+ blockHistory: [],
530
+ createdBlocks: []
531
+ };
532
 
533
+ // DOM элементы
534
+ const elements = {
535
+ canvas: document.getElementById('pixel-canvas'),
536
+ currentBlock: document.getElementById('current-block'),
537
+ minedPixels: document.getElementById('mined-pixels'),
538
+ totalPixels: document.getElementById('total-pixels'),
539
+ onlinePlayers: document.getElementById('online-players'),
540
+ mineButton: document.getElementById('mine-button'),
541
+ blockProgress: document.getElementById('block-progress'),
542
+ userScore: document.getElementById('user-score'),
543
+ userMined: document.getElementById('user-mined'),
544
+ userSilver: document.getElementById('user-silver'),
545
+ userGold: document.getElementById('user-gold'),
546
+ userDiamond: document.getElementById('user-diamond'),
547
+ userCreated: document.getElementById('user-created'),
548
+ userRank: document.getElementById('user-rank'),
549
+ userCoins: document.getElementById('user-coins'),
550
+ userAvatar: document.getElementById('user-avatar'),
551
+ profileAvatar: document.getElementById('profile-avatar'),
552
+ profileName: document.getElementById('profile-name'),
553
+ profilePix: document.getElementById('profile-pix'),
554
+ leaderboardBody: document.getElementById('leaderboard-body'),
555
+ historyList: document.getElementById('history-list'),
556
+ avatarGrid: document.getElementById('avatar-grid'),
557
+ tabs: document.querySelectorAll('.tab'),
558
+ tabContents: document.querySelectorAll('.tab-content')
559
+ };
560
+
561
+ // Симуляция WebSocket соединения
562
+ let socket = {
563
+ connect: () => {
564
+ console.log('Connecting to WebSocket server...');
565
+ // Здесь будет реальное подключение через WebSocket
566
+ // Пока используем setTimeout для имитации сетевых событий
567
 
568
+ // Имитация получения данных от сервера
569
+ setInterval(() => {
570
+ updateOnlinePlayers();
571
+ updateBlockProgress();
572
+ updateLeaderboard();
573
+ }, config.updateInterval);
574
 
575
+ // Для демонстрации - имитация других игроков
576
+ setInterval(() => {
577
+ simulateOtherPlayers();
578
+ }, 1000);
579
+ },
580
+ send: (data) => {
581
+ console.log('Sending data:', data);
582
+ // Здесь будет отправка данных на сервер через WebSocket
583
  }
 
 
 
 
 
 
 
584
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
585
 
586
+ // Инициализация канваса
587
+ function initCanvas() {
588
+ elements.canvas.style.setProperty('--cols', config.cols);
589
+ elements.canvas.style.setProperty('--rows', config.rows);
590
+ elements.totalPixels.textContent = config.cols * config.rows;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
591
 
592
+ // Очищаем канвас
593
+ elements.canvas.innerHTML = '';
594
+
595
+ // Создаем пиксели
596
+ for (let i = 0; i < config.cols * config.rows; i++) {
597
+ const pixel = document.createElement('div');
598
+ pixel.className = 'pixel';
599
+ pixel.dataset.index = i;
 
 
 
 
 
 
 
600
 
601
+ // Добавляем tooltip
602
+ const tooltip = document.createElement('div');
603
+ tooltip.className = 'pixel-tooltip';
604
+ tooltip.textContent = 'Не добыт';
605
 
606
+ pixel.appendChild(tooltip);
607
+ elements.canvas.appendChild(pixel);
 
 
 
 
 
 
 
 
608
  }
609
  }
610
 
611
+ // Обновление счетчиков
612
+ function updateUI() {
613
+ elements.currentBlock.textContent = state.currentBlock;
614
+ elements.minedPixels.textContent = state.minedPixels;
615
+ elements.totalPixels.textContent = config.cols * config.rows;
616
+ elements.onlinePlayers.textContent = state.onlinePlayers;
617
+
618
+ elements.userScore.textContent = state.playerData.score;
619
+ elements.userMined.textContent = state.playerData.mined;
620
+ elements.userSilver.textContent = state.playerData.silver;
621
+ elements.userGold.textContent = state.playerData.gold;
622
+ elements.userDiamond.textContent = state.playerData.diamond;
623
+ elements.userCreated.textContent = state.playerData.createdBlocks;
624
+ elements.userCoins.textContent = state.playerData.pix + ' PX';
625
+ elements.profilePix.textContent = state.playerData.pix + ' PX';
626
+
627
+ // Обновление прогресса блока
628
+ const progress = (state.minedPixels / (config.cols * config.rows)) * 100;
629
+ elements.blockProgress.style.width = progress + '%';
630
+ }
631
 
632
+ // Добыча пикселя
633
+ function minePixel(index = null) {
634
+ // Если индекс не указан, выбираем случайный неподобранный пиксель
635
+ if (index === null) {
636
+ const unminedPixels = Array.from(document.querySelectorAll('.pixel:not(.pixel-mined)'));
637
+ if (unminedPixels.length === 0) return false;
638
 
639
+ const randomIndex = Math.floor(Math.random() * unminedPixels.length);
640
+ index = parseInt(unminedPixels[randomIndex].dataset.index);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
641
  }
 
 
 
 
 
 
 
 
 
642
 
643
+ const pixel = elements.canvas.children[index];
644
+ if (!pixel || pixel.classList.contains('pixel-mined')) return false;
645
+
646
+ // Определяем, является ли пиксель редким
647
+ const random = Math.random();
648
+ let pixelType = 'normal';
649
+ let score = config.baseScore;
650
+
651
+ if (random < config.rareChances.diamond) {
652
+ pixelType = 'diamond';
653
+ score = config.rareScores.diamond;
654
+ state.playerData.diamond++;
655
+ } else if (random < config.rareChances.gold) {
656
+ pixelType = 'gold';
657
+ score = config.rareScores.gold;
658
+ state.playerData.gold++;
659
+ } else if (random < config.rareChances.silver) {
660
+ pixelType = 'silver';
661
+ score = config.rareScores.silver;
662
+ state.playerData.silver++;
663
+ }
664
 
665
+ // Обновляем состояние
666
+ state.minedPixels++;
667
+ state.playerData.mined++;
668
+ state.playerData.score += score;
669
+ state.playerData.pix += score;
670
+
671
+ // Обновляем пиксель
672
+ pixel.classList.add('pixel-mined');
673
+ if (pixelType !== 'normal') {
674
+ pixel.classList.add('pixel-special', `pixel-${pixelType}`);
675
+ pixel.querySelector('.pixel-tooltip').innerHTML =
676
+ pixelType === 'silver' ? 'Серебряный ✨' :
677
+ pixelType === 'gold' ? 'Золотой ⭐' : 'Бриллиантовый 💎';
678
+ } else {
679
+ pixel.querySelector('.pixel-tooltip').textContent = 'Добыт';
680
  }
681
 
682
+ // Проверяем, завершен ли блок
683
+ if (state.minedPixels >= config.cols * config.rows) {
684
+ completeBlock();
685
+ }
686
 
687
+ updateUI();
688
+ return true;
 
 
 
 
 
 
 
 
 
 
689
  }
690
 
691
+ // Завершение блока
692
+ function completeBlock() {
693
+ // Находим игрока с максимальным количеством очков
694
+ // В реальном приложении это будет определяться сервером
695
+ state.playerData.createdBlocks++;
696
+
697
+ // Добавляем блок в историю
698
+ const blockData = {
699
+ id: state.currentBlock,
700
+ creator: state.playerData.name,
701
+ score: state.playerData.score,
702
+ pixels: state.minedPixels,
703
+ silver: state.playerData.silver,
704
+ gold: state.playerData.gold,
705
+ diamond: state.playerData.diamond,
706
+ completedAt: new Date()
707
+ };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
708
 
709
+ state.blockHistory.unshift(blockData);
710
+ state.createdBlocks.push({
711
+ id: state.currentBlock,
712
+ // В реальном приложении здесь будет ссылка на изображение холста
713
+ image: generateBlockImage()
714
+ });
715
 
716
+ // Переходим к следующему блоку
717
+ state.currentBlock++;
718
+ state.minedPixels = 0;
 
719
 
720
+ // Увеличиваем размер холста для следующего блока
721
+ config.cols += config.blockSizeIncrease;
722
+ config.rows += config.blockSizeIncrease;
 
 
 
 
 
 
 
723
 
724
+ // Инициализируем новый холст
725
+ initCanvas();
 
 
 
 
 
 
 
726
 
727
+ // Показываем уведомление
728
+ showBlockCompleteNotification(blockData);
729
+ }
730
+
731
+ // Генерация изображения блока (для демонстрации)
732
+ function generateBlockImage() {
733
+ // В реальном приложении это будет canvas.toDataURL()
734
+ return `https://picsum.photos/200/200?random=${state.currentBlock}`;
735
+ }
736
+
737
+ // Показать уведомление о завершении блока
738
+ function showBlockCompleteNotification(blockData) {
739
+ const notification = document.createElement('div');
740
+ notification.style.position = 'fixed';
741
+ notification.style.bottom = '20px';
742
+ notification.style.left = '50%';
743
+ notification.style.transform = 'translateX(-50%)';
744
+ notification.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
745
+ notification.style.color = 'white';
746
+ notification.style.padding = '12px 20px';
747
+ notification.style.borderRadius = '8px';
748
+ notification.style.zIndex = '1000';
749
+ notification.style.display = 'flex';
750
+ notification.style.alignItems = 'center';
751
+ notification.style.gap = '8px';
752
+ notification.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.3)';
753
+
754
+ notification.innerHTML = `
755
+ <div style="font-weight: bold; color: var(--accent-color)">Блок #${blockData.id} завершен!</div>
756
+ <div style="font-size: 0.9rem;">Создатель: ${blockData.creator}</div>
757
+ `;
758
 
759
+ document.body.appendChild(notification);
 
 
760
 
761
+ setTimeout(() => {
762
+ notification.style.transition = 'opacity 0.5s';
763
+ notification.style.opacity = '0';
764
+ setTimeout(() => notification.remove(), 500);
765
+ }, 3000);
766
+ }
767
+
768
+ // Обновление списка игроков онлайн
769
+ function updateOnlinePlayers() {
770
+ // В реальном приложении это будет получаться с сервера
771
+ state.onlinePlayers = Math.floor(Math.random() * 50) + config.fakePlayers;
772
+ updateUI();
773
+ }
774
+
775
+ // Обновление прогресса блока
776
+ function updateBlockProgress() {
777
+ // В реальном приложении это будет получаться с сервера
778
+ if (state.minedPixels < config.cols * config.rows) {
779
+ state.minedPixels += Math.floor(Math.random() * 5) + 1;
780
+
781
+ // Ограничиваем максимумом
782
+ if (state.minedPixels > config.cols * config.rows) {
783
+ state.minedPixels = config.cols * config.rows;
784
+ }
785
+
786
+ updateUI();
787
+ }
788
+ }
789
+
790
+ // Обновление таблицы лидеров
791
+ function updateLeaderboard() {
792
+ // В реальном приложении это будет получаться с сервера
793
+ const fakeData = Array.from({length: 10}, (_, i) => ({
794
+ id: i+1,
795
+ name: ['Игрок1', 'Игрок2', 'Игрок3', 'Игрок4', 'Игрок5', 'Игрок6', 'Игрок7', 'Игрок8'][i] || `Игрок${i+1}`,
796
+ score: Math.floor(Math.random() * 10000) + 1000,
797
+ avatar: `https://picsum.photos/200/200?random=${i+100}`
798
+ }));
799
+
800
+ // Добавляем текущего игрока, если его нет в топе
801
+ if (!fakeData.some(p => p.id === 999)) {
802
+ fakeData.push({
803
+ id: 999,
804
+ name: state.playerData.name,
805
+ score: state.playerData.score,
806
+ avatar: state.playerData.avatar
807
+ });
808
+ }
809
 
810
+ // Сортируем по очкам
811
+ fakeData.sort((a, b) => b.score - a.score);
812
+ state.leaderboard = fakeData.slice(0, 10);
813
 
814
+ // Обновляем UI
815
+ elements.leaderboardBody.innerHTML = '';
816
 
817
+ state.leaderboard.forEach((player, index) => {
818
+ const isCurrentUser = player.id === 999;
819
+ const row = document.createElement('tr');
820
+ row.style.background = isCurrentUser ? 'rgba(76, 201, 240, 0.2)' : 'transparent';
821
+
822
+ row.innerHTML = `
823
+ <td>
824
+ ${index < 3 ?
825
+ `<span class="medal ${index === 0 ? 'medal-gold' : index === 1 ? 'medal-silver' : 'medal-bronze'}">
826
+ ${index + 1}
827
+ </span>` :
828
+ index + 1
829
+ }
830
+ </td>
831
+ <td>
832
+ <div style="display: flex; align-items: center; gap: 8px;">
833
+ <img src="${player.avatar}" style="width: 24px; height: 24px; border-radius: 50%;">
834
+ ${player.name}
835
+ </div>
836
+ </td>
837
+ <td>${player.score.toLocaleString()}</td>
838
+ `;
839
+
840
+ elements.leaderboardBody.appendChild(row);
841
+
842
+ // Обновляем место текущего игрока
843
+ if (isCurrentUser) {
844
+ const rank = index + 1;
845
+ elements.userRank.textContent = rank;
846
+ }
847
+ });
848
 
849
+ // Если текущего ��грока нет в топе, показываем его место
850
+ if (!state.leaderboard.some(p => p.id === 999)) {
851
+ elements.userRank.textContent = '>10';
 
 
 
852
  }
853
+ }
 
 
 
 
 
 
 
 
854
 
855
+ // Имитация других игроков
856
+ function simulateOtherPlayers() {
857
+ const unminedPixels = Array.from(document.querySelectorAll('.pixel:not(.pixel-mined)'));
858
+ if (unminedPixels.length === 0) return;
859
+
860
+ const simultaneousMining = Math.floor(Math.random() * 5);
861
+ for (let i = 0; i < simultaneousMining; i++) {
862
+ if (Math.random() < 0.7) { // 70% шанс, что другой игрок добудет пиксель
863
+ const randomIndex = Math.floor(Math.random() * unminedPixels.length);
864
+ minePixel(parseInt(unminedPixels[randomIndex].dataset.index));
865
+
866
+ // Обновляем список неподобранных пикселей
867
+ unminedPixels.splice(randomIndex, 1);
868
+ if (unminedPixels.length === 0) break;
869
+ }
870
  }
871
  }
872
 
873
+ // Обновление истории блоков
874
+ function updateHistory() {
875
+ elements.historyList.innerHTML = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
876
 
877
+ if (state.blockHistory.length === 0) {
878
+ elements.historyList.innerHTML = '<div style="text-align: center; padding: 12px; color: #666;">История блоков пуста</div>';
879
+ return;
880
  }
881
 
882
+ state.blockHistory.forEach(block => {
883
+ const item = document.createElement('div');
884
+ item.className = 'history-item';
885
+ item.innerHTML = `
886
+ <div>
887
+ <div style="font-weight: bold;">Блок #${block.id}</div>
888
+ <div style="font-size: 0.8rem; color: #aaa;">${block.completedAt.toLocaleTimeString()}</div>
889
+ </div>
890
+ <div style="text-align: right;">
891
+ <div>${block.creator}</div>
892
+ <div style="font-size: 0.8rem;">
893
+ <span>${block.score} PX</span>
894
+ ${block.silver > 0 ? `<span class="rare-icon">✨${block.silver}</span>` : ''}
895
+ ${block.gold > 0 ? `<span class="rare-icon">⭐${block.gold}</span>` : ''}
896
+ ${block.diamond > 0 ? `<span class="rare-icon">💎${block.diamond}</span>` : ''}
897
+ </div>
898
+ </div>
899
+ `;
900
+
901
+ elements.historyList.appendChild(item);
902
+ });
903
+ }
904
 
905
+ // Обновление профиля
906
+ function updateProfile() {
907
+ elements.profileName.textContent = state.playerData.name;
908
 
909
+ if (state.playerData.avatar) {
910
+ elements.userAvatar.style.backgroundImage = `url(${state.playerData.avatar})`;
911
+ elements.userAvatar.style.backgroundSize = 'cover';
912
+ elements.profileAvatar.style.backgroundImage = `url(${state.playerData.avatar})`;
913
+ elements.profileAvatar.style.backgroundSize = 'cover';
914
  }
915
 
916
+ // Обновляем сетку созданных блоков
917
+ elements.avatarGrid.innerHTML = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
918
 
919
+ if (state.createdBlocks.length === 0) {
920
+ elements.avatarGrid.innerHTML = '<div style="grid-column: 1 / -1; text-align: center; padding: 12px;">Нет созданных блоков</div>';
921
+ return;
922
+ }
923
 
924
+ state.createdBlocks.forEach(block => {
925
+ const item = document.createElement('div');
926
+ item.className = 'avatar-item';
927
+ item.dataset.blockId = block.id;
928
+
929
+ const img = document.createElement('img');
930
+ img.src = block.image;
931
+ img.alt = `Блок ${block.id}`;
932
+
933
+ item.appendChild(img);
934
+ elements.avatarGrid.appendChild(item);
935
+ });
936
+ }
937
+
938
+ // Обработчики событий
939
+ function setupEventListeners() {
940
+ // Кнопка добычи пикселя
941
+ elements.mineButton.addEventListener('click', () => {
942
+ minePixel();
943
+ });
944
 
945
+ // Переключение вкладок
946
+ elements.tabs.forEach(tab => {
947
+ tab.addEventListener('click', () => {
948
+ elements.tabs.forEach(t => t.classList.remove('active'));
949
+ tab.classList.add('active');
950
+
951
+ const tabName = tab.dataset.tab;
952
+ elements.tabContents.forEach(content => {
953
+ content.classList.remove('active');
954
+ if (content.id === `${tabName}-content`) {
955
+ content.classList.add('active');
956
+
957
+ // Обновляем данные при открытии вкладки
958
+ if (tabName === 'leaderboard') {
959
+ updateLeaderboard();
960
+ } else if (tabName === 'history') {
961
+ updateHistory();
962
+ } else if (tabName === 'profile') {
963
+ updateProfile();
964
+ }
965
+ }
966
+ });
967
  });
968
  });
969
 
970
+ // Клик по пикселю
971
+ elements.canvas.addEventListener('click', (e) => {
972
+ if (e.target.classList.contains('pixel')) {
973
+ const index = parseInt(e.target.dataset.index);
974
+ if (!e.target.classList.contains('pixel-mined')) {
975
+ if (Math.random() < 0.5) { // 50% шанс успешной добычи при клике
976
+ minePixel(index);
977
+ }
978
+ }
979
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
980
  });
981
+ }
 
 
 
 
 
 
 
 
 
 
 
982
 
983
+ // Инициализация игры
984
+ function initGame() {
985
+ // Устанавливаем имя игрока из Telegram WebApp.initData
986
+ // В демо-версии используем случайное имя
987
+ const randomName = `Игрок${Math.floor(Math.random() * 1000)}`;
988
+ state.playerData.name = randomName;
989
+ state.playerData.avatar = `https://picsum.photos/200/200?random=${Math.floor(Math.random() * 1000)}`;
990
 
991
+ initCanvas();
992
+ updateUI();
993
+ setupEventListeners();
994
+ socket.connect();
 
 
 
 
 
 
 
995
 
996
+ // Инициализация демо-данных
997
+ setTimeout(() => {
998
+ updateLeaderboard();
999
+ updateHistory();
1000
+ updateProfile();
1001
+ }, 500);
1002
+ }
1003
 
1004
+ // Запуск игры
1005
+ initGame();
1006
+ });
 
 
 
 
1007
  </script>
1008
+ </body>
1009
  </html>