Search code examples
c++artificial-intelligencesdlgame-physicspong

AI to miss ball randomly ping pong C++ with SDL


This is my first time trying to create a game with C++ using SDL I am trying to create a ping pong game in C++ with SDL Only issue i am facing right now is how to make AI miss the ball randomly so that there is a chance for a player to win. The code to edit would be in update game. i tried doing it with various steps but its not helping. i have added all

#include <iostream>

#include <SDL.h>
#include <random>

using namespace std;

#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600

SDL_Event event; //to handle events
int mouseX, mouseY; //for mouse coordinates

SDL_Renderer* renderer;
bool isRunning = true;


SDL_Rect playerPaddle;
SDL_Rect divider;
SDL_Rect aiPaddle;
SDL_Rect ball;
SDL_Rect background = { 0,0,WINDOW_WIDTH,WINDOW_HEIGHT };

int speed_x, speed_y;
int direction[2] = {-1, 1 };

int aiPaddle_speedY = 0;

bool check_collision(SDL_Rect A, SDL_Rect B)
{
    int leftA, leftB;
    int rightA, rightB;
    int bottomA, bottomB;
    int topA, topB;

    leftA = A.x;
    rightA = A.x + A.w;
    topA = A.y;
    bottomA = A.y + A.h;

    leftB = B.x;
    rightB = B.x + B.w;
    topB = B.y;
    bottomB = B.y + B.h;

    if (bottomA <= topB)
    {
        return false;
    }

    if (topA >= bottomB)
    {
        return false;
    }

    if (rightA <= leftB)
    {
        return false;
    }

    if (leftA >= rightB)
    {
        return false;
    }

    return true;


}



bool InitGameEngine()
{
    SDL_Window* window;
    window = SDL_CreateWindow("Pong Game", SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, 0
    );

    if (!window)
    {
        cout << "WIndow creation failed..\n";
        return false;
    }

    renderer = SDL_CreateRenderer(window, -1, 0);

    if (!renderer)
    {
        cout << "Render initialization failed.\n";
        return false;
    }

    return true;
}



void InitGameWorld()
{
    speed_x = -3;
    speed_y = -3;

    //set divider in between
    divider.x = WINDOW_WIDTH / 2;
   // divider.y = WINDOW_HEIGHT/2;
    divider.h = WINDOW_HEIGHT;
    divider.w = 10;

    playerPaddle.x = 20;
    playerPaddle.h = 100;
    //set it just above half of the window's center
    playerPaddle.y = WINDOW_HEIGHT * 0.5 - (playerPaddle.h * 0.5);
    playerPaddle.w = 20;

    //sets the aiPaddle 40 pixels to the left of the right-most
    //corner of the screen
    aiPaddle.x = WINDOW_WIDTH - 40;
    aiPaddle.h = 100;
    aiPaddle.y = WINDOW_HEIGHT * 0.5 - (aiPaddle.h * 0.5);
    aiPaddle.w = 20;

    ball.w = ball.h = 20;
    //center the ball on the screen
    ball.x = (WINDOW_WIDTH * 0.5) - (ball.w * 0.5);
    ball.y = (WINDOW_HEIGHT * 0.5) - (ball.h * 0.5);

}

void Render()
{
    SDL_RenderClear(renderer); //clear the previously drawn frame
    //Start drawing the current frame
    //Render the background
    //5,30,67 represents a dark blue color
    SDL_SetRenderDrawColor(renderer, 5, 30, 67, 255);
    SDL_RenderFillRect(renderer, &background);

    //Render the player and ai Paddle
    SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);//white
    //set the above white color to both ai & player paddles
    SDL_RenderFillRect(renderer, &playerPaddle);
    SDL_RenderFillRect(renderer, &aiPaddle);
    SDL_RenderFillRect(renderer, &divider);

    //Render the ball
    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); //red
    SDL_RenderFillRect(renderer, &ball);//fill ball's rect with red

    //must call this to render all of the above
    SDL_RenderPresent(renderer);
}

void Input()
{
    while (SDL_PollEvent(&event))
    {
        if (event.type == SDL_MOUSEMOTION)
        {
            SDL_GetMouseState(&mouseX, &mouseY); //get and store x,y mouse states
        }
        //if we click 'X" button to close the window then 
        //SDL_QUIT event type is triggered
        if (event.type == SDL_QUIT)
        {
            isRunning = false;
        }
        //check if a key was pressed
        if (event.type == SDL_KEYDOWN)
        {
            switch (event.key.keysym.sym)
            {
            case SDLK_ESCAPE:
                isRunning = false;
                break;

            }

        }

    }
}


void Quit()
{
    //shutdown SDL
    SDL_Quit();
}

void Update()
{
    //map the playerPaddle's y position to the mouse's Y pos
    playerPaddle.y = mouseY;
    //Our initial demo ball movement... 
    //to be removed and replaced later with some other random movement
    ball.x += speed_x ;
    ball.y += speed_y ;

    if (ball.x < 0 || ball.x > WINDOW_WIDTH) {
        ball.x = WINDOW_WIDTH / 2;
        ball.y = WINDOW_HEIGHT / 2;
        
        speed_x = (rand() % 2 + 3)  * direction[rand() % 2] ;
        speed_y = (rand() % 2 + 3) * direction[rand() % 2] ;

    }

    if (ball.y < 0 || ball.y > (WINDOW_HEIGHT - ball.h)) {
        speed_y = -speed_y;
    }

   // aiPaddle.y = ball.y - aiPaddle.h / 2 + ball.h / -5;
    aiPaddle.y = (rand() % 10) + (ball.y+10) ;

   

    if (check_collision(ball ,aiPaddle)  )
    {
        speed_x = -speed_x+2;
        ball.y += (rand() % 2 - 3) * direction[rand() % 2];

    }

    if (check_collision(ball, playerPaddle) ){
        speed_x = -speed_x+2 ;
        ball.y += (rand() % 2 - 3) * direction[rand() % 2];
    }
    //slow down the game by using SDL_Delay

    SDL_Delay(10);
    //pause the game loop for 10 ms before continuing on to the next frame
    //to play the game in a decent speed
}

int main(int argc, char* argv[])
{
    if (!InitGameEngine()) return -1;
    InitGameWorld();
    //Setup game loop
    while (isRunning)
    {
        Input();
        Update();
        Render();
    }

    Quit();
    return 0;
}

Thankyou for your help already.


Solution

  • It really depends on how you want the opponent to behave two options that come to mind is making the opponent too slow to catch balls that are too far away. Another option is to have "randomness" in the opponents position, so it might miss when hitting the ball.

    In your code, you've written:

    aiPaddle.y = (rand() % 10) + (ball.y+10) ;
    

    Although this adds randomness to the opponent player, it's only a variation around the position of the ball. It would work better if you add the randomness when setting the paddle speed instead. Try some variation of this:

    aiPaddle_speedY = (rand() % k)*(aiPaddle.y - ball.y + rand()% j)
    

    Where k and j is some variable. You could and/or add a max speed for the aiPaddle:

    aiPaddle.y = min(ai_Paddle_speedY, ai_Paddle_max_speed)
    

    Some combination of these two with tweeked variables should work.


    Other tips is that you should avoid using:

    namespace std;
    

    as this can cause collisions between names in your code. Especially since the namespace can be included other files.

    Also instead of using:

    SDL_Delay(10);
    

    you should instead have a clock that sets the "fps". This way the game doesn't speed up or slow down depending on how fast your code runs.

    #include <chrono>
    #include <thread>
    
    void Update() {
        static auto tp = std::chrono::steady_clock::now();
    
        // ... then instead of SDL_Delay(10):
    
        tp += std::chrono::milliseconds(10);
        std::this_thread::sleep_until(tp);
    }