#include <LiquidCrystal.h>
#include <avr/interrupt.h>
// Assign the pins for the screen
#define RS 12
#define EN 11
#define D4 6
#define D5 7
#define D6 8
#define D7 9
// Define the buttons that move the paddles
#define Player_1_moveDownButton 10 // Down button for player 1
#define Player_1_moveUpButton 5 // Up button for player 1
#define Player_2_moveDownButton 2 // Down button for player 2
#define Player_2_moveUpButton 3 // Up button for player 2
// Assign the player numbers
#define Player_1 1
#define Player_2 2
// Wait times between ball updates
#define DiagonalballUpdateTime 21
#define HorizontalballUpdateTime 15
// Starting position of the ball
#define Start_X 35
#define Start_Y 7
#define Button_Pressed (p1_UpButState | p1_DownButState | p2_UpButState | p2_DownButState)
void(* resetFunc) (void) = 0;
// Global variables for interrupts
volatile boolean x_Up = true;
volatile boolean x_Down = true;
LiquidCrystal lcd(RS, EN, D4, D5, D6, D7); // Initialize the LCD screen
class Paddle // Class for the paddle
{
public:
// Store the value of each row containing the paddle
uint8_t PaddleColArray[16] = {0,0,0,0,0,4,4,4,0,0,0,0,0,0,0,0};
uint8_t PaddlePos = 6; // Center of the paddle as the reference to move it
uint8_t Score = 0; // Player's score
void MovePaddleUp() // When the player presses the up button
{
// Check if the paddle doesn't go out of the screen
if(PaddlePos != 1)
{
PaddlePos--;
PaddleColArray[PaddlePos+2]=0;
PaddleColArray[PaddlePos-1]=4;
}
}
// When the player presses the down button
void MovePaddleDown()
{
if(PaddlePos != 14)
{
PaddlePos++;
PaddleColArray[PaddlePos-2]=0;
PaddleColArray[PaddlePos+1]=4;
}
}
// Display paddles with the player numbers
void PrintPaddles(uint8_t Player_Num)
{
if(Player_Num == 2)
{
// Each player has a unique number to display
lcd.createChar(0, PaddleColArray);
lcd.createChar(1, PaddleColArray+8);
lcd.setCursor(14, 0);
lcd.write(byte(0));
lcd.setCursor(14, 1);
lcd.write(byte(1));
}
else
{
lcd.createChar(2, PaddleColArray);
lcd.createChar(3, PaddleColArray+8);
lcd.setCursor(1, 0);
lcd.write(byte(2));
lcd.setCursor(1, 1);
lcd.write(byte(3));
}
}
};
// Create the two paddles
Paddle p1, p2;
class Print_Game
{
public:
// Display text when the game starts
void Start_Game()
{
lcd.print(F(" PING PONG "));
// Move to the second line
lcd.setCursor(0, 1);
lcd.print(F("Press a button! "));
// Variable to check if a button is pressed
uint8_t p1_UpButState = 0;
uint8_t p1_DownButState = 0;
uint8_t p2_UpButState = 0;
uint8_t p2_DownButState = 0;
// Wait for a button to be pressed
while(!(Button_Pressed))
{
p1_UpButState = ((PIND & (1 << Player_1_moveUpButton)) );
p1_DownButState = ((PINB & (1 << (Player_1_moveDownButton-8))) );
p2_UpButState = ((PIND & (1 << Player_2_moveUpButton)) );
p2_DownButState = ((PIND & (1 << Player_2_moveDownButton)) );
}
lcd.clear(); // Clear the screen to start the game
}
// Display the score for each player
void Print_Score()
{
// Clear the LCD screen to show the scores
lcd.clear();
lcd.print(F("Player_1 Player_2"));
lcd.setCursor(3 ,1);
lcd.print(p1.Score);
lcd.setCursor(12 ,1);
lcd.print(p2.Score);
// Scores are displayed for 2 seconds
delay(2000);
// Clear the screen to continue the game
lcd.clear();
}
// Display the winner
void Print_Winner(int Player_Num)
{
lcd.setCursor(0 ,0);
lcd.print(F(" LOST "));
lcd.setCursor(1, 1);
lcd.print(F("Player "));
lcd.print(Player_Num);
lcd.setCursor(11 ,1);
lcd.print(F("wins"));
// Text remains for 5 seconds
delay(5000);
// Reset the game
resetFunc();
}
};
Print_Game g;
class Ball // Ball function
{
private:
// Flag to reset the ball and paddles when a point is scored
uint8_t Point_Scored = 0;
uint8_t ballYDir = 0;
uint8_t ballXDir = -1;
// Ball location
uint8_t ballY = Start_Y;
uint8_t ballX = Start_X;
uint8_t ballCharArray[8] = {0, 0, 0, 0, 0, 0, 0, 16};
public:
void GenerateBallArray();
void PrintBall();
void UpdateBall(uint8_t , uint8_t);
void AwardAPoint();
};
void Ball :: GenerateBallArray() // Generate the ball
{
for(uint8_t i=0; i<8; i++)
{
if(i == (ballY % 8))
{
ballCharArray[i] = 2 << (4 - (ballX % 5));
}
else
{
ballCharArray[i] = 0;
}
}
}
void Ball :: PrintBall() // Display the ball
{
uint8_t LCDCol = ballX / 5;
uint8_t LCDRow = (ballY <= 7) ? 0 : 1;
lcd.createChar(4, ballCharArray);
lcd.setCursor(LCDCol,LCDRow);
lcd.write(byte(4));
}
// Update the ball position
void Ball :: UpdateBall(uint8_t P1_PaddlePos, uint8_t P2_PaddlePos)
{
// Small delay before update
// The ball moves at the same speed when moving diagonally
if(ballYDir) // When the ball moves diagonally
{
delay(DiagonalballUpdateTime);
}
else // When the ball moves horizontally
{
delay(HorizontalballUpdateTime);
}
// Calculate the next ball position
// If the ball goes out of the paddle
if((ballX <= 6) || (ballX >= 73))
{
AwardAPoint();
}
// If the ball hits player 2's paddle
else if(ballX == 72)
{
// if the ball hits the middle of the paddle
if(ballY == P2_PaddlePos)
{
ballXDir = -1;
}
// If the ball hits the bottom of the paddle
else if(ballY == (P2_PaddlePos + 1))
{
ballXDir = -1;
if(ballY == 15) // If the ball hits the bottom of the paddle at the screen's edge
{
ballYDir = -1;
}
else
{
ballYDir = 1;
}
}
// If the ball hits the top of player 2's paddle
else if(ballY == (P2_PaddlePos - 1)){
ballXDir = -1;
if(ballY == 0) // If the ball hits the top of the paddle and the top of the screen
{
ballYDir = 1;
}
else
{
ballYDir = -1;
}
}
}
// If the ball is at the middle of player 1's paddle
else if(ballX == 7)
{
if(ballY == P1_PaddlePos)
{
ballXDir = 1;
}
// If the ball hits the bottom of the paddle
else if(ballY == (P1_PaddlePos + 1)){
ballXDir = 1;
if(ballY == 15) // If the ball hits the bottom of the paddle at the screen's edge
{