Milad56902857 commited on
Commit
45e2aaa
·
verified ·
1 Parent(s): 8c5ffec

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +536 -709
index.html CHANGED
@@ -3,806 +3,633 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>شوتر فضایی - Space Shooter</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
- @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&display=swap');
11
-
12
  * {
13
  margin: 0;
14
  padding: 0;
15
  box-sizing: border-box;
16
  }
17
-
18
  body {
19
- font-family: 'Orbitron', monospace;
20
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
21
  min-height: 100vh;
22
- overflow: hidden;
23
- position: relative;
24
- }
25
-
26
- .stars {
27
- position: fixed;
28
- top: 0;
29
- left: 0;
30
- width: 100%;
31
- height: 100%;
32
- pointer-events: none;
33
- }
34
-
35
- .star {
36
- position: absolute;
37
- width: 2px;
38
- height: 2px;
39
- background: white;
40
- border-radius: 50%;
41
- animation: twinkle 3s infinite;
42
- }
43
-
44
- @keyframes twinkle {
45
- 0%, 100% { opacity: 0.3; }
46
- 50% { opacity: 1; }
47
  }
48
-
49
- #gameCanvas {
50
- border: 3px solid rgba(255, 255, 255, 0.3);
51
- border-radius: 15px;
52
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
53
- background: linear-gradient(180deg, #0f0c29 0%, #302b63 50%, #24243e 100%);
54
- max-width: 100%;
55
- height: auto;
56
  }
57
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  .game-container {
59
- backdrop-filter: blur(10px);
60
- background: rgba(255, 255, 255, 0.1);
61
  border-radius: 20px;
62
- padding: 20px;
63
- box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);
64
- border: 1px solid rgba(255, 255, 255, 0.18);
 
 
65
  }
66
-
67
- .stat-card {
68
- background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05));
69
- backdrop-filter: blur(10px);
70
- border-radius: 15px;
71
- padding: 15px;
72
- border: 1px solid rgba(255, 255, 255, 0.2);
73
- transition: transform 0.3s ease, box-shadow 0.3s ease;
74
  }
75
-
76
- .stat-card:hover {
77
- transform: translateY(-5px);
78
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
 
79
  }
80
-
81
- .btn-game {
82
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
83
  color: white;
84
- padding: 12px 30px;
85
- border-radius: 50px;
 
 
 
86
  font-weight: bold;
87
- transition: all 0.3s ease;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  border: none;
 
 
89
  cursor: pointer;
90
- text-transform: uppercase;
91
- letter-spacing: 2px;
92
- box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
 
93
  }
94
-
95
- .btn-game:hover {
96
- transform: translateY(-3px);
97
- box-shadow: 0 6px 25px rgba(102, 126, 234, 0.6);
98
  }
99
-
100
- .btn-game:active {
101
- transform: translateY(-1px);
102
  }
103
-
104
- .health-bar {
105
- width: 100%;
106
- height: 20px;
107
- background: rgba(255, 255, 255, 0.2);
108
- border-radius: 10px;
 
 
109
  overflow: hidden;
 
 
 
 
 
 
 
 
 
 
110
  position: relative;
111
  }
112
-
113
- .health-fill {
114
- height: 100%;
115
- background: linear-gradient(90deg, #ff6b6b, #ff3838);
116
- transition: width 0.3s ease;
117
- box-shadow: 0 0 10px rgba(255, 107, 107, 0.5);
 
118
  }
119
-
120
- .power-up-indicator {
121
- animation: pulse 1s infinite;
 
 
 
 
 
 
 
 
 
 
 
122
  }
123
-
124
- @keyframes pulse {
125
- 0%, 100% { transform: scale(1); opacity: 1; }
126
- 50% { transform: scale(1.1); opacity: 0.8; }
127
  }
128
-
129
- .modal {
130
- display: none;
 
 
 
 
 
 
 
 
 
131
  position: fixed;
132
  top: 0;
133
  left: 0;
134
- width: 100%;
135
- height: 100%;
136
- background: rgba(0, 0, 0, 0.8);
137
- z-index: 1000;
138
- justify-content: center;
139
- align-items: center;
140
- backdrop-filter: blur(5px);
141
- }
142
-
143
- .modal.active {
144
- display: flex;
145
  }
146
-
147
- .modal-content {
148
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
149
- padding: 40px;
150
- border-radius: 20px;
151
- text-align: center;
152
- color: white;
153
- animation: slideIn 0.3s ease;
154
  }
155
-
156
- @keyframes slideIn {
157
  from {
158
- transform: translateY(-50px);
159
  opacity: 0;
 
160
  }
161
  to {
162
- transform: translateY(0);
163
  opacity: 1;
 
164
  }
165
  }
166
-
167
- .floating {
168
- animation: float 3s ease-in-out infinite;
 
 
 
 
 
 
 
169
  }
170
-
171
- @keyframes float {
172
- 0%, 100% { transform: translateY(0); }
173
- 50% { transform: translateY(-10px); }
 
 
 
 
 
 
 
 
 
174
  }
175
  </style>
176
  </head>
177
  <body>
178
- <!-- Animated Stars Background -->
179
- <div class="stars" id="starsContainer"></div>
180
-
181
- <!-- Header -->
182
- <header class="text-center py-4 relative z-10">
183
- <h1 class="text-4xl md:text-5xl font-bold text-white mb-2 floating">
184
- <i class="fas fa-rocket mr-3"></i>شوتر فضایی
185
- </h1>
186
- <p class="text-white/80">Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" class="text-yellow-300 hover:text-yellow-400 underline transition-colors">anycoder</a></p>
187
  </header>
188
-
189
- <!-- Main Game Container -->
190
- <main class="container mx-auto px-4 pb-8">
191
- <div class="game-container max-w-6xl mx-auto">
192
- <!-- Game Stats -->
193
- <div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
194
- <div class="stat-card text-center">
195
- <div class="text-white/70 text-sm mb-1">امتیاز</div>
196
- <div class="text-2xl font-bold text-white" id="score">0</div>
197
- </div>
198
- <div class="stat-card text-center">
199
- <div class="text-white/70 text-sm mb-1">سطح</div>
200
- <div class="text-2xl font-bold text-white" id="level">1</div>
201
  </div>
202
- <div class="stat-card text-center">
203
- <div class="text-white/70 text-sm mb-1">امتیاز بالا</div>
204
- <div class="text-2xl font-bold text-yellow-300" id="highScore">0</div>
205
  </div>
206
- <div class="stat-card text-center">
207
- <div class="text-white/70 text-sm mb-1">قدرت</div>
208
- <div class="text-2xl font-bold text-green-300 power-up-indicator" id="power">
209
- <i class="fas fa-bolt"></i> x1
210
- </div>
211
  </div>
212
  </div>
213
-
214
- <!-- Health Bar -->
215
- <div class="mb-4">
216
- <div class="text-white/70 text-sm mb-2">سلامتی فضاپیما</div>
217
- <div class="health-bar">
218
- <div class="health-fill" id="healthBar" style="width: 100%"></div>
219
- </div>
220
  </div>
221
-
222
- <!-- Game Canvas -->
223
- <div class="flex justify-center mb-6">
224
- <canvas id="gameCanvas" width="800" height="600"></canvas>
225
- </div>
226
-
227
- <!-- Controls -->
228
- <div class="flex flex-wrap justify-center gap-4">
229
- <button class="btn-game" id="startBtn">
230
- <i class="fas fa-play mr-2"></i>شروع بازی
231
- </button>
232
- <button class="btn-game" id="pauseBtn">
233
- <i class="fas fa-pause mr-2"></i>توقف
234
- </button>
235
- <button class="btn-game" id="soundBtn">
236
- <i class="fas fa-volume-up mr-2"></i>صدا
237
- </button>
238
- </div>
239
-
240
- <!-- Instructions -->
241
- <div class="mt-6 text-center text-white/70">
242
- <p class="mb-2"><i class="fas fa-gamepad mr-2"></i>از کلیدهای جهت‌نما یا A/D برای حرکت استفاده کنید</p>
243
- <p><i class="fas fa-space-bar mr-2"></i>برای شلیک فاصله را فشار دهید</p>
244
- </div>
245
- </div>
246
  </main>
247
-
248
- <!-- Game Over Modal -->
249
- <div class="modal" id="gameOverModal">
250
- <div class="modal-content">
251
- <h2 class="text-3xl font-bold mb-4">بازی تمام شد!</h2>
252
- <p class="text-xl mb-2">امتیاز نهایی: <span id="finalScore">0</span></p>
253
- <p class="text-lg mb-6">بهترین امتیاز: <span id="finalHighScore">0</span></p>
254
- <button class="btn-game" onclick="restartGame()">
255
- <i class="fas fa-redo mr-2"></i>بازی مجدد
256
- </button>
257
  </div>
 
258
  </div>
259
-
260
  <script>
261
- // Generate background stars
262
- function generateStars() {
263
- const starsContainer = document.getElementById('starsContainer');
264
- for (let i = 0; i < 100; i++) {
265
- const star = document.createElement('div');
266
- star.className = 'star';
267
- star.style.left = Math.random() * 100 + '%';
268
- star.style.top = Math.random() * 100 + '%';
269
- star.style.animationDelay = Math.random() * 3 + 's';
270
- starsContainer.appendChild(star);
271
  }
272
- }
273
- generateStars();
274
-
275
- // Game Variables
276
- const canvas = document.getElementById('gameCanvas');
277
- const ctx = canvas.getContext('2d');
278
-
279
- let gameState = 'menu'; // menu, playing, paused, gameOver
280
- let score = 0;
281
- let level = 1;
282
- let health = 100;
283
- let highScore = localStorage.getItem('spaceShooterHighScore') || 0;
284
- let soundEnabled = true;
285
- let powerMultiplier = 1;
286
-
287
- // Update high score display
288
- document.getElementById('highScore').textContent = highScore;
289
-
290
- // Player
291
- const player = {
292
- x: canvas.width / 2 - 25,
293
- y: canvas.height - 80,
294
- width: 50,
295
- height: 50,
296
- speed: 5,
297
- color: '#00ff88'
298
- };
299
-
300
- // Game Arrays
301
- let bullets = [];
302
- let enemies = [];
303
- let particles = [];
304
- let powerUps = [];
305
- let stars = [];
306
-
307
- // Controls
308
- const keys = {};
309
-
310
- // Initialize background stars
311
- for (let i = 0; i < 50; i++) {
312
- stars.push({
313
- x: Math.random() * canvas.width,
314
- y: Math.random() * canvas.height,
315
- size: Math.random() * 2,
316
- speed: Math.random() * 2 + 0.5
317
- });
318
- }
319
-
320
- // Event Listeners
321
- document.addEventListener('keydown', (e) => {
322
- keys[e.key] = true;
323
- if (e.key === ' ' && gameState === 'playing') {
324
- shoot();
325
- e.preventDefault();
326
  }
327
- });
328
-
329
- document.addEventListener('keyup', (e) => {
330
- keys[e.key] = false;
331
- });
332
-
333
- // Button Controls
334
- document.getElementById('startBtn').addEventListener('click', startGame);
335
- document.getElementById('pauseBtn').addEventListener('click', togglePause);
336
- document.getElementById('soundBtn').addEventListener('click', toggleSound);
337
-
338
- // Sound Effects
339
- const AudioContext = window.AudioContext || window.webkitAudioContext;
340
- const audioContext = new AudioContext();
341
-
342
- function playSound(frequency, duration, type = 'sine') {
343
- if (!soundEnabled) return;
344
-
345
- const oscillator = audioContext.createOscillator();
346
- const gainNode = audioContext.createGain();
347
-
348
- oscillator.connect(gainNode);
349
- gainNode.connect(audioContext.destination);
350
-
351
- oscillator.frequency.value = frequency;
352
- oscillator.type = type;
353
-
354
- gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
355
- gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + duration);
356
-
357
- oscillator.start(audioContext.currentTime);
358
- oscillator.stop(audioContext.currentTime + duration);
359
- }
360
-
361
- // Game Functions
362
- function startGame() {
363
- gameState = 'playing';
364
- score = 0;
365
- level = 1;
366
- health = 100;
367
- bullets = [];
368
- enemies = [];
369
- particles = [];
370
- powerUps = [];
371
- powerMultiplier = 1;
372
- updateUI();
373
- gameLoop();
374
- }
375
-
376
- function togglePause() {
377
- if (gameState === 'playing') {
378
- gameState = 'paused';
379
- } else if (gameState === 'paused') {
380
- gameState = 'playing';
381
- gameLoop();
382
  }
383
  }
384
-
385
- function toggleSound() {
386
- soundEnabled = !soundEnabled;
387
- const soundBtn = document.getElementById('soundBtn');
388
- soundBtn.innerHTML = soundEnabled ?
389
- '<i class="fas fa-volume-up mr-2"></i>صدا' :
390
- '<i class="fas fa-volume-mute mr-2"></i>صدا';
391
- }
392
-
393
- function restartGame() {
394
- document.getElementById('gameOverModal').classList.remove('active');
395
- startGame();
396
- }
397
-
398
- function shoot() {
399
- playSound(800, 0.1, 'square');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
 
401
- if (powerMultiplier === 1) {
402
- bullets.push({
403
- x: player.x + player.width / 2 - 2,
404
- y: player.y,
405
- width: 4,
406
- height: 10,
407
- speed: 8,
408
- color: '#ffff00'
409
- });
410
- } else if (powerMultiplier === 2) {
411
- bullets.push({
412
- x: player.x + 10,
413
- y: player.y,
414
- width: 4,
415
- height: 10,
416
- speed: 8,
417
- color: '#ff00ff'
418
- });
419
- bullets.push({
420
- x: player.x + player.width - 10,
421
- y: player.y,
422
- width: 4,
423
- height: 10,
424
- speed: 8,
425
- color: '#ff00ff'
426
- });
427
- } else {
428
- bullets.push({
429
- x: player.x + player.width / 2 - 2,
430
- y: player.y,
431
- width: 4,
432
- height: 10,
433
- speed: 8,
434
- color: '#00ffff'
435
- });
436
- bullets.push({
437
- x: player.x + 10,
438
- y: player.y + 10,
439
- width: 4,
440
- height: 10,
441
- speed: 8,
442
- color: '#00ffff'
443
- });
444
- bullets.push({
445
- x: player.x + player.width - 10,
446
- y: player.y + 10,
447
- width: 4,
448
- height: 10,
449
- speed: 8,
450
- color: '#00ffff'
451
- });
452
  }
453
- }
454
-
455
- function spawnEnemy() {
456
- const size = 30 + Math.random() * 20;
457
- enemies.push({
458
- x: Math.random() * (canvas.width - size),
459
- y: -size,
460
- width: size,
461
- height: size,
462
- speed: 1 + Math.random() * 2 + (level * 0.5),
463
- color: `hsl(${Math.random() * 60}, 100%, 50%)`,
464
- health: Math.ceil(size / 20)
465
- });
466
- }
467
-
468
- function spawnPowerUp() {
469
- if (Math.random() < 0.01) {
470
- powerUps.push({
471
- x: Math.random() * (canvas.width - 30),
472
- y: -30,
473
- width: 30,
474
- height: 30,
475
- speed: 2,
476
- type: Math.random() < 0.5 ? 'power' : 'health'
477
  });
478
- }
479
- }
480
-
481
- function createParticles(x, y, color) {
482
- for (let i = 0; i < 15; i++) {
483
- particles.push({
484
- x: x,
485
- y: y,
486
- vx: (Math.random() - 0.5) * 8,
487
- vy: (Math.random() - 0.5) * 8,
488
- size: Math.random() * 3 + 1,
489
- color: color,
490
- life: 1
491
  });
492
  }
493
- }
494
-
495
- function updatePlayer() {
496
- if (keys['ArrowLeft'] || keys['a'] || keys['A']) {
497
- player.x = Math.max(0, player.x - player.speed);
498
- }
499
- if (keys['ArrowRight'] || keys['d'] || keys['D']) {
500
- player.x = Math.min(canvas.width - player.width, player.x + player.speed);
501
- }
502
- }
503
-
504
- function updateBullets() {
505
- for (let i = bullets.length - 1; i >= 0; i--) {
506
- bullets[i].y -= bullets[i].speed;
507
 
508
- if (bullets[i].y < -bullets[i].height) {
509
- bullets.splice(i, 1);
510
- }
511
- }
512
- }
513
-
514
- function updateEnemies() {
515
- for (let i = enemies.length - 1; i >= 0; i--) {
516
- enemies[i].y += enemies[i].speed;
517
-
518
- if (enemies[i].y > canvas.height) {
519
- enemies.splice(i, 1);
520
- health -= 10;
521
- updateUI();
522
- }
523
- }
524
- }
525
-
526
- function updateParticles() {
527
- for (let i = particles.length - 1; i >= 0; i--) {
528
- particles[i].x += particles[i].vx;
529
- particles[i].y += particles[i].vy;
530
- particles[i].life -= 0.02;
531
- particles[i].vx *= 0.98;
532
- particles[i].vy *= 0.98;
533
-
534
- if (particles[i].life <= 0) {
535
- particles.splice(i, 1);
536
  }
537
- }
538
- }
539
-
540
- function updatePowerUps() {
541
- for (let i = powerUps.length - 1; i >= 0; i--) {
542
- powerUps[i].y += powerUps[i].speed;
543
 
544
- if (powerUps[i].y > canvas.height) {
545
- powerUps.splice(i, 1);
 
 
 
 
 
 
 
 
 
 
 
546
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
547
  }
548
- }
549
-
550
- function updateStars() {
551
- stars.forEach(star => {
552
- star.y += star.speed;
553
- if (star.y > canvas.height) {
554
- star.y = 0;
555
- star.x = Math.random() * canvas.width;
556
- }
557
- });
558
- }
559
-
560
- function checkCollisions() {
561
- // Bullet-Enemy collisions
562
- for (let i = bullets.length - 1; i >= 0; i--) {
563
- for (let j = enemies.length - 1; j >= 0; j--) {
564
- if (bullets[i] && enemies[j] &&
565
- bullets[i].x < enemies[j].x + enemies[j].width &&
566
- bullets[i].x + bullets[i].width > enemies[j].x &&
567
- bullets[i].y < enemies[j].y + enemies[j].height &&
568
- bullets[i].y + bullets[i].height > enemies[j].y) {
569
-
570
- enemies[j].health--;
571
- bullets.splice(i, 1);
572
-
573
- if (enemies[j].health <= 0) {
574
- createParticles(
575
- enemies[j].x + enemies[j].width / 2,
576
- enemies[j].y + enemies[j].height / 2,
577
- enemies[j].color
578
- );
579
- enemies.splice(j, 1);
580
- score += 10 * level;
581
- playSound(200, 0.2, 'sawtooth');
582
- updateUI();
583
- }
584
- break;
585
- }
586
  }
587
  }
588
 
589
- // Player-Enemy collisions
590
- for (let i = enemies.length - 1; i >= 0; i--) {
591
- if (player.x < enemies[i].x + enemies[i].width &&
592
- player.x + player.width > enemies[i].x &&
593
- player.y < enemies[i].y + enemies[i].height &&
594
- player.y + player.height > enemies[i].y) {
595
-
596
- createParticles(
597
- enemies[i].x + enemies[i].width / 2,
598
- enemies[i].y + enemies[i].height / 2,
599
- '#ff0000'
600
- );
601
- enemies.splice(i, 1);
602
- health -= 20;
603
- playSound(100, 0.3, 'triangle');
604
- updateUI();
605
  }
606
  }
607
 
608
- // Player-PowerUp collisions
609
- for (let i = powerUps.length - 1; i >= 0; i--) {
610
- if (player.x < powerUps[i].x + powerUps[i].width &&
611
- player.x + player.width > powerUps[i].x &&
612
- player.y < powerUps[i].y + powerUps[i].height &&
613
- player.y + player.height > powerUps[i].y) {
614
-
615
- if (powerUps[i].type === 'power') {
616
- powerMultiplier = Math.min(3, powerMultiplier + 1);
617
- document.getElementById('power').innerHTML = `<i class="fas fa-bolt"></i> x${powerMultiplier}`;
618
  } else {
619
- health = Math.min(100, health + 20);
 
 
 
 
 
620
  }
621
 
622
- powerUps.splice(i, 1);
623
- playSound(600, 0.2, 'sine');
624
- updateUI();
625
- }
626
  }
627
- }
628
-
629
- function drawPlayer() {
630
- ctx.save();
631
- ctx.fillStyle = player.color;
632
- ctx.shadowBlur = 20;
633
- ctx.shadowColor = player.color;
634
-
635
- // Draw spaceship
636
- ctx.beginPath();
637
- ctx.moveTo(player.x + player.width / 2, player.y);
638
- ctx.lineTo(player.x, player.y + player.height);
639
- ctx.lineTo(player.x + player.width / 2, player.y + player.height - 10);
640
- ctx.lineTo(player.x + player.width, player.y + player.height);
641
- ctx.closePath();
642
- ctx.fill();
643
 
644
- // Draw cockpit
645
- ctx.fillStyle = '#00ffff';
646
- ctx.beginPath();
647
- ctx.arc(player.x + player.width / 2, player.y + 20, 5, 0, Math.PI * 2);
648
- ctx.fill();
649
-
650
- ctx.restore();
651
- }
652
-
653
- function drawBullets() {
654
- bullets.forEach(bullet => {
655
- ctx.save();
656
- ctx.fillStyle = bullet.color;
657
- ctx.shadowBlur = 10;
658
- ctx.shadowColor = bullet.color;
659
- ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
660
- ctx.restore();
661
- });
662
- }
663
-
664
- function drawEnemies() {
665
- enemies.forEach(enemy => {
666
- ctx.save();
667
- ctx.fillStyle = enemy.color;
668
- ctx.shadowBlur = 15;
669
- ctx.shadowColor = enemy.color;
670
-
671
- // Draw enemy ship
672
- ctx.beginPath();
673
- ctx.moveTo(enemy.x + enemy.width / 2, enemy.y + enemy.height);
674
- ctx.lineTo(enemy.x, enemy.y);
675
- ctx.lineTo(enemy.x + enemy.width / 2, enemy.y + 10);
676
- ctx.lineTo(enemy.x + enemy.width, enemy.y);
677
- ctx.closePath();
678
- ctx.fill();
679
-
680
- ctx.restore();
681
- });
682
- }
683
-
684
- function drawParticles() {
685
- particles.forEach(particle => {
686
- ctx.save();
687
- ctx.globalAlpha = particle.life;
688
- ctx.fillStyle = particle.color;
689
- ctx.shadowBlur = 10;
690
- ctx.shadowColor = particle.color;
691
- ctx.fillRect(particle.x, particle.y, particle.size, particle.size);
692
- ctx.restore();
693
- });
694
- }
695
-
696
- function drawPowerUps() {
697
- powerUps.forEach(powerUp => {
698
- ctx.save();
699
- ctx.fillStyle = powerUp.type === 'power' ? '#ff00ff' : '#00ff00';
700
- ctx.shadowBlur = 15;
701
- ctx.shadowColor = ctx.fillStyle;
702
-
703
- // Draw star shape for power-up
704
- ctx.beginPath();
705
- for (let i = 0; i < 5; i++) {
706
- const angle = (i * 4 * Math.PI) / 5 - Math.PI / 2;
707
- const x = powerUp.x + powerUp.width / 2 + Math.cos(angle) * 15;
708
- const y = powerUp.y + powerUp.height / 2 + Math.sin(angle) * 15;
709
- if (i === 0) ctx.moveTo(x, y);
710
- else ctx.lineTo(x, y);
711
- }
712
- ctx.closePath();
713
- ctx.fill();
714
 
715
- ctx.restore();
716
- });
717
- }
718
-
719
- function drawStars() {
720
- ctx.fillStyle = 'white';
721
- stars.forEach(star => {
722
- ctx.globalAlpha = 0.8;
723
- ctx.fillRect(star.x, star.y, star.size, star.size);
724
- });
725
- ctx.globalAlpha = 1;
726
- }
727
-
728
- function draw() {
729
- // Clear canvas
730
- ctx.fillStyle = 'rgba(15, 12, 41, 0.1)';
731
- ctx.fillRect(0, 0, canvas.width, canvas.height);
732
-
733
- // Draw game elements
734
- drawStars();
735
- drawParticles();
736
- drawPowerUps();
737
- drawEnemies();
738
- drawBullets();
739
- drawPlayer();
740
-
741
- // Draw UI on canvas
742
- ctx.fillStyle = 'white';
743
- ctx.font = 'bold 20px Orbitron';
744
- ctx.fillText(`Level: ${level}`, 10, 30);
745
- }
746
-
747
- function updateUI() {
748
- document.getElementById('score').textContent = score;
749
- document.getElementById('level').textContent = level;
750
- document.getElementById('healthBar').style.width = health + '%';
751
-
752
- // Level up
753
- if (score > 0 && score % 100 === 0 && score / 100 === level) {
754
- level++;
755
- playSound(400, 0.5, 'sine');
756
  }
757
 
758
- // Check game over
759
- if (health <= 0) {
760
- gameOver();
 
 
 
 
 
761
  }
762
- }
763
-
764
- function gameOver() {
765
- gameState = 'gameOver';
766
 
767
- if (score > highScore) {
768
- highScore = score;
769
- localStorage.setItem('spaceShooterHighScore', highScore);
770
- document.getElementById('highScore').textContent = highScore;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
771
  }
772
 
773
- document.getElementById('finalScore').textContent = score;
774
- document.getElementById('finalHighScore').textContent = highScore;
775
- document.getElementById('gameOverModal').classList.add('active');
776
-
777
- playSound(150, 1, 'sawtooth');
778
- }
779
-
780
- function gameLoop() {
781
- if (gameState !== 'playing') return;
782
-
783
- updatePlayer();
784
- updateBullets();
785
- updateEnemies();
786
- updateParticles();
787
- updatePowerUps();
788
- updateStars();
789
- checkCollisions();
790
-
791
- // Spawn enemies
792
- if (Math.random() < 0.02 + (level * 0.005)) {
793
- spawnEnemy();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
794
  }
795
-
796
- // Spawn power-ups
797
- spawnPowerUp();
798
-
799
- draw();
800
-
801
- requestAnimationFrame(gameLoop);
802
  }
803
 
804
- // Initial draw
805
- draw();
806
  </script>
807
  </body>
808
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>بازی هوشی حافظه - Memory Game</title>
 
 
7
  <style>
 
 
8
  * {
9
  margin: 0;
10
  padding: 0;
11
  box-sizing: border-box;
12
  }
13
+
14
  body {
15
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
  min-height: 100vh;
18
+ display: flex;
19
+ flex-direction: column;
20
+ align-items: center;
21
+ padding: 20px;
22
+ overflow-x: hidden;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  }
24
+
25
+ header {
26
+ text-align: center;
27
+ margin-bottom: 30px;
28
+ animation: fadeInDown 0.8s ease;
 
 
 
29
  }
30
+
31
+ h1 {
32
+ color: white;
33
+ font-size: 2.5rem;
34
+ margin-bottom: 10px;
35
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
36
+ }
37
+
38
+ .header-link {
39
+ color: #ffd700;
40
+ text-decoration: none;
41
+ font-size: 0.9rem;
42
+ transition: color 0.3s;
43
+ }
44
+
45
+ .header-link:hover {
46
+ color: #fff;
47
+ }
48
+
49
  .game-container {
50
+ background: rgba(255, 255, 255, 0.95);
 
51
  border-radius: 20px;
52
+ padding: 30px;
53
+ box-shadow: 0 20px 60px rgba(0,0,0,0.3);
54
+ max-width: 900px;
55
+ width: 100%;
56
+ animation: fadeInUp 0.8s ease;
57
  }
58
+
59
+ .controls {
60
+ display: flex;
61
+ justify-content: space-between;
62
+ align-items: center;
63
+ margin-bottom: 20px;
64
+ flex-wrap: wrap;
65
+ gap: 15px;
66
  }
67
+
68
+ .stats {
69
+ display: flex;
70
+ gap: 20px;
71
+ flex-wrap: wrap;
72
  }
73
+
74
+ .stat-item {
75
+ background: linear-gradient(135deg, #667eea, #764ba2);
76
  color: white;
77
+ padding: 10px 20px;
78
+ border-radius: 10px;
79
+ display: flex;
80
+ align-items: center;
81
+ gap: 8px;
82
  font-weight: bold;
83
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
84
+ }
85
+
86
+ .stat-icon {
87
+ font-size: 1.2rem;
88
+ }
89
+
90
+ .difficulty-buttons {
91
+ display: flex;
92
+ gap: 10px;
93
+ }
94
+
95
+ .btn {
96
+ background: linear-gradient(135deg, #667eea, #764ba2);
97
+ color: white;
98
  border: none;
99
+ padding: 12px 24px;
100
+ border-radius: 10px;
101
  cursor: pointer;
102
+ font-size: 1rem;
103
+ font-weight: bold;
104
+ transition: all 0.3s;
105
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
106
  }
107
+
108
+ .btn:hover {
109
+ transform: translateY(-2px);
110
+ box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
111
  }
112
+
113
+ .btn:active {
114
+ transform: translateY(0);
115
  }
116
+
117
+ .btn.active {
118
+ background: linear-gradient(135deg, #f093fb, #f5576c);
119
+ }
120
+
121
+ .sound-toggle {
122
+ background: linear-gradient(135deg, #4facfe, #00f2fe);
123
+ position: relative;
124
  overflow: hidden;
125
+ }
126
+
127
+ .sound-toggle.muted {
128
+ background: linear-gradient(135deg, #e0e0e0, #9e9e9e);
129
+ }
130
+
131
+ .canvas-container {
132
+ display: flex;
133
+ justify-content: center;
134
+ margin: 20px 0;
135
  position: relative;
136
  }
137
+
138
+ #gameCanvas {
139
+ border-radius: 15px;
140
+ box-shadow: 0 10px 30px rgba(0,0,0,0.2);
141
+ cursor: pointer;
142
+ max-width: 100%;
143
+ height: auto;
144
  }
145
+
146
+ .win-message {
147
+ position: fixed;
148
+ top: 50%;
149
+ left: 50%;
150
+ transform: translate(-50%, -50%) scale(0);
151
+ background: linear-gradient(135deg, #667eea, #764ba2);
152
+ color: white;
153
+ padding: 40px;
154
+ border-radius: 20px;
155
+ text-align: center;
156
+ z-index: 1000;
157
+ box-shadow: 0 20px 60px rgba(0,0,0,0.4);
158
+ transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
159
  }
160
+
161
+ .win-message.show {
162
+ transform: translate(-50%, -50%) scale(1);
 
163
  }
164
+
165
+ .win-message h2 {
166
+ font-size: 2.5rem;
167
+ margin-bottom: 20px;
168
+ }
169
+
170
+ .win-stats {
171
+ font-size: 1.2rem;
172
+ margin-bottom: 30px;
173
+ }
174
+
175
+ .overlay {
176
  position: fixed;
177
  top: 0;
178
  left: 0;
179
+ right: 0;
180
+ bottom: 0;
181
+ background: rgba(0,0,0,0.7);
182
+ z-index: 999;
183
+ opacity: 0;
184
+ pointer-events: none;
185
+ transition: opacity 0.3s;
 
 
 
 
186
  }
187
+
188
+ .overlay.show {
189
+ opacity: 1;
190
+ pointer-events: all;
 
 
 
 
191
  }
192
+
193
+ @keyframes fadeInDown {
194
  from {
 
195
  opacity: 0;
196
+ transform: translateY(-30px);
197
  }
198
  to {
 
199
  opacity: 1;
200
+ transform: translateY(0);
201
  }
202
  }
203
+
204
+ @keyframes fadeInUp {
205
+ from {
206
+ opacity: 0;
207
+ transform: translateY(30px);
208
+ }
209
+ to {
210
+ opacity: 1;
211
+ transform: translateY(0);
212
+ }
213
  }
214
+
215
+ @media (max-width: 768px) {
216
+ h1 {
217
+ font-size: 2rem;
218
+ }
219
+
220
+ .controls {
221
+ justify-content: center;
222
+ }
223
+
224
+ .game-container {
225
+ padding: 20px;
226
+ }
227
  }
228
  </style>
229
  </head>
230
  <body>
231
+ <header>
232
+ <h1>🧠 بازی هوشی حافظه</h1>
233
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="header-link">
234
+ Built with anycoder
235
+ </a>
 
 
 
 
236
  </header>
237
+
238
+ <main class="game-container">
239
+ <section class="controls">
240
+ <div class="stats">
241
+ <div class="stat-item">
242
+ <span class="stat-icon">⏱️</span>
243
+ <span id="timer">00:00</span>
 
 
 
 
 
 
244
  </div>
245
+ <div class="stat-item">
246
+ <span class="stat-icon">🎯</span>
247
+ <span>امتیاز: <span id="score">0</span></span>
248
  </div>
249
+ <div class="stat-item">
250
+ <span class="stat-icon">👆</span>
251
+ <span>تلاش: <span id="moves">0</span></span>
 
 
252
  </div>
253
  </div>
254
+ <div class="difficulty-buttons">
255
+ <button class="btn active" data-level="easy">آسان</button>
256
+ <button class="btn" data-level="medium">متوسط</button>
257
+ <button class="btn" data-level="hard">سخت</button>
258
+ <button class="btn sound-toggle" id="soundToggle">🔊 صدا</button>
 
 
259
  </div>
260
+ </section>
261
+
262
+ <section class="canvas-container">
263
+ <canvas id="gameCanvas"></canvas>
264
+ </section>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
  </main>
266
+
267
+ <div class="overlay" id="overlay"></div>
268
+ <div class="win-message" id="winMessage">
269
+ <h2>🎉 تبریک! شما برنده شدید!</h2>
270
+ <div class="win-stats">
271
+ <p>⏱️ زمان: <span id="finalTime"></span></p>
272
+ <p>🎯 امتیاز نهایی: <span id="finalScore"></span></p>
273
+ <p>👆 تعداد تلاش: <span id="finalMoves"></span></p>
 
 
274
  </div>
275
+ <button class="btn" onclick="game.newGame()">بازی جدید</button>
276
  </div>
277
+
278
  <script>
279
+ class SoundManager {
280
+ constructor() {
281
+ this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
282
+ this.enabled = true;
 
 
 
 
 
 
283
  }
284
+
285
+ playSound(type) {
286
+ if (!this.enabled) return;
287
+
288
+ const oscillator = this.audioContext.createOscillator();
289
+ const gainNode = this.audioContext.createGain();
290
+
291
+ oscillator.connect(gainNode);
292
+ gainNode.connect(this.audioContext.destination);
293
+
294
+ switch(type) {
295
+ case 'flip':
296
+ oscillator.frequency.value = 600;
297
+ gainNode.gain.value = 0.3;
298
+ oscillator.type = 'sine';
299
+ break;
300
+ case 'match':
301
+ oscillator.frequency.value = 800;
302
+ gainNode.gain.value = 0.4;
303
+ oscillator.type = 'triangle';
304
+ break;
305
+ case 'win':
306
+ oscillator.frequency.value = 1000;
307
+ gainNode.gain.value = 0.5;
308
+ oscillator.type = 'square';
309
+ break;
310
+ case 'wrong':
311
+ oscillator.frequency.value = 300;
312
+ gainNode.gain.value = 0.2;
313
+ oscillator.type = 'sawtooth';
314
+ break;
315
+ }
316
+
317
+ oscillator.start();
318
+ gainNode.gain.exponentialRampToValueAtTime(0.01, this.audioContext.currentTime + 0.2);
319
+ oscillator.stop(this.audioContext.currentTime + 0.2);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
  }
321
+
322
+ toggle() {
323
+ this.enabled = !this.enabled;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  }
325
  }
326
+
327
+ class MemoryGame {
328
+ constructor() {
329
+ this.canvas = document.getElementById('gameCanvas');
330
+ this.ctx = this.canvas.getContext('2d');
331
+ this.soundManager = new SoundManager();
332
+
333
+ this.difficulty = 'easy';
334
+ this.gridSizes = {
335
+ easy: { cols: 4, rows: 3 },
336
+ medium: { cols: 4, rows: 4 },
337
+ hard: { cols: 6, rows: 4 }
338
+ };
339
+
340
+ this.cards = [];
341
+ this.flippedCards = [];
342
+ this.matchedPairs = 0;
343
+ this.moves = 0;
344
+ this.score = 0;
345
+ this.startTime = null;
346
+ this.timerInterval = null;
347
+ this.isProcessing = false;
348
+
349
+ this.symbols = ['🎈', '🎨', '🎭', '🎪', '🎯', '🎲', '🎸', '🎺',
350
+ '🌟', '🌈', '🌺', '🌸', '🍎', '🍊', '🍋', '🍇',
351
+ '🦄', '🦋', '🐢', '🐙', '🚀', '✨', '💎', '🔮'];
352
+
353
+ this.cardWidth = 80;
354
+ this.cardHeight = 80;
355
+ this.cardSpacing = 10;
356
+
357
+ this.init();
358
+ }
359
 
360
+ init() {
361
+ this.setupEventListeners();
362
+ this.newGame();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
  }
364
+
365
+ setupEventListeners() {
366
+ this.canvas.addEventListener('click', (e) => this.handleClick(e));
367
+
368
+ document.querySelectorAll('[data-level]').forEach(btn => {
369
+ btn.addEventListener('click', (e) => {
370
+ document.querySelectorAll('[data-level]').forEach(b => b.classList.remove('active'));
371
+ btn.classList.add('active');
372
+ this.difficulty = btn.dataset.level;
373
+ this.newGame();
374
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
375
  });
376
+
377
+ document.getElementById('soundToggle').addEventListener('click', () => {
378
+ this.soundManager.toggle();
379
+ const btn = document.getElementById('soundToggle');
380
+ btn.classList.toggle('muted');
381
+ btn.innerHTML = btn.classList.contains('muted') ? '🔇 صدا' : '🔊 صدا';
 
 
 
 
 
 
 
382
  });
383
  }
384
+
385
+ newGame() {
386
+ this.cards = [];
387
+ this.flippedCards = [];
388
+ this.matchedPairs = 0;
389
+ this.moves = 0;
390
+ this.score = 0;
391
+ this.isProcessing = false;
 
 
 
 
 
 
392
 
393
+ clearInterval(this.timerInterval);
394
+
395
+ const { cols, rows } = this.gridSizes[this.difficulty];
396
+ const totalCards = cols * rows;
397
+ const pairs = totalCards / 2;
398
+
399
+ // Create card pairs
400
+ const selectedSymbols = this.symbols.slice(0, pairs);
401
+ const cardData = [...selectedSymbols, ...selectedSymbols];
402
+
403
+ // Shuffle cards
404
+ for (let i = cardData.length - 1; i > 0; i--) {
405
+ const j = Math.floor(Math.random() * (i + 1));
406
+ [cardData[i], cardData[j]] = [cardData[j], cardData[i]];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
  }
 
 
 
 
 
 
408
 
409
+ // Create card objects
410
+ for (let row = 0; row < rows; row++) {
411
+ for (let col = 0; col < cols; col++) {
412
+ const index = row * cols + col;
413
+ this.cards.push({
414
+ x: col * (this.cardWidth + this.cardSpacing),
415
+ y: row * (this.cardHeight + this.cardSpacing),
416
+ symbol: cardData[index],
417
+ isFlipped: false,
418
+ isMatched: false,
419
+ flipAnimation: 0
420
+ });
421
+ }
422
  }
423
+
424
+ // Set canvas size
425
+ this.canvas.width = cols * (this.cardWidth + this.cardSpacing) - this.cardSpacing;
426
+ this.canvas.height = rows * (this.cardHeight + this.cardSpacing) - this.cardSpacing;
427
+
428
+ // Update UI
429
+ document.getElementById('moves').textContent = '0';
430
+ document.getElementById('score').textContent = '0';
431
+ document.getElementById('timer').textContent = '00:00';
432
+
433
+ // Hide win message
434
+ document.getElementById('winMessage').classList.remove('show');
435
+ document.getElementById('overlay').classList.remove('show');
436
+
437
+ this.draw();
438
  }
439
+
440
+ handleClick(e) {
441
+ if (this.isProcessing) return;
442
+
443
+ const rect = this.canvas.getBoundingClientRect();
444
+ const x = e.clientX - rect.left;
445
+ const y = e.clientY - rect.top;
446
+
447
+ const clickedCard = this.cards.find(card =>
448
+ x >= card.x && x <= card.x + this.cardWidth &&
449
+ y >= card.y && y <= card.y + this.cardHeight &&
450
+ !card.isFlipped && !card.isMatched
451
+ );
452
+
453
+ if (clickedCard) {
454
+ this.flipCard(clickedCard);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
  }
456
  }
457
 
458
+ flipCard(card) {
459
+ if (!this.startTime) {
460
+ this.startTimer();
461
+ }
462
+
463
+ card.isFlipped = true;
464
+ this.flippedCards.push(card);
465
+ this.soundManager.playSound('flip');
466
+
467
+ this.animateFlip(card, true);
468
+
469
+ if (this.flippedCards.length === 2) {
470
+ this.moves++;
471
+ document.getElementById('moves').textContent = this.moves;
472
+ this.checkMatch();
 
473
  }
474
  }
475
 
476
+ animateFlip(card, flip) {
477
+ const animate = () => {
478
+ if (flip) {
479
+ card.flipAnimation += 0.1;
480
+ if (card.flipAnimation >= 1) {
481
+ card.flipAnimation = 1;
482
+ this.draw();
483
+ return;
484
+ }
 
485
  } else {
486
+ card.flipAnimation -= 0.1;
487
+ if (card.flipAnimation <= 0) {
488
+ card.flipAnimation = 0;
489
+ this.draw();
490
+ return;
491
+ }
492
  }
493
 
494
+ this.draw();
495
+ requestAnimationFrame(animate);
496
+ };
497
+ animate();
498
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
 
500
+ checkMatch() {
501
+ this.isProcessing = true;
502
+ const [card1, card2] = this.flippedCards;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
503
 
504
+ setTimeout(() => {
505
+ if (card1.symbol === card2.symbol) {
506
+ card1.isMatched = true;
507
+ card2.isMatched = true;
508
+ this.matchedPairs++;
509
+ this.score += 100;
510
+ this.soundManager.playSound('match');
511
+
512
+ document.getElementById('score').textContent = this.score;
513
+
514
+ if (this.matchedPairs === this.cards.length / 2) {
515
+ this.gameWon();
516
+ }
517
+ } else {
518
+ card1.isFlipped = false;
519
+ card2.isFlipped = false;
520
+ this.soundManager.playSound('wrong');
521
+
522
+ this.animateFlip(card1, false);
523
+ this.animateFlip(card2, false);
524
+ }
525
+
526
+ this.flippedCards = [];
527
+ this.isProcessing = false;
528
+ this.draw();
529
+ }, 1000);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
530
  }
531
 
532
+ startTimer() {
533
+ this.startTime = Date.now();
534
+ this.timerInterval = setInterval(() => {
535
+ const elapsed = Math.floor((Date.now() - this.startTime) / 1000);
536
+ const minutes = Math.floor(elapsed / 60).toString().padStart(2, '0');
537
+ const seconds = (elapsed % 60).toString().padStart(2, '0');
538
+ document.getElementById('timer').textContent = `${minutes}:${seconds}`;
539
+ }, 1000);
540
  }
 
 
 
 
541
 
542
+ gameWon() {
543
+ clearInterval(this.timerInterval);
544
+ this.soundManager.playSound('win');
545
+
546
+ const elapsed = Math.floor((Date.now() - this.startTime) / 1000);
547
+ const minutes = Math.floor(elapsed / 60).toString().padStart(2, '0');
548
+ const seconds = (elapsed % 60).toString().padStart(2, '0');
549
+
550
+ // Bonus score based on time and moves
551
+ const timeBonus = Math.max(0, 300 - elapsed) * 2;
552
+ const movesBonus = Math.max(0, 50 - this.moves) * 10;
553
+ this.score += timeBonus + movesBonus;
554
+
555
+ document.getElementById('finalTime').textContent = `${minutes}:${seconds}`;
556
+ document.getElementById('finalScore').textContent = this.score;
557
+ document.getElementById('finalMoves').textContent = this.moves;
558
+
559
+ setTimeout(() => {
560
+ document.getElementById('overlay').classList.add('show');
561
+ document.getElementById('winMessage').classList.add('show');
562
+ }, 500);
563
  }
564
 
565
+ draw() {
566
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
567
+
568
+ this.cards.forEach(card => {
569
+ this.ctx.save();
570
+
571
+ // Card shadow
572
+ this.ctx.shadowColor = 'rgba(0, 0, 0, 0.2)';
573
+ this.ctx.shadowBlur = 10;
574
+ this.ctx.shadowOffsetX = 3;
575
+ this.ctx.shadowOffsetY = 3;
576
+
577
+ // Draw card background
578
+ const gradient = this.ctx.createLinearGradient(
579
+ card.x, card.y,
580
+ card.x + this.cardWidth, card.y + this.cardHeight
581
+ );
582
+
583
+ if (card.isMatched) {
584
+ gradient.addColorStop(0, '#4caf50');
585
+ gradient.addColorStop(1, '#45a049');
586
+ } else if (card.isFlipped || card.flipAnimation > 0) {
587
+ gradient.addColorStop(0, '#ffffff');
588
+ gradient.addColorStop(1, '#f0f0f0');
589
+ } else {
590
+ gradient.addColorStop(0, '#667eea');
591
+ gradient.addColorStop(1, '#764ba2');
592
+ }
593
+
594
+ this.ctx.fillStyle = gradient;
595
+ this.ctx.fillRect(card.x, card.y, this.cardWidth, this.cardHeight);
596
+
597
+ // Draw border
598
+ this.ctx.strokeStyle = card.isMatched ? '#4caf50' : '#333';
599
+ this.ctx.lineWidth = 2;
600
+ this.ctx.strokeRect(card.x, card.y, this.cardWidth, this.cardHeight);
601
+
602
+ // Draw content
603
+ if (card.isFlipped || card.isMatched) {
604
+ this.ctx.font = '36px Arial';
605
+ this.ctx.textAlign = 'center';
606
+ this.ctx.textBaseline = 'middle';
607
+ this.ctx.fillText(
608
+ card.symbol,
609
+ card.x + this.cardWidth / 2,
610
+ card.y + this.cardHeight / 2
611
+ );
612
+ } else {
613
+ // Draw card back pattern
614
+ this.ctx.fillStyle = 'rgba(255, 255, 255, 0.2)';
615
+ this.ctx.beginPath();
616
+ this.ctx.arc(
617
+ card.x + this.cardWidth / 2,
618
+ card.y + this.cardHeight / 2,
619
+ 25,
620
+ 0,
621
+ Math.PI * 2
622
+ );
623
+ this.ctx.fill();
624
+ }
625
+
626
+ this.ctx.restore();
627
+ });
628
  }
 
 
 
 
 
 
 
629
  }
630
 
631
+ // Initialize game
632
+ const game = new MemoryGame();
633
  </script>
634
  </body>
635
  </html>