Having problem with this arduino pong game code

I am trying to write this arduino pong game code.I have problem in the ball and paddle collision dectection section.Some times ball gets through the player paddle at an angle.Some times ball starts wiggling at the y direction with the paddle. I am using Arduino nano , 128x64px 0.96inch monochrome Oled display, two push button for the player paddle. And the cpu paddle is controlled by the code.
Wokwi project link
Here is the code:

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>


//DEFINE UP AND DOWN BUTTON FOR PLAYER PADDLE
#define UP_BUTTON 2
#define DOWN_BUTTON 3

//DEFINE PADDLE SPEED AND PADDLE RATE FOR PLAYER PADDLE
const unsigned long PADDLE_RATE  = 5;
const uint8_t       PADDLE_SPEED = 2;
//DEFINE PLAYER PADDLE STARTING POSITION(X,Y) AND WIDTH AND HEIGHT (X+W,Y+H)
const uint8_t		PLAYER_X	 = 7;
uint8_t				PLAYER_Y	 = 10;
const uint8_t		PLAYER_W	 = 3;
const uint8_t		PLAYER_H	 = 15;
//DEFINE BALL WIDTH/RADIUS , BALL(X,Y), AND BALL DIRECTIONAL SPEED

int 				BALLX 		 = 64;
int 				BALLY 		 = 32;
const uint8_t		BALL_W		 = 2;
int 				BALLDX		 = 1;
int 				BALLDY		 = 1;

//DEFINE CPU PADDLE STARTING POSITION(X,Y) AND WIDTH AND HEIGHT(X+W,Y+H)
const uint8_t 		CPU_X		 = 120;
uint8_t				CPU_Y		 = 10;
const uint8_t		CPU_W 		 = 3;
const uint8_t		CPU_H		 = 15;

// DEFINE PLAYER AND CPU SCORE

int 				PLAYERSCORE  = 0;
int 				CPUSCORE 	 = 0;

//DEFINE SCREEN HEIGHT AND WIDTH
const uint8_t		SCREEN_H	 = 64;
const uint8_t		SCREEN_W	 = 128;
const int			DISPLAY_RST  = -1;

//INITIALIZE DISPLAY

Adafruit_SSD1306 display(SCREEN_W,SCREEN_H,&Wire,DISPLAY_RST);

//DRAWING SCORE FUNCTION 
void drawScore(int PLAYERSCORE,int CPUSCORE);
long randNumber;

void setup()
{
	display.begin(SSD1306_SWITCHCAPVCC,0x3C);
	display.display();
    Serial.begin(115200);
	pinMode(0, INPUT);
	unsigned long start = millis();

	pinMode(UP_BUTTON,	INPUT_PULLUP);
	pinMode(DOWN_BUTTON,INPUT_PULLUP);
	randomSeed(analogRead(0));

	while(millis() - start < 2000);
	display.clearDisplay();
}

void loop()
{	

    float random_num = random(6);
    Serial.println(random_num);
	display.clearDisplay();
	//READING UP AND DOWN BUTTON FOR PLAYER PADDLE
	int UP_STATE 	= digitalRead(UP_BUTTON);
	int DOWN_STATE  = digitalRead(DOWN_BUTTON);
	//DRAW RECTANGLE AROUND THE SCREEN
	display.drawRect(0,0,128,64,WHITE);
	
	
	if(DOWN_STATE == LOW && (PLAYER_Y+PLAYER_H) < 62)
	{
		PLAYER_Y = PLAYER_Y + PADDLE_RATE;
	}
	else
	{
		PLAYER_Y = PLAYER_Y;
	}
	
	if(UP_STATE == LOW && PLAYER_Y > 2)
	{
		PLAYER_Y = PLAYER_Y - PADDLE_RATE;
	}
	else
	{
		PLAYER_Y = PLAYER_Y;
	}
	
	
	

    // IF THE RANDOM NUMBER IS LESS THAN 0.5, MOVE THE CPU PADDLE TOWARDS THE BALL'S VERTICAL POSITION
    if (random_num < 2.5) {
    if(BALLY > (CPU_Y+CPU_H/2)) {
        if((CPU_Y+CPU_H) < 62) {
        CPU_Y += PADDLE_SPEED;
        }
    }
    else if(BALLY < (CPU_Y + CPU_H/2)) {
        if(CPU_Y > 2) {
        CPU_Y -= PADDLE_SPEED;
        }
    }
    }
    // IF THE RANDOM NUMBER IS GREATER THAN OR EQUAL TO 0.5, DON'T MOVE THE CPU PADDLE
    else {
    CPU_Y = CPU_Y;
    }

		
	
	if(BALLY <= BALL_W || BALLY >= (64-BALL_W))
	{
		BALLDY = BALLDY * (-1);
	}
	if(BALLX <= BALL_W)
	{
		CPUSCORE 	= CPUSCORE + 1;
		BALLX  		= SCREEN_W/2;
		BALLY  		= SCREEN_H/2;
		BALLDX		= 1;
		BALLDY		= 1;
	}
	
	if(BALLX >= (128-BALL_W))
	{
		PLAYERSCORE = PLAYERSCORE + 1;
		BALLX		= 64;
		BALLY		= 32;
		BALLDX		= -1;
		BALLDY		= 1;
	}
	if (BALLX == PLAYER_X+BALL_W+PLAYER_W && BALLY >= PLAYER_Y && BALLY <= PLAYER_Y+PLAYER_H+BALL_W) {
     BALLDX = -BALLDX;

    }

    if (BALLX == CPU_X-BALL_W && BALLY >= CPU_Y && BALLY <= CPU_Y+CPU_H+BALL_W)
    {
 
    BALLDX = -BALLDX;

    }

	BALLX = BALLX + BALLDX;
	BALLY = BALLY + BALLDY;
	
	display.drawRect(PLAYER_X,PLAYER_Y,PLAYER_W,PLAYER_H,WHITE);
	display.drawRect(CPU_X,CPU_Y,CPU_W,CPU_H,WHITE);
	display.drawCircle(BALLX,BALLY,BALL_W,WHITE);
	drawScore(PLAYERSCORE,CPUSCORE);
	display.display();
	delay(10);
}

void drawScore(int PLAYERSCORE, int CPUSCORE) {
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(2, 2);
    display.print(PLAYERSCORE);
    display.setCursor(122, 2);
    display.print(CPUSCORE);
}

Nice work, thanks for sharing the wokwi. I'm looking through a small window at this, so I can't pinpoint your logical errors. I do see what you are talking about.

I can only suggest adding serial printing strategically to see the values of some of the variables and demonstrate the flow of your code.

If you wrote all that code, you should be able to fix the errors. You can trust that it is only doing what you wrote. When I am at the big rig I will take a better look, of course by then the problem(s) will have been found…

One thing I would change is… don't adjust the CPU paddle when the ball is moving towards the human. Anyone knows the CPU can cheat like a madman, but seeing it calmly tracking the ball even when it wouldn't need to is very annoying!

I didn't notice either that the CPU ever missed. Perhaps you plan to have a number of preconceived failure modes for letting the CPU fail from time to time, like overshooting or moving too slow or whatever.

L8R

a7

Ok, getting the CPU to freeze is easy, I used (BALLDX == 1) as a condition for moving the CPU paddle.

I changed the random_num to be an integer 0..999 and changed your test of that. Moving the CPU only 1/5 of the time (random_num < 200) seems to make it miss enough. But that can be tuned, the trouble with floating a random int is that you only get 0, 1, 2 &c. so fine tuning is hard(er).

This code (several places)

	else
	{
		PLAYER_Y = PLAYER_Y;
	}	

is unecessary. I am in favor of removing any code that not needed. Less to read, less to wonder about. The compiler probably tosses all that anyway, as it know that the assignment of a variable to that same variable is the same as not doing it.

I am still not looking for or finding the flaws. Seems like your various widths are meaning the ball cannot escape the condition that makes BALLDX invert.

I suggest you only allow BALLDX to take on the value that makes it move left when it is at the far right, so it won't matter if it gets told to do that many times, and similarly only allow it to take on the value that makes it move right when it is at the far left.

Ok, that was easier than I thought:

  if (BALLX <= PLAYER_X + BALL_W + PLAYER_W && BALLY >= PLAYER_Y && BALLY <= PLAYER_Y + PLAYER_H + BALL_W) {
    Serial.print("changing BALLDX 1"); Serial.print(" ");
    Serial.println("");

    BALLDX = 1; // not toggle, just make it go the other way.
  }

  if (BALLX >= CPU_X - BALL_W && BALLY >= CPU_Y && BALLY <= CPU_Y + CPU_H + BALL_W)
  {
    Serial.print("changing BALLDX 2"); Serial.print(" ");
    Serial.println("");

    BALLDX = -1;
  }

If walls are giving you trouble, pull the same trick there. At the top, only allow the ball to take on the value that makes it move down and so forth.

BTW, it never hurts to use the IDE Tools/Auto Format on your code. It makes it easier for everyone to read, and can often help spot logical errors like mismatched or missing { braces }.

HTH

a7

1 Like

Very very thanks for your help.I was using BALLDX = -BALLDX on my code which was creating this problem.I also modified the cpu paddle code to be more random.Here is the code

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>


//DEFINE UP AND DOWN BUTTON FOR PLAYER PADDLE
#define UP_BUTTON 2
#define DOWN_BUTTON 3
int previousballx = 0;
//DEFINE PADDLE SPEED AND PADDLE RATE FOR PLAYER PADDLE
const unsigned long PADDLE_RATE  = 5;
const uint8_t       PADDLE_SPEED = 2;
//DEFINE PLAYER PADDLE STARTING POSITION(X,Y) AND WIDTH AND HEIGHT (X+W,Y+H)
const uint8_t		PLAYER_X	 = 7;
uint8_t				PLAYER_Y	 = 10;
const uint8_t		PLAYER_W	 = 3;
const uint8_t		PLAYER_H	 = 15;
//DEFINE BALL WIDTH/RADIUS , BALL(X,Y), AND BALL DIRECTIONAL SPEED

int 				BALLX 		 = 64;
int 				BALLY 		 = 32;
const uint8_t		BALL_W		 = 2;
int 				BALLDX		 = 1;
int 				BALLDY		 = 1;

//DEFINE CPU PADDLE STARTING POSITION(X,Y) AND WIDTH AND HEIGHT(X+W,Y+H)
const uint8_t 		CPU_X		 = 120;
uint8_t				CPU_Y		 = 10;
const uint8_t		CPU_W 		 = 3;
const uint8_t		CPU_H		 = 15;

// DEFINE PLAYER AND CPU SCORE

int 				PLAYERSCORE  = 0;
int 				CPUSCORE 	 = 0;

//DEFINE SCREEN HEIGHT AND WIDTH
const uint8_t		SCREEN_H	 = 64;
const uint8_t		SCREEN_W	 = 128;
const int			DISPLAY_RST  = -1;

//INITIALIZE DISPLAY

Adafruit_SSD1306 display(SCREEN_W,SCREEN_H,&Wire,DISPLAY_RST);

//DRAWING SCORE FUNCTION 
void drawScore(int PLAYERSCORE,int CPUSCORE);
long randNumber;

void setup()
{
	display.begin(SSD1306_SWITCHCAPVCC,0x3C);
	display.display();
    Serial.begin(115200);
	pinMode(0, INPUT);
	unsigned long start = millis();

	pinMode(UP_BUTTON,	INPUT_PULLUP);
	pinMode(DOWN_BUTTON,INPUT_PULLUP);
	randomSeed(analogRead(0));

	while(millis() - start < 2000);
	display.clearDisplay();
}

void loop()
{	

    float random_num = random(10);
	display.clearDisplay();
	//READING UP AND DOWN BUTTON FOR PLAYER PADDLE
	int UP_STATE 	= digitalRead(UP_BUTTON);
	int DOWN_STATE  = digitalRead(DOWN_BUTTON);
	//DRAW RECTANGLE AROUND THE SCREEN
	display.drawRect(0,0,128,64,WHITE);
	
	
	if(DOWN_STATE == LOW && (PLAYER_Y+PLAYER_H) < 62)
	{
		PLAYER_Y = PLAYER_Y + PADDLE_RATE;
	}
	else
	{
		PLAYER_Y = PLAYER_Y;
	}
	
	if(UP_STATE == LOW && PLAYER_Y > 2)
	{
		PLAYER_Y = PLAYER_Y - PADDLE_RATE;
	}
	
    if (random_num < 2) {
				if(previousballx < BALLX && previousballx > 64)
				{
    if(BALLY > (CPU_Y+CPU_H/2)) {
        if((CPU_Y+CPU_H) < 62) {
        CPU_Y += PADDLE_SPEED;
        }
    }
    else if(BALLY < (CPU_Y + CPU_H/2)) {
        if(CPU_Y > 2) {
        CPU_Y -= PADDLE_SPEED;
        }
    }
			}
			else{
				CPU_Y = CPU_Y;
			}
    }
    // IF THE RANDOM NUMBER IS GREATER THAN OR EQUAL TO 0.5, DON'T MOVE THE CPU PADDLE
    else {
    CPU_Y = CPU_Y;
    }

		previousballx = BALLX;

		
	
	if(BALLY <= BALL_W || BALLY >= (64-BALL_W))
	{
		BALLDY = BALLDY * (-1);
	}
	if(BALLX <= BALL_W)
	{
		CPUSCORE 	= CPUSCORE + 1;
		BALLX  		= SCREEN_W/2;
		BALLY  		= SCREEN_H/2;
		BALLDX		= 1;
		BALLDY		= 1;
	}
	
	if(BALLX >= (128-BALL_W))
	{
		PLAYERSCORE = PLAYERSCORE + 1;
		BALLX		= 64;
		BALLY		= 32;
		BALLDX		= -1;
		BALLDY		= 1;
	}
	
	if (BALLX <= PLAYER_X + BALL_W + PLAYER_W && BALLY >= PLAYER_Y && BALLY <= PLAYER_Y + PLAYER_H + BALL_W) {
    Serial.print("changing BALLDX 1"); Serial.print(" ");
    Serial.println("");

    BALLDX = 1; // not toggle, just make it go the other way.
  }

  if (BALLX >= CPU_X - BALL_W && BALLY >= CPU_Y && BALLY <= CPU_Y + CPU_H + BALL_W)
  {
    Serial.print("changing BALLDX 2"); Serial.print(" ");
    Serial.println("");

    BALLDX = -1;
  }

	BALLX = BALLX + BALLDX;
	BALLY = BALLY + BALLDY;
	
	display.drawRect(PLAYER_X,PLAYER_Y,PLAYER_W,PLAYER_H,WHITE);
	display.drawRect(CPU_X,CPU_Y,CPU_W,CPU_H,WHITE);
	display.drawCircle(BALLX,BALLY,BALL_W,WHITE);
	drawScore(PLAYERSCORE,CPUSCORE);
	display.display();
	delay(10);
}

void drawScore(int PLAYERSCORE, int CPUSCORE) {
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(2, 2);
    display.print(PLAYERSCORE);
    display.setCursor(122, 2);
    display.print(CPUSCORE);
}

This is my first time posting in any community. And yes all of the code I didn't wrote, I took help from the code base available for arduino pong game.Some I also tried to copy but that I didn't work well.So I started to write this code with taking something from here and there.I still need to sort some problem like sound and showing score properly.Also need to build it physically.

I'll keep pointing this out if you leave it in you code:

    else {
    CPU_Y = CPU_Y;
    }

Unless you feel you really need to be reminded that the else case is doing nothing.

I played the new code and it seems to work well.

When you add features, you may well solve another thing I had to adjust - there should be a good pause in the action before the ball is "served" again after a point is scored.

I wouldn't have bothered, but it was not always clear what was happening, it serves so fast. Was it a bad bounce or what?

The CPU opponent looks a bit dumb now. The paddle moves in no relation to the approaching ball.

Lastly (for now!) I suggest you consider making the BALLDY be random up or down after a paddle hit, perhaps even based on if/how the paddle it hit was moving.

Please post your progress. If you continue to use the wokwi, we can continue to plunk your code in there and play with it.

HTH

a7

A departure from the traditional Pong while still keeping the skill requirement:

For the ProMicro 32U4

/*Binary sketch size: 13,766 bytes (of a 28,672 byte maximum)

  This example code is in the public domain.
  Based upon:http://arduino.cc/en/Tutorial/TFTPong
 
 
  Created by Tom Igoe December 2012
  Modified 15 April 2013 by Scott Fitzgerald
  Modified 15 June 2014 by M. Ray Burnette for ILI9340 TFT & X/Y joystick
 
  Joystick used: http://www.ebay.com/itm/New-JoyStick-Breakout-Module-Shield-PS2-Joystick-Game-Controller-For-Arduino-/200955114766
  LCD TFT 2.2":  http://www.ebay.com/itm/2-2-inch-2-2-SPI-TFT-LCD-Display-module-240x320-ILI9341-51-AVR-STM32-ARM-PIC-/200939222521
  LCD Note:      Exact version used is an older version of the above, ILI9340.

  Color definitions for TFT SPI 2.2" Display:
                    ILI9340_BLACK   0x0000
                    ILI9340_BLUE    0x001F
                    ILI9340_RED     0xF800
                    ILI9340_GREEN   0x07E0
                    ILI9340_CYAN    0x07FF
                    ILI9340_MAGENTA 0xF81F
                    ILI9340_YELLOW  0xFFE0  
                    ILI9340_WHITE   0xFFFF
*/

#include "Adafruit_ILI9340.h"
#include "Adafruit_GFX.h"
#include <SPI.h>

// SPI pins Sparkfun Pro Micro 32U4
#define mosi 16
#define miso 14
#define sclk 15
#define cs   10
#define dc    9
#define rst   8

void(* resetFunc) (void) = 0; //declare reset function @ address 0

// Use hardware SPI
Adafruit_ILI9340 TFT = Adafruit_ILI9340(cs, dc, rst);

// variables for the position of the ball and paddle
int paddleX ;
int paddleY ;
int oldPaddleX ;
int oldPaddleY ;
int ballDirectionX = 1;
int ballDirectionY = 1;
int ballSpeed = 10; // lower numbers are faster
int ballX, ballY, oldBallX, oldBallY;
// screen stuff used to be private now global
int myWidth ;
int myHeight ;
boolean OneTime ;
char Arduino = 57 ;
char Player = 57 ;


void setup() 
{
  pinMode( 2, INPUT) ; digitalWrite( 2, HIGH);  // turn on pullup resistor
  // initialize the display
  TFT.begin();
  myWidth   = TFT.width() ;
  myHeight  = TFT.height();
  TFT.fillScreen(ILI9340_BLACK);  // clear display
  TFT.setRotation(3);             // landscape
  // black background
  TFT.setTextColor(0xF9B0, ILI9340_BLACK) ;
  TFT.setTextSize(1);  // Small 53 char / line 
  TFT.print("A game by Ray Burnette") ;
  TFT.setTextSize(2);     // Small 26 char / line 
  TFT.setCursor(40, 40) ; TFT.print("Blazing Paddle") ;
  TFT.setTextColor(ILI9340_YELLOW, ILI9340_BLACK) ;
  TFT.setCursor(0, 59); TFT.print("<--FASTER-+-SLOWER-->") ;
  TFT.setCursor(0, 174); TFT.print(" Left or Right joystick   ") ;
  TFT.setCursor(0, 195); TFT.print(" to adjust ball speed.    ") ;
  TFT.setCursor(0, 220); TFT.print("Press down to continue...") ;
  paddleX = oldPaddleX ;
  paddleY = oldPaddleY =  myHeight / 3 ;
  while( UserInput() )
  {
      //        map(         value, fromLow, fromHigh, toLow, toHigh)
      paddleX = map(analogRead(A0), 0,         1023,   0,   myWidth) ; 
      if ( oldPaddleX != paddleX )
        {
          TFT.fillRect(oldPaddleX, oldPaddleY, 20, 5, ILI9340_BLACK) ;
        }
      TFT.fillRect(paddleX, paddleY, 20, 5, ILI9340_WHITE);
      oldPaddleX = paddleX;
  }
  randomSeed(analogRead(0));  // X axis value
  TFT.begin();
  myWidth   = TFT.width() - 85 ; // smaller court for score keeping
  // myWidth   = TFT.width() ;  // full-screen court
  myHeight  = TFT.height();
  TFT.setRotation(3);             // landscape
  TFT.fillScreen(ILI9340_BLACK);  // clear display
  ballX = random(20, 220) ;
  ballY = random( 5, 235) ;
  // partition court for score keeping to right side of screen
  //  drawLine(             x0, y0,           x1,       y1, uint16_t color);
  TFT.drawLine(myWidth + 20,  0, myWidth + 20, myHeight, ILI9340_CYAN ) ;
  // drawChar(uint16_t x, uint16_t y, char c, uint16_t color, uint16_t bg, uint8_t size);
  TFT.drawChar(myWidth + 30, 55, 'M', ILI9340_CYAN, ILI9340_BLACK, 2) ;
  TFT.drawChar(myWidth + 45, 55, 'e', ILI9340_CYAN, ILI9340_BLACK, 2) ;
  TFT.drawChar(myWidth + 30, 128, 'Y', ILI9340_YELLOW, ILI9340_BLACK, 2) ;
  TFT.drawChar(myWidth + 45, 128, 'o', ILI9340_YELLOW, ILI9340_BLACK, 2) ;
  TFT.drawChar(myWidth + 60, 128, 'u', ILI9340_YELLOW, ILI9340_BLACK, 2) ;
}


void loop()
{

  // map the paddle's location to the position of the potentiometers
  //        map(         value, fromLow, fromHigh, toLow,   toHigh)
  paddleX = map(analogRead(A0),       0,     1020,     1,   myWidth - 20)  ; //- 20/2; 
  paddleY = map(analogRead(A1),       0,     1020,     5,   myHeight - 5) ; //-  5/2; 

  // set the fill color to black and erase the previous 
  // position of the paddle if different from present
  if ( (oldPaddleX != paddleX) || (oldPaddleY != paddleY) )
  {
    TFT.fillRect(oldPaddleX, oldPaddleY, 20, 5, ILI9340_BLACK) ;
  }

  // draw the paddle on screen, save the current position
  TFT.fillRect(paddleX, paddleY, 20, 5, ILI9340_WHITE);
  oldPaddleX = paddleX;
  oldPaddleY = paddleY;

  // update the ball's position and draw it on screen
  if (millis() % ballSpeed < 2)
  {
    moveBall();
  }
  
  if ( Scoring(Arduino, Player) )  // either score has gone to 0
  {
      TFT.setTextColor(ILI9340_GREEN, ILI9340_BLACK) ;
      TFT.setTextSize(3);
      TFT.setCursor(5,  30) ; TFT.print("  END OF GAME") ;
      TFT.setCursor(5, 180) ; TFT.print("PRESS JOYSTICK") ;
      TFT.setCursor(5, 100) ;
   
      if (Arduino == '0')
      {
        TFT.print("   YOU WIN !") ;
      } 
        else
      {
        TFT.print("  I WIN !!!") ;
      }

      while( UserInput() ) {}
      resetFunc();  //call reset function to reboot Arduino code
  }
}


void moveBall()    // this function determines the ball's position on screen
{
  OneTime = true ;
  // if the ball goes offscreen, reverse the direction:
 if (ballX > myWidth || ballX < 1 )
 {
   ballDirectionX = -ballDirectionX;
 }
 
  if (ballY >= TFT.height() || ballY < 1)
  {
   ballDirectionY = -ballDirectionY;
     if( OneTime ) {
       Player-- ;  // deduct 1 from player
       OneTime = false ;
     }
   }

  // check if the ball and the paddle occupy the same space on screen
  if (inPaddle(ballX, ballY, paddleX, paddleY, 20, 5))
  {
    ballDirectionX = -ballDirectionX;
    ballDirectionY = -ballDirectionY;
    if (OneTime ) {
      Arduino-- ;  // deduct 1 from Arduino
      OneTime = false ;
    }
  }
 
  // update the ball's position
  ballX += ballDirectionX;
  ballY += ballDirectionY;
 
 // erase the ball's previous position
  if (oldBallX != ballX || oldBallY != ballY)
  {
    TFT.fillRect(oldBallX, oldBallY, 7, 7, ILI9340_BLACK) ; // ball orig 5*5 made larger to see on denser TFT
  }

  // draw the ball's current position
  //  fillRect(   x0,    y0, w, h, uint16_t color);
  TFT.fillRect(ballX, ballY, 7, 7, ILI9340_GREEN) ;

  oldBallX = ballX ;
  oldBallY = ballY ;
}

// this function checks the position of the ball to see if it intersects with the paddle
boolean inPaddle(int x, int y, int rectX, int rectY, int rectWidth, int rectHeight)
{
  boolean result = false;

  if ((x >= rectX && x <= (rectX + rectWidth)) && 
      (y >= rectY && y <= (rectY + rectHeight)))
      {
       result = true;
      }
 
  return result;  
}


boolean UserInput()
{
  // map         (         value, fromLow, fromHigh, toLow, toHigh)
  ballSpeed = map(analogRead(A0),       0,     1023,     1,     20) ;
  return digitalRead(2);
}


boolean Scoring(char Arduino, char Player)
{
  boolean endGame = false ;
  static char previousArduino, previousPlayer ;
  uint16_t x  = 270 ;
  uint16_t y  =  75 ;
  uint16_t y1 = 150 ;

  if( Arduino == 48 || Player == 48) endGame = true ;  // char 48 == int 0
  
  if ( Arduino != previousArduino || Player != previousPlayer )
  {
   // drawChar(uint16_t x, uint16_t y, char c, uint16_t color, uint16_t bg, uint8_t size);
  TFT.drawChar(x, y,  Arduino, ILI9340_CYAN, ILI9340_BLACK, 3);
  TFT.drawChar(x, y1, Player,  ILI9340_YELLOW, ILI9340_BLACK, 3);
  previousArduino = Arduino ;
  previousPlayer  = Player ;
  }

  return endGame ;
}



Notes/etc:

/*
// These are the pins used for the UNO

              UNO LEO  // J2 header pin # on GLCD
#define _sclk 13   15  // 7
#define _miso 12   14  // 9
#define _mosi 11   16  // 6
#define _cs   10   10  // 3
#define _dc    9    9  // 5
#define _rst   8    8  // 4



                                      +-\/-+
Reset                           PC6  1|    |28  PC5 (AI 5)
Rx                        (D 0) PD0  2|    |27  PC4 (AI 4)
Tx                        (D 1) PD1  3|    |26  PC3 (AI 3)
                          (D 2) PD2  4|    |25  PC2 (AI 2)
                     PWM+ (D 3) PD3  5|    |24  PC1 (AI 1)
lDebug Low=true           (D 4) PD4  6|    |23  PC0 (AI 0)
+5                              VCC  7|    |22  GND             GND
GND                             GND  8|    |21  AREF            +5 and bypass cap
XTL1                            PB6  9|    |20  AVCC            +5
XTL2                            PB7 10|    |19  PB5 (D 13)      J2#7/SCK  -----------> D15 32U4
                          (D 5) PD5 11|    |18  PB4 (D 12)      J2#9/MISO -----------> D14 32U4
BAUD (low=4800)      PWM+ (D 6) PD6 12|    |17  PB3 (D 11) PWM  J2#6/MOSI (SDI) -----> D16 32U4
                          (D 7) PD7 13|    |16  PB2 (D 10) PWM  J2#3/CS
J2#4/Reset                (D 8) PB0 14|    |15  PB1 (D  9) PWM  J2#5/DC
                                      +----+

/--------------------------------------------------------------------/ 
/             Color definitions for TFT SPI 2.2" Display             /
/                    ILI9340_BLACK   0x0000                          /
/                    ILI9340_BLUE    0x001F                          /
/                    ILI9340_RED     0xF800                          /
/                    ILI9340_GREEN   0x07E0                          /
/                    ILI9340_CYAN    0x07FF                          /
/                    ILI9340_MAGENTA 0xF81F                          /
/                    ILI9340_YELLOW  0xFFE0                          /
/                    ILI9340_WHITE   0xFFFF                          /
/--------------------------------------------------------------------/ 

0        1         2         =  26 x 15 =   390 char/display    
12345678901234567890123456   = 240 x 320 (landscape) = 76,800 pixels
___display characters_____/ line#  character#
12345678901234567890123456    1     1 -  26
12345678901234567890123456    2    27 -  52
12345678901234567890123456    3    53 -  78
12345678901234567890123456    4    79 - 104
12345678901234567890123456    5   105 - 130
12345678901234567890123456    6   131 - 156
12345678901234567890123456    7   157 - 182
12345678901234567890123456    8   183 - 208
12345678901234567890123456    9   209 - 234
12345678901234567890123456    10  235 - 260
12345678901234567890123456    11  261 - 286
12345678901234567890123456    12  287 - 312
12345678901234567890123456    13  313 - 338
12345678901234567890123456    14  339 - 364
12345678901234567890123456    15  365 - 390
--------------------------/

    //tft.drawLine( x1,  y1,  x2,   y2, color);
    lcd.drawLine(    0,  0,  319,    0, ILI9340_YELLOW);
    lcd.drawLine(    0, 15,  319,   15, ILI9340_YELLOW);
    lcd.drawLine(    0,  0,    0,  239, ILI9340_YELLOW);
    lcd.drawLine(   11,  0,   11,  239, ILI9340_YELLOW);
    //lcd.setTextColor(ILI9340_WHITE, ILI9340_BLACK) ;
    
    (character# % 26 ) * 16 == (000,yyy)
    
    ***** Graphic text appears to be 12w x 16h font for .setTextSize(2) ***** Landscape == 15 Lines x 26 Characters Wide  == 390 Characters per Screen
___________________________________/1st/_______________________________________________________     ___________________________________/26th/_______________________________________________________
_______/_______/_______/_______/_______/_______/_______/_______/_______/_______/_______/_______/    _______/_______/_______/_______/_______/_______/_______/_______/_______/_______/_______/_______/
000,000 001,000 002,000 003,000 004,000 005,000 006,000 007,000 008,000 009,000 010,000 011,000     308,000 309,000 310,000 311,000 312,000 313,000 314,000 315,000 316,000 317,000 318,000 319,000 
000,001 001,001 001,002 001,003 001,004 001,005 001,006 001,007 001,008 001,009 001,010 001,011     308,001 309,001
000,002                                                                                             308,002
000,003                                                                                             308,003
000,004                                                                                             308,004
000,005                                                                                             308,005
000,006                                                                                             308,006
000,007                                                                                             308,007
000,008                                                                                             308,008
000,009                                                                                             308,009
000,010                                                                                             308,010
000,011                                                                                             308,011
000,012                                                                                             308,012
000,013                                                                                             308,013
000,014                                                                                             308,014
000,015 001,015 002,015 003,015 004,015 005,015 006,015 007,015 008,015 009,015 010,015 011,015     308,015 309,015 310,015 311,015 312,015 313,015 314,015 315,015 316,015 317,015 318,015 319,015

000,016  .line 2    000,080  .line 6    000,144  .line 10    000,208  .line 14
000,032  .line 3    000,096  .line 7    000,160  .line 11    000,224  .line 15
000,048  .line 4    000,112  .line 8    000,176  .line 12    000,240 --- > 000,000
000,064  .line 5    000,128  .line 9    000,192  .line 13

___________________________________/365th/_____________________________________________________     ___________________________________/390th/_____________________________________________________
_______/_______/_______/_______/_______/_______/_______/_______/_______/_______/_______/_______/    _______/_______/_______/_______/_______/_______/_______/_______/_______/_______/_______/_______/
000,224 001,224 002,224 003,224 004,224 005,224 006,224 007,224 008,224 009,224 010,224 011,224     308,224 309,224 310,224 311,224 312,224 313,224 314,224 315,224 316,224 317,224 318,224 319,224
000,225                                                                                             308,225
000,226                                                                                             308,226
000,227                                                                                             308,227
000,228                                                                                             308,228
000,229                                                                                             308,229
000,230                                                                                             308,230
000,231                                                                                             308,231
000,232                                                                                             308,232
000,233                                                                                             308,233
000,234                                                                                             308,234
000,235                                                                                             308,235
000,236                                                                                             308,236
000,237                                                                                             308,237
000,238                                                                                             308,238
000,239 001,239 002,239 003,239 004,239 005,239 006,239 007,239 008,239 009,239 010,239 011,239     308,239 309,239 310,239 311,239 312,239 313,239 314,239 315,239 316,239 317,239 318,239 319,239

 */



Still there is a problem when player paddle collide with the ball.Some time the ball bounce 2 or three times.

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>


//DEFINE UP AND DOWN BUTTON FOR PLAYER PADDLE
#define UP_BUTTON 2
#define DOWN_BUTTON 3
int previousballx = 0;
//DEFINE PADDLE SPEED AND PADDLE RATE FOR PLAYER PADDLE
const unsigned long PADDLE_RATE  = 5;
const uint8_t       PADDLE_SPEED = 2;
//DEFINE PLAYER PADDLE STARTING POSITION(X,Y) AND WIDTH AND HEIGHT (X+W,Y+H)
const uint8_t		PLAYER_X	 = 7;
uint8_t				PLAYER_Y	 = 10;
const uint8_t		PLAYER_W	 = 3;
const uint8_t		PLAYER_H	 = 15;
//DEFINE BALL WIDTH/RADIUS , BALL(X,Y), AND BALL DIRECTIONAL SPEED

int 				BALLX 		 = 64;
int 				BALLY 		 = 32;
const uint8_t		BALL_W		 = 2;
int 				BALLDX		 = 1;
int 				BALLDY		 = 1;

//DEFINE CPU PADDLE STARTING POSITION(X,Y) AND WIDTH AND HEIGHT(X+W,Y+H)
const uint8_t 		CPU_X		 = 120;
uint8_t				    CPU_Y		 = 10;
const uint8_t	  	CPU_W 	 = 3;
const uint8_t		 CPU_H		 = 15;

// DEFINE PLAYER AND CPU SCORE

int 				PLAYERSCORE  = 0;
int 				CPUSCORE 	 = 0;

//DEFINE SCREEN HEIGHT AND WIDTH
const uint8_t		SCREEN_H	 = 64;
const uint8_t		SCREEN_W	 = 128;
const int			DISPLAY_RST  = -1;

//INITIALIZE DISPLAY

Adafruit_SSD1306 display(SCREEN_W,SCREEN_H,&Wire,DISPLAY_RST);

//DRAWING SCORE FUNCTION 
void drawScore(int PLAYERSCORE,int CPUSCORE);
long randNumber;

void setup()
{
	display.begin(SSD1306_SWITCHCAPVCC,0x3C);
	display.display();
    Serial.begin(115200);
	pinMode(0, INPUT);
	unsigned long start = millis();

	pinMode(UP_BUTTON,	INPUT_PULLUP);
	pinMode(DOWN_BUTTON,INPUT_PULLUP);
	pinMode(5, OUTPUT);
	
	randomSeed(analogRead(0));

	while(millis() - start < 2000);
	display.clearDisplay();
}

void loop()
{	

  float random_num = random(7);
	display.clearDisplay();
	//READING UP AND DOWN BUTTON FOR PLAYER PADDLE
	int UP_STATE 	= digitalRead(UP_BUTTON);
	int DOWN_STATE  = digitalRead(DOWN_BUTTON);
	//DRAW RECTANGLE AROUND THE SCREEN
	display.drawRect(0,0,128,64,WHITE);
	
	
	if(DOWN_STATE == LOW && (PLAYER_Y+PLAYER_H) < 62)
	{
		PLAYER_Y = PLAYER_Y + PADDLE_RATE;
	}

	
	if(UP_STATE == LOW && PLAYER_Y > 2)
	{
		PLAYER_Y = PLAYER_Y - PADDLE_RATE;
	}
	
    if (random_num < 4) {
				if(previousballx < BALLX && previousballx > 64)
				{
    if(BALLY > (CPU_Y+CPU_H/2)) {
        if((CPU_Y+CPU_H) < 62) {
        CPU_Y += PADDLE_SPEED;
        }
    }
    else if(BALLY < (CPU_Y + CPU_H/2)) {
        if(CPU_Y > 2) {
        CPU_Y -= PADDLE_SPEED;
        }
    }
			}
			
    }
    // IF THE RANDOM NUMBER IS GREATER THAN OR EQUAL TO 0.5, DON'T MOVE THE CPU PADDLE

		previousballx = BALLX;

		
	
	if(BALLY <= BALL_W || BALLY >= (64-BALL_W))
	{
		BALLDY = BALLDY * (-1);
		soundBouncec();

	}
	if(BALLX <= BALL_W)
	{
		CPUSCORE 	= CPUSCORE + 1;
		soundPoint();
		delay(50);
		BALLX  		= SCREEN_W/2;
		BALLY  		= SCREEN_H/2;
		BALLDX		= 1;
		BALLDY		= 1;
	}
	
	if(BALLX >= (128-BALL_W))
	{
		PLAYERSCORE = PLAYERSCORE + 1;
		soundPoint();
		delay(50);
		BALLX		= 64;
		BALLY		= 32;
		BALLDX		= -1;
		BALLDY		= 1;
	}
	
	if (BALLX <= PLAYER_X + BALL_W + PLAYER_W && BALLY >= PLAYER_Y && BALLY <= PLAYER_Y + PLAYER_H + BALL_W) {
    Serial.print("changing BALLDX 1"); Serial.print(" ");
    Serial.println("");

    BALLDX = 1; // not toggle, just make it go the other way.
		soundBounce();
  }

  if (BALLX >= CPU_X - BALL_W && BALLY >= CPU_Y && BALLY <= CPU_Y + CPU_H + BALL_W)
  {
    Serial.print("changing BALLDX 2"); Serial.print(" ");
    Serial.println("");

    BALLDX = -1;
		soundBounce();
  }

	BALLX = BALLX + BALLDX;
	BALLY = BALLY + BALLDY;
	
	display.drawRect(PLAYER_X,PLAYER_Y,PLAYER_W,PLAYER_H,WHITE);
	display.drawRect(CPU_X,CPU_Y,CPU_W,CPU_H,WHITE);
	display.drawCircle(BALLX,BALLY,BALL_W,WHITE);
	for (int i=0; i<SCREEN_H; i+=4) {
    display.drawFastVLine(SCREEN_W/2, i, 2, WHITE);
  }
	drawScore(PLAYERSCORE,CPUSCORE);
	display.display();
	delay(10);
}

void drawScore(int PLAYERSCORE, int CPUSCORE) {
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(2, 2);
    display.print(PLAYERSCORE);
    display.setCursor(122, 2);
    display.print(CPUSCORE);
}

void soundBounce() 
{
  tone(5, 500, 10);
}

void soundBouncec() 
{
  tone(5, 400, 10);
}
void soundPoint() 
{
  tone(5, 150, 150);
}
Serial output
changing BALLDX 2 
changing BALLDX 1 
changing BALLDX 2 
changing BALLDX 1 
changing BALLDX 1 
changing BALLDX 2 
changing BALLDX 1 
changing BALLDX 1 
changing BALLDX 1

Again in another test this was happend with the cpu paddle.

changing BALLDX 2 
changing BALLDX 1 
changing BALLDX 2 
changing BALLDX 2 
changing BALLDX 2 
changing BALLDX 2 
changing BALLDX 2 
changing BALLDX 2 
changing BALLDX 1

Solved that problem by using BALLDX < 0 for player paddle and BALLDX > 0 for the cpu paddle.

Please post the corrected code, or at least put it into your wokwi and share the link.

I saw this

	if(BALLY <= BALL_W || BALLY >= (64-BALL_W))
	{
		BALLDY = BALLDY * (-1);
		soundBouncec();

	}

and wonder if it was the part that gave you trouble.

a7

OK it took a lot of play before I found a flaw, perhaps the same one you are talking about.

I applaud your insistence on ferreting out and fixing these subtle aesthetic issues.

Here's what I did when I finally heard it:

        if (BALLDX != 1) {
          BALLDX = 1;       // go left
          soundBounce();    // once per collision!
        }

so both the bounce correction to BALLDX and the launch of the collision tone happen once and only once, no matter the logic that makes it seem to want to many times.

Which is some flaw in that logic... which won't keep me up at night. Sometimes you have to hit a nail sticking up with a hammer, rather than work to find out and fix why nails are popping up at all.

That's not generally a good way to fix things - in a more complicate sketch fixong thins here and three with a hammer will soon run you into a wall, so beware.

The sound f/x add to the game fun.

a7

I solved it by using BALLDX < 0 for player paddle and BALLDX > 0 for CPU PADDLE.Here is the code.

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>


//DEFINE UP AND DOWN BUTTON FOR PLAYER PADDLE
#define UP_BUTTON 2
#define DOWN_BUTTON 3
int previousballx = 0;

//DEFINE PADDLE SPEED AND PADDLE RATE FOR PLAYER PADDLE
const unsigned long PADDLE_RATE  = 5;
const uint8_t       PADDLE_SPEED = 2;
//DEFINE PLAYER PADDLE STARTING POSITION(X,Y) AND WIDTH AND HEIGHT (X+W,Y+H)
const uint8_t		PLAYER_X	 = 7;
uint8_t			PLAYER_Y	 = 10;
const uint8_t		PLAYER_W	 = 3;
const uint8_t		PLAYER_H	 = 15;
//DEFINE BALL WIDTH/RADIUS , BALL(X,Y), AND BALL DIRECTIONAL SPEED

int 						BALLX 		 = 64;
int 						BALLY 		 = 32;
const uint8_t		                BALL_W		 = 2;
int 						BALLDX		 = 1;
int 						BALLDY		 = 1;

//DEFINE CPU PADDLE STARTING POSITION(X,Y) AND WIDTH AND HEIGHT(X+W,Y+H)
const uint8_t 		CPU_X		 = 120;
uint8_t				    CPU_Y		 = 10;
const uint8_t	  	CPU_W 	 = 3;
const uint8_t		  CPU_H		 = 15;

// DEFINE PLAYER AND CPU SCORE

int 				PLAYERSCORE  = 0;
int 				CPUSCORE 	   = 0;

//DEFINE SCREEN HEIGHT AND WIDTH
const uint8_t		SCREEN_H	 	 = 64;
const uint8_t		SCREEN_W	 	 = 128;
const int			DISPLAY_RST          = -1;

//INITIALIZE DISPLAY

Adafruit_SSD1306 display(SCREEN_W,SCREEN_H,&Wire,DISPLAY_RST);

//DRAWING SCORE FUNCTION 
void drawScore(int PLAYERSCORE,int CPUSCORE);
long randNumber;

void setup()
{
	display.begin(SSD1306_SWITCHCAPVCC,0x3C);
	display.display();

  Serial.begin(115200);
	pinMode(A0, INPUT);
	
	unsigned long start = millis();

	pinMode(UP_BUTTON,	INPUT_PULLUP);
	pinMode(DOWN_BUTTON,INPUT_PULLUP);
	pinMode(5, OUTPUT);
	
	randomSeed(analogRead(A0));

	while(millis() - start < 2000);
	display.clearDisplay();
}

void loop()
{	

  float random_num = random(7);
	display.clearDisplay();

	//READING UP AND DOWN BUTTON FOR PLAYER PADDLE
	int UP_STATE 	= digitalRead(UP_BUTTON);
	int DOWN_STATE  = digitalRead(DOWN_BUTTON);

	//DRAW RECTANGLE AROUND THE SCREEN
	display.drawRect(0,0,128,64,WHITE);
	
	
	if(DOWN_STATE == LOW && (PLAYER_Y+PLAYER_H) < 62)
	{
		PLAYER_Y = PLAYER_Y + PADDLE_RATE;
	}

	
	if(UP_STATE == LOW && PLAYER_Y > 2)
	{
		PLAYER_Y = PLAYER_Y - PADDLE_RATE;
	}
	
	if (random_num < 3) 
	{
			if(previousballx < BALLX && previousballx > 64)
			{
				if(BALLY > (CPU_Y+CPU_H/2)) {
						if((CPU_Y+CPU_H) < 62) {
						CPU_Y += PADDLE_SPEED;
						}
				}
				else if(BALLY < (CPU_Y + CPU_H/2)) {
						if(CPU_Y > 2) {
						CPU_Y -= PADDLE_SPEED;
						}
					}
		}			
	}
  // IF THE RANDOM NUMBER IS GREATER THAN OR EQUAL TO 0.5, DON'T MOVE THE CPU PADDLE

		previousballx = BALLX;		
	
	if(BALLY <= BALL_W || BALLY >= (64-BALL_W))
	{
		BALLDY = BALLDY * (-1);
		soundBouncec();

	}
	if(BALLX <= BALL_W)
	{
		CPUSCORE 	= CPUSCORE + 1;
		soundPoint();
		delay(50);
		BALLX  		= SCREEN_W/2;
		BALLY  		= SCREEN_H/2;
		BALLDX		= 1;
		BALLDY		= 1;
	}
	
	if(BALLX >= (128-BALL_W))
	{
		PLAYERSCORE = PLAYERSCORE + 1;
		soundPoint();
		delay(50);
		BALLX		= 64;
		BALLY		= 32;
		BALLDX		= -1;
		BALLDY		= 1;
	}
	
	if (BALLDX < 0 && BALLX <= PLAYER_X + BALL_W + PLAYER_W && BALLY >= PLAYER_Y && BALLY <= PLAYER_Y + PLAYER_H + BALL_W) 
	{
    Serial.print("changing BALLDX 1"); Serial.print(" ");
    Serial.println("");


    BALLDX = 1; // not toggle, just make it go the other way.
		soundBounce();
  }

  if (BALLDX > 0 && BALLX >= CPU_X - BALL_W && BALLY >= CPU_Y && BALLY <= CPU_Y + CPU_H + BALL_W)
  {
    Serial.print("changing BALLDX 2"); Serial.print(" ");
    Serial.println("");
    BALLDX = -1;
		soundBounce();
  }

	BALLX = BALLX + BALLDX;
	BALLY = BALLY + BALLDY;
	
	display.drawRect(PLAYER_X,PLAYER_Y,PLAYER_W,PLAYER_H,WHITE);
	display.drawRect(CPU_X,CPU_Y,CPU_W,CPU_H,WHITE);
	display.drawCircle(BALLX,BALLY,BALL_W,WHITE);

	//Draw Line middle of the court
	for (int i=0; i<SCREEN_H; i+=4) {
    display.drawFastVLine(SCREEN_W/2, i, 2, WHITE);
  }

	drawScore(PLAYERSCORE,CPUSCORE);
	display.display();

	delay(10);
}

void drawScore(int PLAYERSCORE, int CPUSCORE) {
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(2, 2);
    display.print(PLAYERSCORE);
    display.setCursor(122, 2);
    display.print(CPUSCORE);
}

void soundBounce() 
{
  tone(5, 500, 10);
}

void soundBouncec() 
{
  tone(5, 400, 10);
}
void soundPoint() 
{
  tone(5, 150, 150);
}

(pong.ino - Wokwi Arduino and ESP32 Simulator)

Yes, I see. Given that the if block of code sets BALLDX, that is just another way to make it happen but once.

Which means there is still something "wrong" with the original code, that makes a problem that has to be solved in this manner.

If it rains, I may take time to see how those tests could be written so they don't call for a bounce repeatedly. As I said before, I think it is to do with the widths, meaning the ball doesn't get out of the way of the paddle for a frame or two or three.

Now write the part where the game ends and the winner gets a splashy reward.

a7

1 Like

I also think that this and last problem has something to do with the paddle width.But some how it is working now.Thanks for your help.If you have any solution by which this problem can solved once and for all, Please let me know.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.