zeroed-some/sprong / ef6eca3

Browse files

ai can rotate too

Authored by espadonne
SHA
ef6eca3882ab6362770232bace93b625e30e97d4
Parents
5b429e8
Tree
0182e1e

3 changed files

StatusFile+-
M js/ai.js 159 5
M js/rendering.js 11 5
M js/sprong.js 4 0
js/ai.jsmodified
@@ -16,6 +16,13 @@ const AI_NERVOUS_ENERGY = 0.5; // Random fidgeting energy
1616
 const AI_PREDICTIVE_MOVEMENT = 0.7;   // How much AI moves based on prediction
1717
 const AI_TRACKING_AGGRESSION = 0.15;  // How aggressively AI tracks the ball
1818
 
19
+// AI Rotation constants
20
+const AI_ROTATION_UPDATE_RATE = 150;  // How often AI reconsiders rotation (ms)
21
+const AI_ROTATION_SMOOTHING = 0.08;   // How smoothly AI rotates
22
+const AI_DEFENSIVE_ANGLE = 0.2;       // Slight angle for defensive shots
23
+const AI_OFFENSIVE_ANGLE = 0.4;       // Larger angle for offensive shots
24
+const AI_TRICK_SHOT_ANGLE = 0.6;      // Maximum angle for trick shots
25
+const AI_ROTATION_PREDICTION = 1.2;   // How far ahead AI predicts for rotation
1926
 
2027
 // ============= AI SETTINGS =============
2128
 const AI_SETTINGS = {
@@ -33,7 +40,12 @@ const AI_SETTINGS = {
3340
         circularMotion: 0.4,
3441
         phaseSpeed: 0.06,
3542
         idleMovement: 0.3,
36
-        trackingAggression: 0.1
43
+        trackingAggression: 0.1,
44
+        // Rotation settings
45
+        rotationUse: 0.2,           // How often AI uses rotation
46
+        rotationAccuracy: 0.5,       // How accurately AI calculates angle
47
+        rotationSpeed: 0.6,          // How fast AI rotates
48
+        rotationAnticipation: 0.3    // How well AI predicts needed angle
3749
     },
3850
     medium: {
3951
         reactionTime: 200,
@@ -49,7 +61,12 @@ const AI_SETTINGS = {
4961
         circularMotion: 0.6,
5062
         phaseSpeed: 0.08,
5163
         idleMovement: 0.5,
52
-        trackingAggression: 0.15
64
+        trackingAggression: 0.15,
65
+        // Rotation settings
66
+        rotationUse: 0.5,
67
+        rotationAccuracy: 0.7,
68
+        rotationSpeed: 0.8,
69
+        rotationAnticipation: 0.6
5370
     },
5471
     hard: {
5572
         reactionTime: 100,
@@ -65,7 +82,12 @@ const AI_SETTINGS = {
6582
         circularMotion: 0.8,
6683
         phaseSpeed: 0.12,
6784
         idleMovement: 0.8,
68
-        trackingAggression: 0.25
85
+        trackingAggression: 0.25,
86
+        // Rotation settings
87
+        rotationUse: 0.8,
88
+        rotationAccuracy: 0.9,
89
+        rotationSpeed: 1.0,
90
+        rotationAnticipation: 0.85
6991
     }
7092
 };
7193
 
@@ -116,7 +138,14 @@ let aiState = {
116138
     // AI Bop system
117139
     consideringBop: false,
118140
     bopDecisionTime: 0,
119
-    bopTiming: 200
141
+    bopTiming: 200,
142
+    
143
+    // AI Rotation system
144
+    targetRotation: 0,       // Desired rotation angle
145
+    rotationMode: 'NEUTRAL', // NEUTRAL, OFFENSIVE, DEFENSIVE, TRICK_SHOT
146
+    lastRotationUpdate: 0,   // Time of last rotation decision
147
+    plannedShotAngle: 0,     // Angle AI wants to send ball
148
+    rotationConfidence: 0    // How confident AI is in its rotation choice
120149
 };
121150
 
122151
 // ============= MAIN AI HANDLER =============
@@ -129,6 +158,7 @@ function handleAI(currentTime, ball, rightPaddle, rightSupport,
129158
     
130159
     updateAIAggression(leftScore, rightScore);
131160
     updateAILifelikeBehavior(currentTime, height);
161
+    updateAIRotation(currentTime, ball, rightPaddle, width, height, aiSettings);
132162
     
133163
     switch (aiState.mode) {
134164
         case 'TRACKING':
@@ -559,4 +589,128 @@ function executeAIMovement(aiSettings, rightSupport) {
559589
             window.inputBuffer.right *= 0.9; // Slower decay for more visible movement
560590
         }
561591
     }
562
-}
592
+}
593
+
594
+// ============= AI ROTATION SYSTEM =============
595
+function updateAIRotation(currentTime, ball, rightPaddle, width, height, aiSettings) {
596
+    // Only update rotation decision periodically
597
+    if (currentTime - aiState.lastRotationUpdate < AI_ROTATION_UPDATE_RATE) {
598
+        // Still apply rotation smoothly even if not updating decision
599
+        applyAIRotation(aiSettings);
600
+        return;
601
+    }
602
+    
603
+    aiState.lastRotationUpdate = currentTime;
604
+    
605
+    let ballPos = ball.position;
606
+    let ballVel = ball.velocity;
607
+    let paddlePos = rightPaddle.position;
608
+    
609
+    // Check if AI should use rotation
610
+    if (Math.random() > aiSettings.rotationUse) {
611
+        aiState.rotationMode = 'NEUTRAL';
612
+        aiState.targetRotation = 0;
613
+        applyAIRotation(aiSettings);
614
+        return;
615
+    }
616
+    
617
+    // Calculate ball approach
618
+    let ballApproaching = ballVel.x > 0;
619
+    let ballDistance = width - ballPos.x;
620
+    let timeToReach = ballDistance / Math.abs(ballVel.x);
621
+    
622
+    // Predict where ball will be
623
+    let predictedBallY = ballPos.y + ballVel.y * timeToReach * AI_ROTATION_PREDICTION * aiSettings.rotationAnticipation;
624
+    
625
+    // Bounce prediction
626
+    if (predictedBallY < 50) {
627
+        predictedBallY = 100 - predictedBallY;
628
+    } else if (predictedBallY > height - 50) {
629
+        predictedBallY = 2 * (height - 50) - predictedBallY;
630
+    }
631
+    
632
+    // Decide rotation strategy
633
+    if (!ballApproaching || ballDistance > 300) {
634
+        // Return to neutral when ball is far
635
+        aiState.rotationMode = 'NEUTRAL';
636
+        aiState.targetRotation = 0;
637
+    } else if (aiState.mode === 'WINDING_UP' || aiState.mode === 'SWINGING') {
638
+        // Offensive rotation during power shots
639
+        aiState.rotationMode = 'OFFENSIVE';
640
+        
641
+        // Calculate desired shot angle
642
+        let targetY = height / 2; // Aim for center by default
643
+        
644
+        // Try to aim away from player
645
+        if (paddlePos.y < height / 2) {
646
+            targetY = height - 80; // Aim down
647
+        } else {
648
+            targetY = 80; // Aim up
649
+        }
650
+        
651
+        // Calculate required angle
652
+        let deltaY = targetY - paddlePos.y;
653
+        let desiredAngle = Math.atan2(deltaY, width - paddlePos.x) * AI_OFFENSIVE_ANGLE;
654
+        
655
+        // Add some inaccuracy based on difficulty
656
+        let error = (Math.random() - 0.5) * (1 - aiSettings.rotationAccuracy) * 0.5;
657
+        desiredAngle += error;
658
+        
659
+        // Clamp angle
660
+        aiState.targetRotation = Math.max(-ROTATION_MAX_ANGLE, Math.min(ROTATION_MAX_ANGLE, desiredAngle));
661
+        
662
+    } else if (aiState.consideringBop || bopState.right.active) {
663
+        // Trick shot rotation during bop
664
+        aiState.rotationMode = 'TRICK_SHOT';
665
+        
666
+        // More extreme angles for bop shots
667
+        let trickDirection = (paddlePos.y < height / 2) ? 1 : -1;
668
+        aiState.targetRotation = trickDirection * AI_TRICK_SHOT_ANGLE * aiSettings.rotationAccuracy;
669
+        
670
+    } else if (Math.abs(predictedBallY - paddlePos.y) < 30) {
671
+        // Defensive slight angle when ball is coming straight
672
+        aiState.rotationMode = 'DEFENSIVE';
673
+        
674
+        // Slight angle to control return
675
+        let defensiveDirection = (predictedBallY < height / 2) ? -1 : 1;
676
+        aiState.targetRotation = defensiveDirection * AI_DEFENSIVE_ANGLE * aiSettings.rotationAccuracy;
677
+        
678
+    } else {
679
+        // Normal tracking
680
+        aiState.rotationMode = 'NEUTRAL';
681
+        aiState.targetRotation = 0;
682
+    }
683
+    
684
+    // Apply rotation confidence based on AI state
685
+    aiState.rotationConfidence = aiSettings.rotationAccuracy;
686
+    if (aiState.mode === 'RECOVERING') {
687
+        aiState.rotationConfidence *= 0.5; // Less confident when recovering
688
+    }
689
+    
690
+    applyAIRotation(aiSettings);
691
+}
692
+
693
+function applyAIRotation(aiSettings) {
694
+    // Get current rotation state
695
+    let rotState = window.rotationState.right;
696
+    
697
+    // Calculate rotation input needed
698
+    let angleDiff = aiState.targetRotation - rotState.currentAngle;
699
+    let rotationInput = 0;
700
+    
701
+    if (Math.abs(angleDiff) > 0.05) {
702
+        // Determine rotation direction
703
+        rotationInput = Math.sign(angleDiff);
704
+        
705
+        // Scale by AI rotation speed and confidence
706
+        rotationInput *= aiSettings.rotationSpeed * aiState.rotationConfidence;
707
+        
708
+        // Add some imperfection for easier difficulties
709
+        if (aiSettings.rotationAccuracy < 0.8) {
710
+            rotationInput += (Math.random() - 0.5) * 0.2 * (1 - aiSettings.rotationAccuracy);
711
+        }
712
+    }
713
+    
714
+    // Update rotation physics for AI paddle
715
+    updateRotationPhysics('right', rotationInput, window.rightPaddle);
716
+}
js/rendering.jsmodified
@@ -356,26 +356,32 @@ function drawDebugInfo(ball, leftSupport, leftPaddle, rightSupport, rightPaddle,
356356
         text(`Target: ${Math.round(aiState.targetY)} | Intercept: ${Math.round(aiState.interceptY)}`, 10, 170);
357357
         text(`Ball: (${Math.round(ball.position.x)}, ${Math.round(ball.position.y)}) Vel: (${ball.velocity.x.toFixed(1)}, ${ball.velocity.y.toFixed(1)})`, 10, 185);
358358
         
359
+        // AI rotation info
360
+        if (aiState.rotationMode !== 'NEUTRAL') {
361
+            fill(150, 200, 255, 200);
362
+            text(`AI Rotation: ${aiState.rotationMode} | Target: ${(aiState.targetRotation * 180 / Math.PI).toFixed(1)}°`, 10, 200);
363
+        }
364
+        
359365
         // AI technique indicators
360366
         if (aiState.mode === 'WINDING_UP') {
361367
             fill(255, 150, 50, 200);
362
-            text(`AI WINDING UP | Phase: ${(aiState.windupPhase % (Math.PI * 2)).toFixed(2)} | Velocity: ${aiState.currentVelocity.toFixed(1)}`, 10, 205);
368
+            text(`AI WINDING UP | Phase: ${(aiState.windupPhase % (Math.PI * 2)).toFixed(2)} | Velocity: ${aiState.currentVelocity.toFixed(1)}`, 10, 215);
363369
             
364370
             if (aiState.comboBop) {
365371
                 fill(255, 50, 255, 200);
366
-                text("⚡ COMBO PLANNED!", 10, 220);
372
+                text("⚡ COMBO PLANNED!", 10, 230);
367373
             }
368374
         } else if (aiState.mode === 'SWINGING') {
369375
             fill(255, 50, 50, 200);
370
-            text("AI POWER SWING!", 10, 205);
376
+            text("AI POWER SWING!", 10, 215);
371377
         } else if (aiState.consideringBop) {
372378
             fill(255, 255, 100, 200);
373
-            text("AI PREPARING BOP!", 10, 205);
379
+            text("AI PREPARING BOP!", 10, 215);
374380
         }
375381
         
376382
         if (bopState.right.active) {
377383
             fill(255, 255, 0, 255);
378
-            text("AI BOPPING!", 10, 220);
384
+            text("AI BOPPING!", 10, 230);
379385
         }
380386
     }
381387
 }
js/sprong.jsmodified
@@ -50,6 +50,9 @@ window.inputBuffer = inputBuffer;
5050
 window.moveSupportEnhanced = moveSupportEnhanced;
5151
 window.ball = null;
5252
 window.rotationState = rotationState;
53
+window.rightPaddle = null;
54
+window.width = CANVAS_WIDTH;
55
+window.height = CANVAS_HEIGHT;
5356
 
5457
 function setup() {
5558
     let canvas = createCanvas(CANVAS_WIDTH, CANVAS_HEIGHT);
@@ -80,6 +83,7 @@ function setup() {
8083
     // Create ball
8184
     ball = resetBall(null, world, width, height);
8285
     window.ball = ball;
86
+    window.rightPaddle = rightPaddle;
8387
     
8488
     // Add everything to the world
8589
     World.add(world, [