DanPoliakov commited on
Commit
4bbd9e8
·
verified ·
1 Parent(s): 969cce7

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +784 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Cybernetic Pong
3
- emoji: 📉
4
- colorFrom: purple
5
- colorTo: red
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: cybernetic-pong
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: blue
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,784 @@
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>Human-Like AI Pong</title>
7
+ <style>
8
+ @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap');
9
+
10
+ * {
11
+ margin: 0;
12
+ padding: 0;
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ body {
17
+ background-color: #0a0a1a;
18
+ overflow: hidden;
19
+ font-family: 'Orbitron', sans-serif;
20
+ color: #00fffc;
21
+ height: 100vh;
22
+ display: flex;
23
+ flex-direction: column;
24
+ align-items: center;
25
+ justify-content: center;
26
+ }
27
+
28
+ #game-container {
29
+ position: relative;
30
+ width: 800px;
31
+ height: 500px;
32
+ border: 3px solid #00fffc;
33
+ box-shadow: 0 0 20px #00fffc, inset 0 0 20px #00fffc;
34
+ border-radius: 5px;
35
+ overflow: hidden;
36
+ }
37
+
38
+ #game-board {
39
+ width: 100%;
40
+ height: 100%;
41
+ background-color: rgba(0, 10, 20, 0.7);
42
+ position: relative;
43
+ }
44
+
45
+ .paddle {
46
+ position: absolute;
47
+ width: 15px;
48
+ height: 100px;
49
+ background: linear-gradient(to bottom, #00fffc, #0066ff);
50
+ border-radius: 5px;
51
+ box-shadow: 0 0 10px #00fffc;
52
+ transition: top 0.1s ease-out;
53
+ }
54
+
55
+ #left-paddle {
56
+ left: 20px;
57
+ top: 200px;
58
+ }
59
+
60
+ #right-paddle {
61
+ right: 20px;
62
+ top: 200px;
63
+ }
64
+
65
+ #ball {
66
+ position: absolute;
67
+ width: 20px;
68
+ height: 20px;
69
+ background: radial-gradient(circle, #ff00ff, #ff0066);
70
+ border-radius: 50%;
71
+ box-shadow: 0 0 15px #ff00ff;
72
+ left: 390px;
73
+ top: 240px;
74
+ }
75
+
76
+ .score {
77
+ position: absolute;
78
+ font-size: 3rem;
79
+ top: 20px;
80
+ }
81
+
82
+ #left-score {
83
+ left: 100px;
84
+ }
85
+
86
+ #right-score {
87
+ right: 100px;
88
+ }
89
+
90
+ .center-line {
91
+ position: absolute;
92
+ width: 2px;
93
+ height: 100%;
94
+ background: linear-gradient(to bottom,
95
+ transparent 0%,
96
+ #00fffc 10%,
97
+ transparent 20%,
98
+ transparent 80%,
99
+ #00fffc 90%,
100
+ transparent 100%);
101
+ left: 50%;
102
+ transform: translateX(-50%);
103
+ }
104
+
105
+ .explosion {
106
+ position: absolute;
107
+ width: 40px;
108
+ height: 40px;
109
+ border-radius: 50%;
110
+ background: radial-gradient(circle, #ff6600, #ff0066);
111
+ transform: scale(0);
112
+ opacity: 0.8;
113
+ pointer-events: none;
114
+ }
115
+
116
+ #start-screen {
117
+ position: absolute;
118
+ width: 100%;
119
+ height: 100%;
120
+ background-color: rgba(0, 5, 15, 0.9);
121
+ display: flex;
122
+ flex-direction: column;
123
+ align-items: center;
124
+ justify-content: center;
125
+ z-index: 10;
126
+ }
127
+
128
+ #start-screen h1 {
129
+ font-size: 3rem;
130
+ margin-bottom: 30px;
131
+ text-shadow: 0 0 10px #00fffc;
132
+ animation: pulse 2s infinite;
133
+ }
134
+
135
+ #start-screen p {
136
+ margin-bottom: 30px;
137
+ text-align: center;
138
+ max-width: 80%;
139
+ }
140
+
141
+ #start-button {
142
+ background: linear-gradient(to right, #0066ff, #00fffc);
143
+ border: none;
144
+ color: #0a0a1a;
145
+ padding: 15px 30px;
146
+ font-size: 1.2rem;
147
+ font-family: 'Orbitron', sans-serif;
148
+ border-radius: 5px;
149
+ cursor: pointer;
150
+ box-shadow: 0 0 15px #00fffc;
151
+ transition: all 0.3s;
152
+ }
153
+
154
+ #start-button:hover {
155
+ transform: scale(1.05);
156
+ box-shadow: 0 0 20px #00fffc;
157
+ }
158
+
159
+ #game-over {
160
+ position: absolute;
161
+ width: 100%;
162
+ height: 100%;
163
+ background-color: rgba(0, 5, 15, 0.9);
164
+ display: none;
165
+ flex-direction: column;
166
+ align-items: center;
167
+ justify-content: center;
168
+ z-index: 10;
169
+ }
170
+
171
+ #game-over h1 {
172
+ font-size: 3rem;
173
+ margin-bottom: 20px;
174
+ color: #ff0066;
175
+ text-shadow: 0 0 10px #ff0066;
176
+ }
177
+
178
+ #final-score {
179
+ font-size: 2rem;
180
+ margin-bottom: 30px;
181
+ }
182
+
183
+ #restart-button {
184
+ background: linear-gradient(to right, #ff0066, #ff6600);
185
+ border: none;
186
+ color: #0a0a1a;
187
+ padding: 15px 30px;
188
+ font-size: 1.2rem;
189
+ font-family: 'Orbitron', sans-serif;
190
+ border-radius: 5px;
191
+ cursor: pointer;
192
+ box-shadow: 0 0 15px #ff0066;
193
+ transition: all 0.3s;
194
+ }
195
+
196
+ #restart-button:hover {
197
+ transform: scale(1.05);
198
+ box-shadow: 0 0 20px #ff0066;
199
+ }
200
+
201
+ .particle {
202
+ position: absolute;
203
+ width: 4px;
204
+ height: 4px;
205
+ border-radius: 50%;
206
+ background-color: #00fffc;
207
+ pointer-events: none;
208
+ }
209
+
210
+ @keyframes pulse {
211
+ 0% { transform: scale(1); }
212
+ 50% { transform: scale(1.05); }
213
+ 100% { transform: scale(1); }
214
+ }
215
+
216
+ .cyber-grid {
217
+ position: absolute;
218
+ width: 100%;
219
+ height: 100%;
220
+ background-image:
221
+ linear-gradient(rgba(0, 255, 252, 0.05) 1px, transparent 1px),
222
+ linear-gradient(90deg, rgba(0, 255, 252, 0.05) 1px, transparent 1px);
223
+ background-size: 20px 20px;
224
+ pointer-events: none;
225
+ }
226
+
227
+ #instructions {
228
+ position: absolute;
229
+ bottom: 10px;
230
+ font-size: 0.8rem;
231
+ color: #00fffc;
232
+ text-align: center;
233
+ width: 100%;
234
+ }
235
+
236
+ #game-mode {
237
+ margin-top: 20px;
238
+ display: flex;
239
+ gap: 15px;
240
+ }
241
+
242
+ .mode-button {
243
+ background: rgba(0, 255, 252, 0.2);
244
+ border: 1px solid #00fffc;
245
+ color: #00fffc;
246
+ padding: 10px 20px;
247
+ font-family: 'Orbitron', sans-serif;
248
+ border-radius: 5px;
249
+ cursor: pointer;
250
+ transition: all 0.3s;
251
+ }
252
+
253
+ .mode-button:hover {
254
+ background: rgba(0, 255, 252, 0.4);
255
+ }
256
+
257
+ .mode-button.active {
258
+ background: linear-gradient(to right, #0066ff, #00fffc);
259
+ color: #0a0a1a;
260
+ box-shadow: 0 0 10px #00fffc;
261
+ }
262
+
263
+ #difficulty {
264
+ margin-top: 15px;
265
+ display: flex;
266
+ gap: 10px;
267
+ }
268
+
269
+ .difficulty-button {
270
+ background: rgba(0, 255, 252, 0.1);
271
+ border: 1px solid #00fffc;
272
+ color: #00fffc;
273
+ padding: 8px 15px;
274
+ font-family: 'Orbitron', sans-serif;
275
+ font-size: 0.8rem;
276
+ border-radius: 5px;
277
+ cursor: pointer;
278
+ transition: all 0.3s;
279
+ }
280
+
281
+ .difficulty-button:hover {
282
+ background: rgba(0, 255, 252, 0.3);
283
+ }
284
+
285
+ .difficulty-button.active {
286
+ background: rgba(0, 255, 252, 0.5);
287
+ box-shadow: 0 0 5px #00fffc;
288
+ }
289
+
290
+ .reaction-delay {
291
+ position: absolute;
292
+ width: 100px;
293
+ height: 10px;
294
+ background: rgba(255, 255, 255, 0.2);
295
+ border-radius: 5px;
296
+ top: 10px;
297
+ left: 50%;
298
+ transform: translateX(-50%);
299
+ overflow: hidden;
300
+ display: none;
301
+ }
302
+
303
+ .reaction-progress {
304
+ height: 100%;
305
+ width: 0%;
306
+ background: linear-gradient(to right, #ff0066, #ff6600);
307
+ transition: width 0.1s linear;
308
+ }
309
+ </style>
310
+ </head>
311
+ <body>
312
+ <div id="game-container">
313
+ <div class="cyber-grid"></div>
314
+ <div id="game-board">
315
+ <div class="center-line"></div>
316
+ <div id="left-paddle" class="paddle"></div>
317
+ <div id="right-paddle" class="paddle"></div>
318
+ <div id="ball"></div>
319
+ <div id="left-score" class="score">0</div>
320
+ <div id="right-score" class="score">0</div>
321
+ <div class="reaction-delay">
322
+ <div class="reaction-progress"></div>
323
+ </div>
324
+
325
+ <div id="start-screen">
326
+ <h1>HUMAN-LIKE PONG</h1>
327
+ <p>Experience a more realistic opponent with human-like reactions and imperfections.<br>
328
+ The AI will make mistakes, hesitate, and even show emotional responses!</p>
329
+ <button id="start-button">START MATCH</button>
330
+ <div id="game-mode">
331
+ <button class="mode-button active" data-mode="single">SINGLE PLAYER</button>
332
+ <button class="mode-button" data-mode="multi">TWO PLAYERS</button>
333
+ </div>
334
+ <div id="difficulty">
335
+ <button class="difficulty-button active" data-difficulty="medium">MEDIUM</button>
336
+ <button class="difficulty-button" data-difficulty="easy">EASY</button>
337
+ <button class="difficulty-button" data-difficulty="hard">HARD</button>
338
+ </div>
339
+ <div id="instructions">
340
+ Controls: W/S keys for Player 1 | Up/Down arrows for Player 2
341
+ </div>
342
+ </div>
343
+
344
+ <div id="game-over">
345
+ <h1>GAME OVER</h1>
346
+ <div id="final-score"></div>
347
+ <button id="restart-button">REMATCH</button>
348
+ </div>
349
+ </div>
350
+ </div>
351
+
352
+ <script>
353
+ document.addEventListener('DOMContentLoaded', () => {
354
+ // Game elements
355
+ const gameBoard = document.getElementById('game-board');
356
+ const leftPaddle = document.getElementById('left-paddle');
357
+ const rightPaddle = document.getElementById('right-paddle');
358
+ const ball = document.getElementById('ball');
359
+ const leftScore = document.getElementById('left-score');
360
+ const rightScore = document.getElementById('right-score');
361
+ const startScreen = document.getElementById('start-screen');
362
+ const gameOverScreen = document.getElementById('game-over');
363
+ const finalScore = document.getElementById('final-score');
364
+ const startButton = document.getElementById('start-button');
365
+ const restartButton = document.getElementById('restart-button');
366
+ const modeButtons = document.querySelectorAll('.mode-button');
367
+ const difficultyButtons = document.querySelectorAll('.difficulty-button');
368
+ const reactionDelay = document.querySelector('.reaction-delay');
369
+ const reactionProgress = document.querySelector('.reaction-progress');
370
+
371
+ // Game variables
372
+ let gameRunning = false;
373
+ let leftScoreValue = 0;
374
+ let rightScoreValue = 0;
375
+ const winningScore = 5;
376
+ let gameMode = 'single'; // 'single' or 'multi'
377
+ let difficulty = 'medium'; // 'easy', 'medium', 'hard'
378
+
379
+ // Ball variables
380
+ let ballX = 390;
381
+ let ballY = 240;
382
+ let ballSpeedX = 4;
383
+ let ballSpeedY = 4;
384
+
385
+ // Paddle variables
386
+ const paddleHeight = 100;
387
+ const paddleWidth = 15;
388
+ let leftPaddleY = 200;
389
+ let rightPaddleY = 200;
390
+ const paddleSpeed = 8;
391
+
392
+ // AI personality variables
393
+ let aiReactionTime = 300; // ms
394
+ let aiAccuracy = 0.8; // 0-1
395
+ let aiMood = 'neutral'; // 'happy', 'neutral', 'angry'
396
+ let aiMistakeChance = 0.2; // 0-1
397
+ let aiOverShootChance = 0.3; // 0-1
398
+ let aiDecisionDelay = 0;
399
+ let aiLastDecisionTime = 0;
400
+ let aiTargetY = 200;
401
+ let aiIsMoving = false;
402
+
403
+ // Key states
404
+ const keys = {
405
+ w: false,
406
+ s: false,
407
+ ArrowUp: false,
408
+ ArrowDown: false
409
+ };
410
+
411
+ // Event listeners
412
+ startButton.addEventListener('click', startGame);
413
+ restartButton.addEventListener('click', restartGame);
414
+
415
+ modeButtons.forEach(button => {
416
+ button.addEventListener('click', () => {
417
+ modeButtons.forEach(btn => btn.classList.remove('active'));
418
+ button.classList.add('active');
419
+ gameMode = button.dataset.mode;
420
+ });
421
+ });
422
+
423
+ difficultyButtons.forEach(button => {
424
+ button.addEventListener('click', () => {
425
+ difficultyButtons.forEach(btn => btn.classList.remove('active'));
426
+ button.classList.add('active');
427
+ difficulty = button.dataset.difficulty;
428
+ setDifficultyParameters();
429
+ });
430
+ });
431
+
432
+ document.addEventListener('keydown', (e) => {
433
+ if (e.key in keys) {
434
+ keys[e.key] = true;
435
+ }
436
+ });
437
+
438
+ document.addEventListener('keyup', (e) => {
439
+ if (e.key in keys) {
440
+ keys[e.key] = false;
441
+ }
442
+ });
443
+
444
+ // Set AI parameters based on difficulty
445
+ function setDifficultyParameters() {
446
+ switch(difficulty) {
447
+ case 'easy':
448
+ aiReactionTime = 400;
449
+ aiAccuracy = 0.6;
450
+ aiMistakeChance = 0.4;
451
+ aiOverShootChance = 0.5;
452
+ break;
453
+ case 'medium':
454
+ aiReactionTime = 300;
455
+ aiAccuracy = 0.8;
456
+ aiMistakeChance = 0.2;
457
+ aiOverShootChance = 0.3;
458
+ break;
459
+ case 'hard':
460
+ aiReactionTime = 200;
461
+ aiAccuracy = 0.95;
462
+ aiMistakeChance = 0.1;
463
+ aiOverShootChance = 0.1;
464
+ break;
465
+ }
466
+ }
467
+
468
+ // Start game
469
+ function startGame() {
470
+ startScreen.style.display = 'none';
471
+ gameRunning = true;
472
+ resetBall();
473
+ gameLoop();
474
+ }
475
+
476
+ // Restart game
477
+ function restartGame() {
478
+ gameOverScreen.style.display = 'none';
479
+ leftScoreValue = 0;
480
+ rightScoreValue = 0;
481
+ leftScore.textContent = '0';
482
+ rightScore.textContent = '0';
483
+ resetBall();
484
+ gameRunning = true;
485
+ gameLoop();
486
+ }
487
+
488
+ // Game loop
489
+ function gameLoop() {
490
+ if (!gameRunning) return;
491
+
492
+ update();
493
+ render();
494
+
495
+ requestAnimationFrame(gameLoop);
496
+ }
497
+
498
+ // Update game state
499
+ function update() {
500
+ // Move player paddle
501
+ if (keys.w && leftPaddleY > 0) {
502
+ leftPaddleY -= paddleSpeed;
503
+ }
504
+ if (keys.s && leftPaddleY < gameBoard.clientHeight - paddleHeight) {
505
+ leftPaddleY += paddleSpeed;
506
+ }
507
+
508
+ // Move second player paddle in multiplayer mode
509
+ if (gameMode === 'multi') {
510
+ if (keys.ArrowUp && rightPaddleY > 0) {
511
+ rightPaddleY -= paddleSpeed;
512
+ }
513
+ if (keys.ArrowDown && rightPaddleY < gameBoard.clientHeight - paddleHeight) {
514
+ rightPaddleY += paddleSpeed;
515
+ }
516
+ } else {
517
+ // Single player mode - AI controls right paddle
518
+ moveHumanLikeAiPaddle();
519
+ }
520
+
521
+ // Update paddle positions
522
+ leftPaddle.style.top = leftPaddleY + 'px';
523
+ rightPaddle.style.top = rightPaddleY + 'px';
524
+
525
+ // Move ball
526
+ ballX += ballSpeedX;
527
+ ballY += ballSpeedY;
528
+
529
+ // Ball collision with top and bottom
530
+ if (ballY <= 0 || ballY >= gameBoard.clientHeight - 20) {
531
+ ballSpeedY = -ballSpeedY;
532
+ createParticles(ballX, ballY, 10);
533
+ }
534
+
535
+ // Ball collision with paddles
536
+ if (
537
+ ballX <= 35 &&
538
+ ballX >= 20 &&
539
+ ballY + 20 >= leftPaddleY &&
540
+ ballY <= leftPaddleY + paddleHeight
541
+ ) {
542
+ ballSpeedX = -ballSpeedX * 1.05;
543
+ ballSpeedY = (ballY - (leftPaddleY + paddleHeight / 2)) * 0.2;
544
+ createExplosion(ballX, ballY);
545
+ createParticles(ballX, ballY, 20);
546
+ }
547
+
548
+ if (
549
+ ballX >= gameBoard.clientWidth - 35 &&
550
+ ballX <= gameBoard.clientWidth - 20 &&
551
+ ballY + 20 >= rightPaddleY &&
552
+ ballY <= rightPaddleY + paddleHeight
553
+ ) {
554
+ ballSpeedX = -ballSpeedX * 1.05;
555
+ ballSpeedY = (ballY - (rightPaddleY + paddleHeight / 2)) * 0.2;
556
+ createExplosion(ballX, ballY);
557
+ createParticles(ballX, ballY, 20);
558
+ }
559
+
560
+ // Ball out of bounds (score)
561
+ if (ballX < 0) {
562
+ rightScoreValue++;
563
+ rightScore.textContent = rightScoreValue;
564
+ updateAiMood();
565
+ checkGameOver();
566
+ resetBall();
567
+ }
568
+
569
+ if (ballX > gameBoard.clientWidth) {
570
+ leftScoreValue++;
571
+ leftScore.textContent = leftScoreValue;
572
+ updateAiMood();
573
+ checkGameOver();
574
+ resetBall();
575
+ }
576
+ }
577
+
578
+ // Human-like AI paddle movement
579
+ function moveHumanLikeAiPaddle() {
580
+ const now = Date.now();
581
+
582
+ // Only make decisions when ball is coming towards AI
583
+ if (ballSpeedX > 0) {
584
+ // Calculate predicted ball position
585
+ const timeToReach = (gameBoard.clientWidth - ballX) / ballSpeedX;
586
+ const predictedY = ballY + (ballSpeedY * timeToReach);
587
+
588
+ // Add some prediction error based on accuracy
589
+ const predictionError = (1 - aiAccuracy) * 100;
590
+ const errorY = (Math.random() * predictionError * 2) - predictionError;
591
+ aiTargetY = Math.max(0, Math.min(gameBoard.clientHeight - paddleHeight, predictedY + errorY));
592
+
593
+ // Sometimes overshoot the target
594
+ if (Math.random() < aiOverShootChance) {
595
+ aiTargetY += (Math.random() > 0.5 ? 1 : -1) * paddleHeight * 0.3;
596
+ }
597
+
598
+ // Sometimes make a mistake and move the wrong way
599
+ if (Math.random() < aiMistakeChance) {
600
+ aiTargetY = (Math.random() * (gameBoard.clientHeight - paddleHeight));
601
+ }
602
+
603
+ // Set a random delay before reacting (human reaction time)
604
+ if (!aiIsMoving) {
605
+ aiDecisionDelay = Math.max(100, aiReactionTime * (0.5 + Math.random()));
606
+ aiLastDecisionTime = now;
607
+ aiIsMoving = true;
608
+
609
+ // Show reaction delay visualization
610
+ reactionDelay.style.display = 'block';
611
+ reactionProgress.style.width = '0%';
612
+ const reactionInterval = setInterval(() => {
613
+ const elapsed = Date.now() - aiLastDecisionTime;
614
+ const progress = Math.min(100, (elapsed / aiDecisionDelay) * 100);
615
+ reactionProgress.style.width = `${progress}%`;
616
+
617
+ if (progress >= 100) {
618
+ clearInterval(reactionInterval);
619
+ reactionDelay.style.display = 'none';
620
+ }
621
+ }, 16);
622
+ }
623
+ }
624
+
625
+ // Move towards target after delay
626
+ if (aiIsMoving && now - aiLastDecisionTime > aiDecisionDelay) {
627
+ const paddleCenter = rightPaddleY + paddleHeight / 2;
628
+ const distanceToTarget = aiTargetY + paddleHeight / 2 - paddleCenter;
629
+
630
+ // Add some human-like variability to speed
631
+ const currentSpeed = paddleSpeed * (0.8 + Math.random() * 0.4);
632
+
633
+ if (Math.abs(distanceToTarget) > 5) {
634
+ if (distanceToTarget > 0 && rightPaddleY < gameBoard.clientHeight - paddleHeight) {
635
+ rightPaddleY += currentSpeed;
636
+ } else if (distanceToTarget < 0 && rightPaddleY > 0) {
637
+ rightPaddleY -= currentSpeed;
638
+ }
639
+ } else {
640
+ aiIsMoving = false;
641
+
642
+ // Sometimes pause before next movement
643
+ if (Math.random() < 0.3) {
644
+ aiDecisionDelay = Math.max(100, aiReactionTime * (0.5 + Math.random()));
645
+ aiLastDecisionTime = now;
646
+ }
647
+ }
648
+ }
649
+ }
650
+
651
+ // Update AI mood based on score
652
+ function updateAiMood() {
653
+ const scoreDiff = rightScoreValue - leftScoreValue;
654
+
655
+ if (scoreDiff >= 2) {
656
+ aiMood = 'happy';
657
+ // When happy, AI might get overconfident and make more mistakes
658
+ aiMistakeChance = 0.3;
659
+ aiOverShootChance = 0.4;
660
+ } else if (scoreDiff <= -2) {
661
+ aiMood = 'angry';
662
+ // When angry, AI might try too hard and overshoot more
663
+ aiMistakeChance = 0.1;
664
+ aiOverShootChance = 0.5;
665
+ } else {
666
+ aiMood = 'neutral';
667
+ aiMistakeChance = 0.2;
668
+ aiOverShootChance = 0.3;
669
+ }
670
+
671
+ // Adjust based on difficulty
672
+ setDifficultyParameters();
673
+ }
674
+
675
+ // Render game
676
+ function render() {
677
+ ball.style.left = ballX + 'px';
678
+ ball.style.top = ballY + 'px';
679
+ }
680
+
681
+ // Reset ball position
682
+ function resetBall() {
683
+ ballX = gameBoard.clientWidth / 2 - 10;
684
+ ballY = gameBoard.clientHeight / 2 - 10;
685
+
686
+ // Random direction
687
+ ballSpeedX = (Math.random() > 0.5 ? 1 : -1) * 4;
688
+ ballSpeedY = (Math.random() * 4 - 2);
689
+
690
+ // Reset AI movement state
691
+ aiIsMoving = false;
692
+ reactionDelay.style.display = 'none';
693
+
694
+ // Small delay before ball starts moving
695
+ setTimeout(() => {
696
+ if (gameRunning) {
697
+ ball.style.display = 'block';
698
+ }
699
+ }, 500);
700
+ }
701
+
702
+ // Check if game is over
703
+ function checkGameOver() {
704
+ if (leftScoreValue >= winningScore || rightScoreValue >= winningScore) {
705
+ gameRunning = false;
706
+ const winner = leftScoreValue >= winningScore ? 'Player 1' : (gameMode === 'multi' ? 'Player 2' : 'AI');
707
+ finalScore.textContent = `${winner} wins! (${leftScoreValue}-${rightScoreValue})`;
708
+ gameOverScreen.style.display = 'flex';
709
+
710
+ // Create celebration explosions
711
+ for (let i = 0; i < 10; i++) {
712
+ setTimeout(() => {
713
+ const x = Math.random() * gameBoard.clientWidth;
714
+ const y = Math.random() * gameBoard.clientHeight;
715
+ createExplosion(x, y);
716
+ }, i * 200);
717
+ }
718
+ }
719
+ }
720
+
721
+ // Create explosion effect
722
+ function createExplosion(x, y) {
723
+ const explosion = document.createElement('div');
724
+ explosion.className = 'explosion';
725
+ explosion.style.left = x + 'px';
726
+ explosion.style.top = y + 'px';
727
+ gameBoard.appendChild(explosion);
728
+
729
+ // Animate explosion
730
+ const animation = explosion.animate([
731
+ { transform: 'scale(0)', opacity: 0.8 },
732
+ { transform: 'scale(3)', opacity: 0 }
733
+ ], {
734
+ duration: 500,
735
+ easing: 'cubic-bezier(0.1, 0.8, 0.2, 1)'
736
+ });
737
+
738
+ animation.onfinish = () => {
739
+ gameBoard.removeChild(explosion);
740
+ };
741
+ }
742
+
743
+ // Create particle effect
744
+ function createParticles(x, y, count) {
745
+ for (let i = 0; i < count; i++) {
746
+ const particle = document.createElement('div');
747
+ particle.className = 'particle';
748
+ particle.style.left = x + 'px';
749
+ particle.style.top = y + 'px';
750
+
751
+ // Random color
752
+ const colors = ['#00fffc', '#ff00ff', '#ff6600', '#0066ff'];
753
+ const randomColor = colors[Math.floor(Math.random() * colors.length)];
754
+ particle.style.backgroundColor = randomColor;
755
+
756
+ gameBoard.appendChild(particle);
757
+
758
+ // Random direction and speed
759
+ const angle = Math.random() * Math.PI * 2;
760
+ const speed = Math.random() * 5 + 2;
761
+
762
+ const animation = particle.animate([
763
+ {
764
+ transform: 'translate(0, 0) scale(1)',
765
+ opacity: 1
766
+ },
767
+ {
768
+ transform: `translate(${Math.cos(angle) * speed * 10}px, ${Math.sin(angle) * speed * 10}px) scale(0)`,
769
+ opacity: 0
770
+ }
771
+ ], {
772
+ duration: 1000,
773
+ easing: 'cubic-bezier(0.1, 0.8, 0.2, 1)'
774
+ });
775
+
776
+ animation.onfinish = () => {
777
+ gameBoard.removeChild(particle);
778
+ };
779
+ }
780
+ }
781
+ });
782
+ </script>
783
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <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>
784
+ </html>