Annu72772 commited on
Commit
4d0bd1b
·
verified ·
1 Parent(s): eb8cbbd

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +818 -19
index.html CHANGED
@@ -1,19 +1,818 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Carrom Board Game</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
+ --primary-red: #8B0000;
11
+ --light-brown: #D2B48C;
12
+ --orange: #FFA500;
13
+ --black: #000000;
14
+ --purple: #800080;
15
+ --green: #008000;
16
+ --white: #FFFFFF;
17
+ --gold: #FFD700;
18
+ }
19
+
20
+ * {
21
+ margin: 0;
22
+ padding: 0;
23
+ box-sizing: border-box;
24
+ font-family: 'Arial', sans-serif;
25
+ }
26
+
27
+ body {
28
+ background: linear-gradient(to bottom, var(--primary-red), #A0522D);
29
+ min-height: 100vh;
30
+ display: flex;
31
+ flex-direction: column;
32
+ align-items: center;
33
+ padding: 10px;
34
+ color: white;
35
+ overflow-x: hidden;
36
+ }
37
+
38
+ .header {
39
+ width: 100%;
40
+ display: flex;
41
+ justify-content: space-between;
42
+ align-items: center;
43
+ padding: 15px 0;
44
+ margin-bottom: 10px;
45
+ }
46
+
47
+ .player-info {
48
+ display: flex;
49
+ align-items: center;
50
+ gap: 10px;
51
+ }
52
+
53
+ .player-avatar {
54
+ width: 50px;
55
+ height: 50px;
56
+ border-radius: 8px;
57
+ object-fit: cover;
58
+ border: 2px solid var(--gold);
59
+ }
60
+
61
+ .player-name {
62
+ font-size: 18px;
63
+ font-weight: bold;
64
+ }
65
+
66
+ .player-score {
67
+ font-size: 24px;
68
+ font-weight: bold;
69
+ }
70
+
71
+ .disc-icon {
72
+ width: 20px;
73
+ height: 20px;
74
+ border-radius: 50%;
75
+ margin-left: 5px;
76
+ }
77
+
78
+ .pot-container {
79
+ display: flex;
80
+ flex-direction: column;
81
+ align-items: center;
82
+ gap: 5px;
83
+ }
84
+
85
+ .pot-icon {
86
+ font-size: 30px;
87
+ color: var(--gold);
88
+ text-shadow: 0 0 5px rgba(255, 215, 0, 0.7);
89
+ }
90
+
91
+ .pot-amount {
92
+ font-size: 18px;
93
+ font-weight: bold;
94
+ }
95
+
96
+ .game-container {
97
+ width: 100%;
98
+ max-width: 500px;
99
+ aspect-ratio: 1/1;
100
+ position: relative;
101
+ background-color: var(--light-brown);
102
+ border-radius: 10px;
103
+ box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
104
+ overflow: hidden;
105
+ background-image:
106
+ radial-gradient(circle at center, rgba(0,0,0,0.1) 1px, transparent 1px),
107
+ linear-gradient(45deg, rgba(0,0,0,0.05) 1px, transparent 1px),
108
+ linear-gradient(-45deg, rgba(0,0,0,0.05) 1px, transparent 1px);
109
+ background-size: 100px 100px, 20px 20px, 20px 20px;
110
+ }
111
+
112
+ .board-border {
113
+ position: absolute;
114
+ top: 0;
115
+ left: 0;
116
+ width: 100%;
117
+ height: 100%;
118
+ border: 15px solid var(--orange);
119
+ border-radius: 10px;
120
+ pointer-events: none;
121
+ }
122
+
123
+ .pocket {
124
+ position: absolute;
125
+ width: 40px;
126
+ height: 40px;
127
+ background-color: var(--black);
128
+ border-radius: 50%;
129
+ z-index: 10;
130
+ }
131
+
132
+ .pocket::before {
133
+ content: '';
134
+ position: absolute;
135
+ top: 50%;
136
+ left: 50%;
137
+ transform: translate(-50%, -50%);
138
+ width: 30px;
139
+ height: 30px;
140
+ background-color: rgba(0, 0, 0, 0.5);
141
+ border-radius: 50%;
142
+ }
143
+
144
+ .bumper {
145
+ position: absolute;
146
+ width: 20px;
147
+ height: 20px;
148
+ background-color: var(--orange);
149
+ border-radius: 50%;
150
+ z-index: 5;
151
+ }
152
+
153
+ .center-circle {
154
+ position: absolute;
155
+ top: 50%;
156
+ left: 50%;
157
+ transform: translate(-50%, -50%);
158
+ width: 80px;
159
+ height: 80px;
160
+ border: 2px dashed rgba(0, 0, 0, 0.3);
161
+ border-radius: 50%;
162
+ z-index: 2;
163
+ }
164
+
165
+ .striker-zone {
166
+ position: absolute;
167
+ bottom: 20px;
168
+ left: 20px;
169
+ width: 80px;
170
+ height: 40px;
171
+ border: 2px dashed rgba(255, 255, 255, 0.7);
172
+ border-radius: 40px 40px 0 0;
173
+ background-color: rgba(255, 255, 255, 0.1);
174
+ z-index: 3;
175
+ }
176
+
177
+ .striker {
178
+ position: absolute;
179
+ width: 25px;
180
+ height: 25px;
181
+ background-color: var(--white);
182
+ border-radius: 50%;
183
+ background-image: radial-gradient(circle, rgba(255,255,255,0.8) 2px, transparent 2px);
184
+ background-size: 5px 5px;
185
+ z-index: 4;
186
+ cursor: grab;
187
+ touch-action: none;
188
+ box-shadow: 0 0 5px rgba(255, 255, 255, 0.7);
189
+ }
190
+
191
+ .disc {
192
+ position: absolute;
193
+ width: 20px;
194
+ height: 20px;
195
+ border-radius: 50%;
196
+ z-index: 2;
197
+ transition: transform 0.1s linear;
198
+ }
199
+
200
+ .purple-disc {
201
+ background-color: var(--purple);
202
+ box-shadow: 0 0 5px rgba(128, 0, 128, 0.7);
203
+ }
204
+
205
+ .green-disc {
206
+ background-color: var(--green);
207
+ box-shadow: 0 0 5px rgba(0, 128, 0, 0.7);
208
+ }
209
+
210
+ .white-disc {
211
+ background-color: var(--white);
212
+ box-shadow: 0 0 5px rgba(255, 255, 255, 0.7);
213
+ }
214
+
215
+ .queen-disc {
216
+ background-color: #FF0000;
217
+ box-shadow: 0 0 10px rgba(255, 0, 0, 0.9);
218
+ }
219
+
220
+ .trajectory-line {
221
+ position: absolute;
222
+ height: 2px;
223
+ background-color: rgba(255, 255, 255, 0.7);
224
+ z-index: 1;
225
+ pointer-events: none;
226
+ }
227
+
228
+ .trajectory-arrow {
229
+ position: absolute;
230
+ width: 0;
231
+ height: 0;
232
+ border-left: 8px solid transparent;
233
+ border-right: 8px solid transparent;
234
+ border-top: 12px solid var(--orange);
235
+ z-index: 1;
236
+ pointer-events: none;
237
+ }
238
+
239
+ .controls {
240
+ margin-top: 20px;
241
+ display: flex;
242
+ gap: 15px;
243
+ flex-wrap: wrap;
244
+ justify-content: center;
245
+ }
246
+
247
+ .btn {
248
+ padding: 10px 20px;
249
+ border: none;
250
+ border-radius: 5px;
251
+ font-weight: bold;
252
+ cursor: pointer;
253
+ transition: all 0.3s;
254
+ background-color: var(--orange);
255
+ color: white;
256
+ }
257
+
258
+ .btn:hover {
259
+ background-color: #FF8C00;
260
+ transform: scale(1.05);
261
+ }
262
+
263
+ .btn:active {
264
+ transform: scale(0.95);
265
+ }
266
+
267
+ .game-status {
268
+ margin-top: 15px;
269
+ padding: 10px;
270
+ background-color: rgba(0, 0, 0, 0.3);
271
+ border-radius: 5px;
272
+ text-align: center;
273
+ font-size: 16px;
274
+ }
275
+
276
+ .anycoder-link {
277
+ position: fixed;
278
+ bottom: 10px;
279
+ right: 10px;
280
+ color: white;
281
+ text-decoration: none;
282
+ font-size: 12px;
283
+ background-color: rgba(0, 0, 0, 0.3);
284
+ padding: 5px 10px;
285
+ border-radius: 5px;
286
+ z-index: 100;
287
+ }
288
+
289
+ @media (max-width: 400px) {
290
+ .player-info {
291
+ flex-direction: column;
292
+ align-items: flex-start;
293
+ }
294
+
295
+ .player-avatar {
296
+ width: 40px;
297
+ height: 40px;
298
+ }
299
+
300
+ .player-name {
301
+ font-size: 16px;
302
+ }
303
+
304
+ .player-score {
305
+ font-size: 20px;
306
+ }
307
+
308
+ .pot-icon {
309
+ font-size: 25px;
310
+ }
311
+ }
312
+ </style>
313
+ </head>
314
+ <body>
315
+ <div class="header">
316
+ <div class="player-info">
317
+ <img src="https://via.placeholder.com/50x50/808080/FFFFFF?text=A" alt="Ash Avatar" class="player-avatar">
318
+ <div>
319
+ <div class="player-name">Ash</div>
320
+ <div class="player-score">0 <div class="disc-icon" style="background-color: var(--purple);"></div></div>
321
+ </div>
322
+ </div>
323
+
324
+ <div class="pot-container">
325
+ <i class="fas fa-coins pot-icon"></i>
326
+ <div class="pot-amount">1000</div>
327
+ </div>
328
+
329
+ <div class="player-info">
330
+ <div>
331
+ <div class="player-name">MD</div>
332
+ <div class="player-score">1 <div class="disc-icon" style="background-color: var(--green);"></div></div>
333
+ </div>
334
+ <img src="https://via.placeholder.com/50x50/D2B48C/000000?text=MD" alt="MD Avatar" class="player-avatar">
335
+ </div>
336
+ </div>
337
+
338
+ <div class="game-container">
339
+ <div class="board-border"></div>
340
+
341
+ <!-- Pockets -->
342
+ <div class="pocket" style="top: 10px; left: 10px;"></div>
343
+ <div class="pocket" style="top: 10px; right: 10px;"></div>
344
+ <div class="pocket" style="bottom: 10px; left: 10px;"></div>
345
+ <div class="pocket" style="bottom: 10px; right: 10px;"></div>
346
+
347
+ <!-- Bumpers -->
348
+ <div class="bumper" style="top: 30px; left: 50%; transform: translateX(-50%);"></div>
349
+ <div class="bumper" style="top: 50%; left: 30px; transform: translateY(-50%);"></div>
350
+ <div class="bumper" style="bottom: 30px; left: 50%; transform: translateX(-50%);"></div>
351
+ <div class="bumper" style="top: 50%; right: 30px; transform: translateY(-50%);"></div>
352
+
353
+ <!-- Center Circle -->
354
+ <div class="center-circle"></div>
355
+
356
+ <!-- Striker Zone -->
357
+ <div class="striker-zone"></div>
358
+
359
+ <!-- Striker -->
360
+ <div class="striker" id="striker"></div>
361
+
362
+ <!-- Trajectory Line and Arrow -->
363
+ <div class="trajectory-line" id="trajectoryLine"></div>
364
+ <div class="trajectory-arrow" id="trajectoryArrow"></div>
365
+
366
+ <!-- Discs will be added by JavaScript -->
367
+ </div>
368
+
369
+ <div class="controls">
370
+ <button class="btn" id="resetBtn">Reset Game</button>
371
+ <button class="btn" id="aiBtn">AI Opponent</button>
372
+ <button class="btn" id="shootBtn">Shoot</button>
373
+ </div>
374
+
375
+ <div class="game-status" id="gameStatus">
376
+ Player Ash's turn. Drag the striker to aim and shoot!
377
+ </div>
378
+
379
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" class="anycoder-link" target="_blank">Built with anycoder</a>
380
+
381
+ <script>
382
+ document.addEventListener('DOMContentLoaded', function() {
383
+ // Game state
384
+ const gameState = {
385
+ currentPlayer: 'Ash',
386
+ scores: {
387
+ Ash: 0,
388
+ MD: 0
389
+ },
390
+ discs: [],
391
+ striker: {
392
+ x: 30,
393
+ y: 85,
394
+ angle: 0,
395
+ power: 0,
396
+ isDragging: false
397
+ },
398
+ queenPocketed: false,
399
+ gameOver: false
400
+ };
401
+
402
+ // DOM elements
403
+ const gameContainer = document.querySelector('.game-container');
404
+ const striker = document.getElementById('striker');
405
+ const trajectoryLine = document.getElementById('trajectoryLine');
406
+ const trajectoryArrow = document.getElementById('trajectoryArrow');
407
+ const gameStatus = document.getElementById('gameStatus');
408
+ const resetBtn = document.getElementById('resetBtn');
409
+ const aiBtn = document.getElementById('aiBtn');
410
+ const shootBtn = document.getElementById('shootBtn');
411
+ const ashScore = document.querySelector('.player-info:first-child .player-score');
412
+ const mdScore = document.querySelector('.player-info:last-child .player-score');
413
+
414
+ // Initialize the game
415
+ function initGame() {
416
+ // Clear existing discs
417
+ document.querySelectorAll('.disc').forEach(disc => disc.remove());
418
+ gameState.discs = [];
419
+
420
+ // Create discs
421
+ createDiscs();
422
+
423
+ // Reset striker position
424
+ striker.style.left = `${gameState.striker.x}%`;
425
+ striker.style.top = `${gameState.striker.y}%`;
426
+
427
+ // Reset game state
428
+ gameState.currentPlayer = 'Ash';
429
+ gameState.queenPocketed = false;
430
+ gameState.gameOver = false;
431
+
432
+ // Update UI
433
+ updateGameStatus();
434
+ updateScores();
435
+ }
436
+
437
+ // Create discs
438
+ function createDiscs() {
439
+ const centerX = 50;
440
+ const centerY = 50;
441
+ const radius = 15;
442
+ const discSize = 20;
443
+
444
+ // Create queen disc (red)
445
+ const queen = document.createElement('div');
446
+ queen.className = 'disc queen-disc';
447
+ queen.style.left = `${centerX}%`;
448
+ queen.style.top = `${centerY}%`;
449
+ queen.style.width = `${discSize}px`;
450
+ queen.style.height = `${discSize}px`;
451
+ queen.style.transform = 'translate(-50%, -50%)';
452
+ queen.dataset.type = 'queen';
453
+ gameContainer.appendChild(queen);
454
+ gameState.discs.push({
455
+ element: queen,
456
+ x: centerX,
457
+ y: centerY,
458
+ type: 'queen',
459
+ pocketed: false
460
+ });
461
+
462
+ // Create player discs in a circle around the queen
463
+ const discCount = 9;
464
+ const angleStep = (2 * Math.PI) / discCount;
465
+
466
+ for (let i = 0; i < discCount; i++) {
467
+ const angle = i * angleStep;
468
+ const x = centerX + radius * Math.cos(angle);
469
+ const y = centerY + radius * Math.sin(angle);
470
+
471
+ // Alternate between purple and green/white discs
472
+ const discType = i % 2 === 0 ? 'purple' : (i % 3 === 0 ? 'green' : 'white');
473
+
474
+ const disc = document.createElement('div');
475
+ disc.className = `disc ${discType}-disc`;
476
+ disc.style.left = `${x}%`;
477
+ disc.style.top = `${y}%`;
478
+ disc.style.width = `${discSize}px`;
479
+ disc.style.height = `${discSize}px`;
480
+ disc.style.transform = 'translate(-50%, -50%)';
481
+ disc.dataset.type = discType;
482
+ gameContainer.appendChild(disc);
483
+
484
+ gameState.discs.push({
485
+ element: disc,
486
+ x: x,
487
+ y: y,
488
+ type: discType,
489
+ pocketed: false
490
+ });
491
+ }
492
+ }
493
+
494
+ // Update game status display
495
+ function updateGameStatus() {
496
+ if (gameState.gameOver) {
497
+ const winner = gameState.scores.Ash > gameState.scores.MD ? 'Ash' : 'MD';
498
+ gameStatus.textContent = `Game Over! ${winner} wins!`;
499
+ return;
500
+ }
501
+
502
+ gameStatus.textContent = `Player ${gameState.currentPlayer}'s turn. Drag the striker to aim and shoot!`;
503
+ }
504
+
505
+ // Update scores display
506
+ function updateScores() {
507
+ ashScore.innerHTML = `${gameState.scores.Ash} <div class="disc-icon" style="background-color: var(--purple);"></div>`;
508
+ mdScore.innerHTML = `${gameState.scores.MD} <div class="disc-icon" style="background-color: var(--green);"></div>`;
509
+ }
510
+
511
+ // Check if a disc is pocketed
512
+ function checkPocketed(disc) {
513
+ const pockets = [
514
+ { x: 5, y: 5 }, // Top-left
515
+ { x: 95, y: 5 }, // Top-right
516
+ { x: 5, y: 95 }, // Bottom-left
517
+ { x: 95, y: 95 } // Bottom-right
518
+ ];
519
+
520
+ for (const pocket of pockets) {
521
+ const distance = Math.sqrt(
522
+ Math.pow(disc.x - pocket.x, 2) +
523
+ Math.pow(disc.y - pocket.y, 2)
524
+ );
525
+
526
+ if (distance < 3) { // Pocket radius is about 3% of container
527
+ return true;
528
+ }
529
+ }
530
+ return false;
531
+ }
532
+
533
+ // Handle disc movement physics
534
+ function moveDiscs() {
535
+ const friction = 0.98;
536
+ const gravity = 0.1;
537
+
538
+ let allStopped = true;
539
+
540
+ gameState.discs.forEach(disc => {
541
+ if (disc.pocketed) return;
542
+
543
+ const element = disc.element;
544
+
545
+ // Get current velocity from data attributes
546
+ let vx = parseFloat(element.dataset.vx) || 0;
547
+ let vy = parseFloat(element.dataset.vy) || 0;
548
+
549
+ // Apply friction
550
+ vx *= friction;
551
+ vy *= friction;
552
+
553
+ // Apply gravity (slight downward pull)
554
+ vy += gravity;
555
+
556
+ // Update position
557
+ disc.x += vx;
558
+ disc.y += vy;
559
+
560
+ // Check boundaries
561
+ const containerWidth = gameContainer.offsetWidth;
562
+ const containerHeight = gameContainer.offsetHeight;
563
+ const discSize = parseInt(element.style.width);
564
+
565
+ // Bounce off walls
566
+ if (disc.x - discSize/2 < 5 || disc.x + discSize/2 > 95) {
567
+ vx *= -0.8; // Bounce with some energy loss
568
+ }
569
+
570
+ if (disc.y - discSize/2 < 5 || disc.y + discSize/2 > 95) {
571
+ vy *= -0.8; // Bounce with some energy loss
572
+ }
573
+
574
+ // Check if pocketed
575
+ if (checkPocketed(disc)) {
576
+ disc.pocketed = true;
577
+ element.style.display = 'none';
578
+
579
+ // Update score
580
+ if (disc.type === 'queen') {
581
+ gameState.queenPocketed = true;
582
+ } else if (disc.type === 'purple' && gameState.currentPlayer === 'Ash') {
583
+ gameState.scores.Ash++;
584
+ } else if ((disc.type === 'green' || disc.type === 'white') && gameState.currentPlayer === 'MD') {
585
+ gameState.scores.MD++;
586
+ }
587
+
588
+ updateScores();
589
+ }
590
+
591
+ // Update element position
592
+ element.style.left = `${disc.x}%`;
593
+ element.style.top = `${disc.y}%`;
594
+
595
+ // Store velocity for next frame
596
+ element.dataset.vx = vx;
597
+ element.dataset.vy = vy;
598
+
599
+ // Check if any disc is still moving
600
+ if (Math.abs(vx) > 0.01 || Math.abs(vy) > 0.01) {
601
+ allStopped = false;
602
+ }
603
+ });
604
+
605
+ // If all discs stopped, switch player
606
+ if (allStopped && !gameState.gameOver) {
607
+ gameState.currentPlayer = gameState.currentPlayer === 'Ash' ? 'MD' : 'Ash';
608
+ updateGameStatus();
609
+
610
+ // Check for game over conditions
611
+ if (gameState.queenPocketed) {
612
+ // Simple game over condition: queen pocketed and player has at least one disc
613
+ const currentPlayerDiscs = gameState.discs.filter(d =>
614
+ !d.pocketed &&
615
+ ((d.type === 'purple' && gameState.currentPlayer === 'Ash') ||
616
+ ((d.type === 'green' || d.type === 'white') && gameState.currentPlayer === 'MD'))
617
+ );
618
+
619
+ if (currentPlayerDiscs.length === 0) {
620
+ gameState.gameOver = true;
621
+ updateGameStatus();
622
+ }
623
+ }
624
+ } else if (!allStopped) {
625
+ // Continue animation if discs are still moving
626
+ requestAnimationFrame(moveDiscs);
627
+ }
628
+ }
629
+
630
+ // Handle striker drag
631
+ function handleStrikerDrag(e) {
632
+ if (gameState.gameOver) return;
633
+
634
+ const rect = gameContainer.getBoundingClientRect();
635
+ const x = ((e.clientX || e.touches[0].clientX) - rect.left) / rect.width * 100;
636
+ const y = ((e.clientY || e.touches[0].clientY) - rect.top) / rect.height * 100;
637
+
638
+ // Calculate angle and distance from striker zone center
639
+ const zoneCenterX = 30;
640
+ const zoneCenterY = 85;
641
+ const dx = x - zoneCenterX;
642
+ const dy = y - zoneCenterY;
643
+ const distance = Math.sqrt(dx * dx + dy * dy);
644
+
645
+ // Limit striker movement to within the striker zone
646
+ if (distance <= 15) { // 15% radius for striker zone
647
+ striker.style.left = `${x}%`;
648
+ striker.style.top = `${y}%`;
649
+
650
+ // Update striker position in game state
651
+ gameState.striker.x = x;
652
+ gameState.striker.y = y;
653
+
654
+ // Calculate angle for trajectory
655
+ const angle = Math.atan2(dy, dx);
656
+ gameState.striker.angle = angle;
657
+
658
+ // Calculate power based on distance from center
659
+ const power = Math.min(distance / 15 * 100, 100);
660
+ gameState.striker.power = power;
661
+
662
+ // Update trajectory line
663
+ updateTrajectory();
664
+ }
665
+ }
666
+
667
+ // Update trajectory line
668
+ function updateTrajectory() {
669
+ const angle = gameState.striker.angle;
670
+ const power = gameState.striker.power;
671
+
672
+ // Calculate end point of trajectory
673
+ const length = 50 + power * 0.5; // Base length + power factor
674
+ const endX = gameState.striker.x + Math.cos(angle) * length;
675
+ const endY = gameState.striker.y + Math.sin(angle) * length;
676
+
677
+ // Update trajectory line
678
+ trajectoryLine.style.left = `${gameState.striker.x}%`;
679
+ trajectoryLine.style.top = `${gameState.striker.y}%`;
680
+ trajectoryLine.style.width = `${length}%`;
681
+ trajectoryLine.style.transform = `rotate(${angle}rad)`;
682
+ trajectoryLine.style.transformOrigin = '0 50%';
683
+
684
+ // Update trajectory arrow
685
+ trajectoryArrow.style.left = `${endX}%`;
686
+ trajectoryArrow.style.top = `${endY}%`;
687
+ trajectoryArrow.style.transform = `rotate(${angle + Math.PI/2}rad) translateY(-50%)`;
688
+ }
689
+
690
+ // Handle shoot
691
+ function handleShoot() {
692
+ if (gameState.gameOver) return;
693
+
694
+ // Calculate velocity based on angle and power
695
+ const angle = gameState.striker.angle;
696
+ const power = gameState.striker.power / 20; // Scale down power
697
+
698
+ const vx = Math.cos(angle) * power;
699
+ const vy = Math.sin(angle) * power;
700
+
701
+ // Apply velocity to striker
702
+ striker.dataset.vx = vx;
703
+ striker.dataset.vy = vy;
704
+
705
+ // Add striker to discs for physics
706
+ gameState.discs.push({
707
+ element: striker,
708
+ x: gameState.striker.x,
709
+ y: gameState.striker.y,
710
+ type: 'striker',
711
+ pocketed: false
712
+ });
713
+
714
+ // Start disc movement
715
+ moveDiscs();
716
+
717
+ // Hide trajectory
718
+ trajectoryLine.style.display = 'none';
719
+ trajectoryArrow.style.display = 'none';
720
+ }
721
+
722
+ // AI opponent move
723
+ function aiMove() {
724
+ if (gameState.currentPlayer !== 'MD' || gameState.gameOver) return;
725
+
726
+ gameStatus.textContent = "AI is thinking...";
727
+
728
+ // Simple AI: target a random disc
729
+ setTimeout(() => {
730
+ const targetDisc = gameState.discs.find(d =>
731
+ !d.pocketed &&
732
+ (d.type === 'green' || d.type === 'white') &&
733
+ d.element.style.display !== 'none'
734
+ );
735
+
736
+ if (targetDisc) {
737
+ // Calculate angle to target disc
738
+ const dx = targetDisc.x - 30; // Striker zone center X
739
+ const dy = targetDisc.y - 85; // Striker zone center Y
740
+ const angle = Math.atan2(dy, dx);
741
+
742
+ // Set striker position (slightly offset from center)
743
+ const offsetX = 30 + Math.cos(angle) * 5;
744
+ const offsetY = 85 + Math.sin(angle) * 5;
745
+
746
+ striker.style.left = `${offsetX}%`;
747
+ striker.style.top = `${offsetY}%`;
748
+
749
+ gameState.striker.x = offsetX;
750
+ gameState.striker.y = offsetY;
751
+ gameState.striker.angle = angle;
752
+ gameState.striker.power = 50 + Math.random() * 50; // Random power
753
+
754
+ updateTrajectory();
755
+
756
+ // Shoot after a short delay
757
+ setTimeout(handleShoot, 1000);
758
+ } else {
759
+ // No discs to target, end turn
760
+ gameState.currentPlayer = 'Ash';
761
+ updateGameStatus();
762
+ }
763
+ }, 1500);
764
+ }
765
+
766
+ // Event listeners
767
+ striker.addEventListener('mousedown', () => {
768
+ gameState.striker.isDragging = true;
769
+ trajectoryLine.style.display = 'block';
770
+ trajectoryArrow.style.display = 'block';
771
+ });
772
+
773
+ striker.addEventListener('touchstart', (e) => {
774
+ e.preventDefault();
775
+ gameState.striker.isDragging = true;
776
+ trajectoryLine.style.display = 'block';
777
+ trajectoryArrow.style.display = 'block';
778
+ });
779
+
780
+ document.addEventListener('mousemove', (e) => {
781
+ if (gameState.striker.isDragging) {
782
+ handleStrikerDrag(e);
783
+ }
784
+ });
785
+
786
+ document.addEventListener('touchmove', (e) => {
787
+ if (gameState.striker.isDragging) {
788
+ e.preventDefault();
789
+ handleStrikerDrag(e);
790
+ }
791
+ });
792
+
793
+ document.addEventListener('mouseup', () => {
794
+ gameState.striker.isDragging = false;
795
+ });
796
+
797
+ document.addEventListener('touchend', () => {
798
+ gameState.striker.isDragging = false;
799
+ });
800
+
801
+ shootBtn.addEventListener('click', handleShoot);
802
+
803
+ resetBtn.addEventListener('click', initGame);
804
+
805
+ aiBtn.addEventListener('click', () => {
806
+ if (gameState.currentPlayer === 'MD') {
807
+ aiMove();
808
+ } else {
809
+ gameStatus.textContent = "It's not the AI's turn yet!";
810
+ }
811
+ });
812
+
813
+ // Initialize the game
814
+ initGame();
815
+ });
816
+ </script>
817
+ </body>
818
+ </html>