// this code was generated using Deepseek AI with the following prompt: "Can you code a pong game like the Arduino TFT pong documentation does but for using an 0.91" OLED Display, an Arduino nano board? I have two potentiometers on my breadboard which are wired to pin A0 and pin A1. Your first example only allowed me to use one potentiometer - can you make it a two player game where each player uses a potentiometer to control their respective rectangle?" Then updated by angela with the help of instructor Will

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

// OLED configuration for 0.91" 128x32 display
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_ADDR 0x3C

// Initialize display
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// Game variables for two players
int paddle1Y, paddle2Y;        // Current paddle positions (Y-axis only)
int oldPaddle1Y, oldPaddle2Y;   // Previous positions for erasing
int ballX, ballY, oldBallX, oldBallY;
int ballDirectionX = 1;
int ballDirectionY = 1;
int buttonState= 0; //variable for reading the pushbutton status

// Game dimensions
const int PADDLE_WIDTH = 3;      // Narrow paddles for left/right sides
const int PADDLE_HEIGHT = 10;     // Tall enough for 32px screen
const int BALL_SIZE = 2;
const int PADDLE_OFFSET = 5;      // Distance from edge
const int buttonPin = 5;  // number of the pushbutton  pin

// Scoring
int player1Score = 0;
int player2Score = 0;
const int WIN_SCORE = 5;

// Ball speed control
int ballSpeed = 20;  // Lower = faster
unsigned long lastBallMove = 0;

// Game state
bool gameActive = true;

void setup() {
  Serial.begin(9600);
  
  // Initialize OLED
  if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
    Serial.println("SSD1306 allocation failed");
    while(1);
  }
  
  // Clear screen
  display.clearDisplay();
  display.display();
  
  // Initialize ball position (center)
  ballX = SCREEN_WIDTH / 2;
  ballY = SCREEN_HEIGHT / 2;
  
  // Initialize paddle positions (center)
  paddle1Y = (SCREEN_HEIGHT - PADDLE_HEIGHT) / 2;
  paddle2Y = (SCREEN_HEIGHT - PADDLE_HEIGHT) / 2;
  oldPaddle1Y = paddle1Y;
  oldPaddle2Y = paddle2Y;
  
  Serial.println("Two-Player Pong Started!");
  showStartScreen();

  // Initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT_PULLUP);

}

void loop() {
  
  // read the state of the pushbutton vlaue:
  buttonState = digitalRead(buttonPin); 

  // check if the pushbutton is pressed. If it is, the buttonState is LOW:
  if (buttonState == LOW) {
    resetGame();
  }

  if (!gameActive) {
    showGameOver();
    return;
  }
  
  // Read potentiometers and map to paddle positions
  // Player 1 (left) - A0
  int pot1 = analogRead(A0);
  paddle1Y = map(pot1, 0, 1023, 0, SCREEN_HEIGHT - PADDLE_HEIGHT);
  paddle1Y = constrain(paddle1Y, 0, SCREEN_HEIGHT - PADDLE_HEIGHT);
  
  // Player 2 (right) - A1
  int pot2 = analogRead(A1);
  paddle2Y = map(pot2, 0, 1023, 0, SCREEN_HEIGHT - PADDLE_HEIGHT);
  paddle2Y = constrain(paddle2Y, 0, SCREEN_HEIGHT - PADDLE_HEIGHT);
  
  // Erase old paddle positions if moved
  if (oldPaddle1Y != paddle1Y) {
    display.fillRect(PADDLE_OFFSET, oldPaddle1Y, PADDLE_WIDTH, PADDLE_HEIGHT, SSD1306_BLACK);
  }
  if (oldPaddle2Y != paddle2Y) {
    display.fillRect(SCREEN_WIDTH - PADDLE_OFFSET - PADDLE_WIDTH, oldPaddle2Y, PADDLE_WIDTH, PADDLE_HEIGHT, SSD1306_BLACK);
  }
  
  // Draw paddles at new positions
  display.fillRect(PADDLE_OFFSET, paddle1Y, PADDLE_WIDTH, PADDLE_HEIGHT, SSD1306_WHITE);
  display.fillRect(SCREEN_WIDTH - PADDLE_OFFSET - PADDLE_WIDTH, paddle2Y, PADDLE_WIDTH, PADDLE_HEIGHT, SSD1306_WHITE);
  
  // Update ball position at controlled speed
  if (millis() - lastBallMove > ballSpeed) {
    moveBall();
    lastBallMove = millis();
  }
  
  // Draw center line
  drawCenterLine();
  
  // Draw scores
  displayScores();
  
  // Save current positions for next loop
  oldPaddle1Y = paddle1Y;
  oldPaddle2Y = paddle2Y;
  oldBallX = ballX;
  oldBallY = ballY;
  
  // Update display
  display.display();
}

void moveBall() {
  // Erase old ball position
  display.fillRect(oldBallX, oldBallY, BALL_SIZE, BALL_SIZE, SSD1306_BLACK);
  
  // Update ball position
  ballX += ballDirectionX;
  ballY += ballDirectionY;
  
  // Top/bottom wall collisions
  if (ballY <= 0 || ballY >= SCREEN_HEIGHT - BALL_SIZE) {
    ballDirectionY = -ballDirectionY;
  }
  
  // Left paddle (Player 1) collision
  if (ballX <= PADDLE_OFFSET + PADDLE_WIDTH && 
      ballX > PADDLE_OFFSET &&
      ballY + BALL_SIZE >= paddle1Y && 
      ballY <= paddle1Y + PADDLE_HEIGHT) {
    ballDirectionX = abs(ballDirectionX); // Bounce right
    ballX = PADDLE_OFFSET + PADDLE_WIDTH + 1; // Prevent sticking
  }
  
  // Right paddle (Player 2) collision
  if (ballX >= SCREEN_WIDTH - PADDLE_OFFSET - PADDLE_WIDTH - BALL_SIZE && 
      ballX < SCREEN_WIDTH - PADDLE_OFFSET - PADDLE_WIDTH &&
      ballY + BALL_SIZE >= paddle2Y && 
      ballY <= paddle2Y + PADDLE_HEIGHT) {
    ballDirectionX = -abs(ballDirectionX); // Bounce left
    ballX = SCREEN_WIDTH - PADDLE_OFFSET - PADDLE_WIDTH - BALL_SIZE - 1; // Prevent sticking
  }
  
  // Scoring - ball passed paddles
  if (ballX < 0) {
    player2Score++;  // Right player scores
    checkWin();
    resetBall();
    return;
  }
  
  if (ballX > SCREEN_WIDTH) {
    player1Score++;  // Left player scores
    checkWin();
    resetBall();
    return;
  }
  
  // Draw ball at new position
  display.fillRect(ballX, ballY, BALL_SIZE, BALL_SIZE, SSD1306_WHITE);
}

void resetBall() {
  ballX = SCREEN_WIDTH / 2;
  ballY = SCREEN_HEIGHT / 2;
  
  // Randomize starting direction
  ballDirectionX = (random(2) == 0) ? 1 : -1;
  ballDirectionY = (random(2) == 0) ? 1 : -1;
  
  // Small delay to let players prepare
  delay(500);
}

void drawCenterLine() {
  // Draw dashed center line
  for (int i = 0; i < SCREEN_HEIGHT; i += 4) {
    display.drawFastVLine(SCREEN_WIDTH / 2, i, 2, SSD1306_WHITE);
  }
}

void displayScores() {
  // Display scores at top corners
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  
  // Player 1 score (left)
  display.setCursor(10, 0);
  display.print(player1Score);
  
  // Player 2 score (right)
  display.setCursor(SCREEN_WIDTH - 20, 0);
  display.print(player2Score);
}

void showStartScreen() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(20, 8);
  display.println("TWO PLAYER");
  display.setCursor(15, 18);
  display.println("PONG");
  display.display();
  delay(2000);
  display.clearDisplay();
}

void checkWin() {
  if (player1Score >= WIN_SCORE || player2Score >= WIN_SCORE) {
    gameActive = false;
  }
}

void showGameOver() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(25, 0);
  display.println("GAME OVER");
  
  display.setCursor(40, 12);
  display.print(player1Score);
  display.print(" - ");
  display.println(player2Score);
  
  display.setCursor(15, 24);
  if (player1Score >= WIN_SCORE) {
    display.println("PLAYER 1 WINS!");
  } else {
    display.println("PLAYER 2 WINS!");
  }
  
  display.display();
  
}

void resetGame()
{
  // Clear screen
    display.clearDisplay();
    display.display();
    
    // Initialize ball position (center)
    ballX = SCREEN_WIDTH / 2;
    ballY = SCREEN_HEIGHT / 2;
    
    // Initialize paddle positions (center)
    paddle1Y = (SCREEN_HEIGHT - PADDLE_HEIGHT) / 2;
    paddle2Y = (SCREEN_HEIGHT - PADDLE_HEIGHT) / 2;
    oldPaddle1Y = paddle1Y;
    oldPaddle2Y = paddle2Y;
    
    Serial.println("Two-Player Pong Started!");
    showStartScreen();

    gameActive = true;

    player1Score = 0;
    player2Score = 0;


}
