Python
import turtle
import time
import random
import math
# --- MENU ---
print("\n" + "="*40)
print("✨ NEON PING PONG".center(40))
print("="*40)
print("1. Play with a friend")
print("2. Play with computer")
while True:
mode = input("\nChoose mode (1 or 2): ").strip()
if mode in ("1", "2"):
break
else:
print("Invalid input")
if mode == "2":
print("\nChoose computer level:")
print("1. Easy")
print("2. Medium")
print("3. Hard")
while True:
level = input("Enter level (1, 2, or 3): ").strip()
if level in ("1", "2", "3"):
level = int(level)
break
else:
print("Invalid input")
else:
level = None
# --- TURTLE SETUP ---
wind = turtle.Screen()
wind.title("⚡ NEON PING PONG")
wind.bgcolor("#0a0a1a")
wind.setup(width=800, height=600)
wind.tracer(0)
wind.cv.config(borderwidth=0, highlightthickness=0)
# --- MODERN CENTER LINE (FULL HEIGHT) ---
def create_dashed_line():
dashes = turtle.Turtle()
dashes.hideturtle()
dashes.penup()
dashes.goto(0, 300)
dashes.setheading(-90)
dashes.width(3)
dashes.pencolor("#00ffff")
# Draw dashes all the way down the screen
for _ in range(30): # Increased number of dashes to cover full height
dashes.pendown()
dashes.forward(10) # Smaller segments for denser pattern
dashes.penup()
dashes.forward(10)
return dashes
dashed_line = create_dashed_line()
# --- PARTICLE EFFECT CLASS ---
class Particle:
def __init__(self, x, y, color):
self.turtle = turtle.Turtle()
self.turtle.hideturtle()
self.turtle.penup()
self.turtle.goto(x, y)
self.turtle.shape("circle")
self.turtle.color(color)
self.turtle.shapesize(0.3)
self.turtle.showturtle()
self.lifetime = random.randint(10, 20)
self.velocity = [random.uniform(-1, 1), random.uniform(-0.5, 1)]
def update(self):
self.lifetime -= 1
if self.lifetime <= 0:
self.turtle.hideturtle()
self.turtle.clear()
return False
x, y = self.turtle.position()
self.turtle.goto(x + self.velocity[0], y + self.velocity[1])
alpha = self.lifetime / 20
# Ensure size never reaches zero
new_size = max(0.01, 0.3 * alpha)
self.turtle.shapesize(new_size)
return True
particles = []
# --- PADDLES WITH GLOW EFFECT ---
def create_paddle(x, color, glow_color):
# Glow effect
glow = turtle.Turtle()
glow.hideturtle()
glow.penup()
glow.goto(x, 0)
glow.shape("square")
glow.color(glow_color)
glow.shapesize(stretch_wid=5.5, stretch_len=1.5)
# Main paddle
paddle = turtle.Turtle()
paddle.speed(0)
paddle.shape("square")
paddle.color(color)
paddle.shapesize(stretch_wid=5, stretch_len=1)
paddle.penup()
paddle.goto(x, 0)
return paddle, glow
madrab1, glow1 = create_paddle(-350, "#00ffea", "#00ffff")
madrab2, glow2 = create_paddle(350, "#ff00e6", "#ff00ff")
# --- BALL WITH TRAIL EFFECT ---
ball = turtle.Turtle()
ball.speed(0)
ball.shape("circle")
ball.color("#ffffff")
ball.penup()
ball.goto(0, 0)
ball.dx = random.choice([-1, 1]) * 1.3
ball.dy = random.choice([-1, 1]) * 1.3
ball.trail = []
# --- SCORE DISPLAY ---
score1 = 0
score2 = 0
# Main score
score = turtle.Turtle()
score.speed(0)
score.color("#ffffff")
score.penup()
score.hideturtle()
score.goto(0, 260)
# Score shadow
score_shadow = turtle.Turtle()
score_shadow.speed(0)
score_shadow.color("#00ffff")
score_shadow.penup()
score_shadow.hideturtle()
score_shadow.goto(2, 258)
def update_score():
score_shadow.clear()
score.clear()
score_text = f"Player 1: {score1} Player 2: {score2}"
score_shadow.write(score_text, align="center", font=("Arial", 24, "bold"))
score.write(score_text, align="center", font=("Arial", 24, "bold"))
update_score()
# --- PADDLE GLOW ANIMATION ---
def paddle_glow(paddle, glow):
glow.goto(paddle.xcor(), paddle.ycor())
for i in range(5, 0, -1):
glow.shapesize(stretch_wid=5 + i/3, stretch_len=1 + i/6)
wind.update()
time.sleep(0.02)
glow.shapesize(stretch_wid=5.5, stretch_len=1.5)
# --- PADDLE CONTROLS ---
def madrab1_up():
if game_state == "running":
y = madrab1.ycor()
if y < 250:
madrab1.sety(y + 30)
glow1.goto(madrab1.xcor(), madrab1.ycor())
def madrab1_down():
if game_state == "running":
y = madrab1.ycor()
if y > -250:
madrab1.sety(y - 30)
glow1.goto(madrab1.xcor(), madrab1.ycor())
def madrab2_up():
if game_state == "running":
y = madrab2.ycor()
if y < 250:
madrab2.sety(y + 30)
glow2.goto(madrab2.xcor(), madrab2.ycor())
def madrab2_down():
if game_state == "running":
y = madrab2.ycor()
if y > -250:
madrab2.sety(y - 30)
glow2.goto(madrab2.xcor(), madrab2.ycor())
# --- COMPUTER AI ---
def computer_move():
target_y = ball.ycor()
if level == 1: # Easy
if random.random() < 0.7: # Only move 70% of the time
if target_y > madrab2.ycor() + 15:
madrab2.sety(min(madrab2.ycor() + 15, 250))
elif target_y < madrab2.ycor() - 15:
madrab2.sety(max(madrab2.ycor() - 15, -250))
elif level == 2: # Medium
if abs(target_y - madrab2.ycor()) > 20:
if target_y > madrab2.ycor():
madrab2.sety(min(madrab2.ycor() + 20, 250))
else:
madrab2.sety(max(madrab2.ycor() - 20, -250))
elif level == 3: # Hard
# Predict ball trajectory
future_y = ball.ycor() + ball.dy * 15
if abs(future_y - madrab2.ycor()) > 10:
if future_y > madrab2.ycor():
madrab2.sety(min(madrab2.ycor() + 25, 250))
else:
madrab2.sety(max(madrab2.ycor() - 25, -250))
glow2.goto(madrab2.xcor(), madrab2.ycor())
# --- RESET BALL ---
def reset_ball():
ball.goto(0, 0)
ball.dx = random.choice([-1, 1]) * 2.0
ball.dy = random.choice([-1, 1]) * 2.0
# Clean up old trails
for trail, _ in ball.trail:
trail.hideturtle()
trail.clear()
ball.trail = []
# Create explosion effect
for _ in range(30):
particles.append(Particle(0, 0, random.choice(["#ff00e6", "#00ffea", "#ffffff"])))
time.sleep(0.5)
# --- START SCREEN ---
game_state = "waiting"
start_msg = turtle.Turtle()
start_msg.hideturtle()
start_msg.color("#ffffff")
start_msg.penup()
start_msg.goto(0, 0)
start_msg.write("PRESS SPACE OR RETURN TO START", align="center", font=("Arial", 28, "bold"))
# Create win message turtle
win_msg = turtle.Turtle()
win_msg.hideturtle()
win_msg.color("#ffffff")
win_msg.penup()
win_msg.goto(0, 0)
def start_game():
global game_state
if game_state == "waiting" or game_state == "game_over":
game_state = "running"
start_msg.clear()
win_msg.clear()
def quit_game():
wind.bye()
# SET UP KEY BINDINGS PROPERLY
wind.listen()
wind.onkeypress(madrab1_up, "w")
wind.onkeypress(madrab1_down, "s")
# Changed Player 2 down key from Down Arrow to 'm'
if mode == "1":
wind.onkeypress(madrab2_up, "Up")
wind.onkeypress(madrab2_down, "m") # Changed to 'm' key
# Bind start to space and return
wind.onkeypress(start_game, "Return")
wind.onkeypress(start_game, "space")
wind.onkeypress(quit_game, "q")
# --- GAME LOOP ---
last_time = time.time()
while True:
current_time = time.time()
delta_time = current_time - last_time
last_time = current_time
wind.update()
# Update particles
new_particles = []
for p in particles:
if p.update():
new_particles.append(p)
else:
p.turtle.clear()
particles = new_particles
if game_state == "running":
# Ball trail effect
if len(ball.trail) < 5:
trail = turtle.Turtle()
trail.hideturtle()
trail.shape("circle")
trail.color("#fff700")
trail.penup()
trail.goto(ball.xcor(), ball.ycor())
trail.shapesize(0.5)
trail.showturtle()
ball.trail.append((trail, 10))
# Update ball trail
updated_trails = []
for trail, life in ball.trail:
new_life = life - 0.5
if new_life > 0:
# Ensure size never reaches zero
new_size = max(0.05, 0.5 * (new_life/10))
trail.shapesize(new_size)
updated_trails.append((trail, new_life))
else:
trail.hideturtle()
trail.clear()
ball.trail = updated_trails
# Move ball
ball.setx(ball.xcor() + ball.dx)
ball.sety(ball.ycor() + ball.dy)
# Wall collision
if ball.ycor() > 290:
ball.sety(290)
ball.dy *= -1
# Create particles on wall hit
for _ in range(10):
particles.append(Particle(ball.xcor(), ball.ycor(), "#fff700"))
elif ball.ycor() < -290:
ball.sety(-290)
ball.dy *= -1
for _ in range(10):
particles.append(Particle(ball.xcor(), ball.ycor(), "#fff700"))
# Paddle collisions
if (-350 < ball.xcor() < -340) and (madrab1.ycor() - 50 < ball.ycor() < madrab1.ycor() + 50):
ball.setx(-340)
ball.dx = abs(ball.dx) * 1.05 # Ensure positive direction
ball.dy += random.uniform(-0.2, 0.2)
ball.color("#00ffea")
paddle_glow(madrab1, glow1)
# Create particles on hit
for _ in range(15):
particles.append(Particle(ball.xcor(), ball.ycor(), "#00ffea"))
if (340 < ball.xcor() < 350) and (madrab2.ycor() - 50 < ball.ycor() < madrab2.ycor() + 50):
ball.setx(340)
ball.dx = -abs(ball.dx) * 1.05 # Ensure negative direction
ball.dy += random.uniform(-0.2, 0.2)
ball.color("#ff00e6")
paddle_glow(madrab2, glow2)
# Create particles on hit
for _ in range(15):
particles.append(Particle(ball.xcor(), ball.ycor(), "#ff00e6"))
# Score
if ball.xcor() < -390:
score2 += 1
update_score()
reset_ball()
if ball.xcor() > 390:
score1 += 1
update_score()
reset_ball()
# Computer move
if mode == "2":
computer_move()
# Win condition - stops game and shows winner
if score1 >= 11 or score2 >= 11:
winner = "PLAYER 1" if score1 >= 11 else "PLAYER 2"
win_msg.clear()
win_msg.write(f"{winner} WINS!", align="center", font=("Arial", 40, "bold"))
game_state = "game_over"
time.sleep(0.005)