dong/main.py
2024-02-25 11:05:47 -05:00

410 lines
12 KiB
Python

import pygame
import math
from enum import Enum
# import pprint
# from pygame import gfxdraw
import numpy as np
SIZE = (1000, 1000)
LEFT_COLOR = "#606858"
RIGHT_COLOR = "#F7F9F6"
BLOCKS_SIZE = 10
PADDLE_LENGTH = 100
HALF_SPEED = 250
def draw_circle(surface, x, y, radius, color):
pygame.draw.circle(surface, color, (x, y), radius)
# AA Drawing
# gfxdraw.aacircle(surface, x, y, radius, color)
# gfxdraw.filled_circle(surface, x, y, radius, color)
def draw_rect(surface, topL, topT, width, height, color, corner_radius=0):
pygame.draw.rect(
surface,
color,
pygame.Rect(topL, topT, width, height),
border_radius=corner_radius,
)
def angle_arccos(v1, v2):
y_diff = v2[1] - v1[1]
x_diff = v2[0] - v1[0]
angle = math.atan2(y_diff, x_diff)
return angle
def intersects(rect, r, center):
circle_distance_x = abs(center[0] - rect.centerx)
circle_distance_y = abs(center[1] - rect.centery)
if circle_distance_x > rect.w / 2.0 + r or circle_distance_y > rect.h / 2.0 + r:
return False
if circle_distance_x <= rect.w / 2.0 or circle_distance_y <= rect.h / 2.0:
return True
corner_x = circle_distance_x - rect.w / 2.0
corner_y = circle_distance_y - rect.h / 2.0
corner_distance_sq = corner_x**2.0 + corner_y**2.0
return corner_distance_sq <= r**2.0
class TileType(Enum):
PLAYER = 1
CPU = 2
class ObjectType(Enum):
PLAYER = 1
CPU = 2
class Tile:
def __init__(self, positionx, positiony, sidelength, color, tileType) -> None:
self.x = positionx
self.y = positiony
self.length = sidelength
self.rect = pygame.Rect(positionx, positiony, sidelength, sidelength)
self.color = color
self.tileType = tileType
class Paddle:
def __init__(
self, startx, starty, length, depth, color, radius, initial_vel=300
) -> None:
self.x = startx
self.y = starty
self.length = length
self.depth = depth
self.vy = initial_vel
self.color = color
self.radius = radius
self.elasticity = 0.08
def draw(self, screen):
draw_rect(
screen,
self.x - (self.depth / 2),
self.y - (self.length / 2),
self.depth,
self.length,
self.color,
corner_radius=self.radius,
)
def move(self, dt):
newy = self.y + (self.vy * dt)
if (newy - self.length / 2 <= 0) or (newy + self.length / 2 >= SIZE[0]):
return
self.y = newy
def set_vel(self, vel):
self.vy = vel
class Ball:
def __init__(self, startx, starty, size, color, charType) -> None:
self.x = startx
self.y = starty
self.radius = size
self.color = pygame.Color(color)
self.mass = 0.25
self.vx = 0
self.vy = 0
self.charType = charType
self.speed = HALF_SPEED
def draw(self, screen):
draw_circle(screen, self.x, self.y, self.radius, self.color)
def set_speed(self, speed):
self.speed = speed
def move(self, grid, object_grid, dt):
# Pretend to move to new location
newx = self.x + (self.vx * dt)
newy = self.y + (self.vy * dt)
# If we are at the top/bottom edge of the board, flip Y velocity
if newy + self.radius >= SIZE[0] or newy - self.radius <= 0:
self.vy = self.vy * -1.0
# Back wall
# TODO: Make this a life or lose condition
if newx + self.radius >= SIZE[0] or newx - self.radius <= 0:
self.vx = self.vx * -1.0
grid_x = math.floor(newx / 100)
grid_y = math.floor(newy / 100)
candidates = []
if (
grid[grid_y][grid_x] == TileType.CPU and self.charType == ObjectType.PLAYER
) or (
grid[grid_y][grid_x] == TileType.PLAYER and self.charType == ObjectType.CPU
):
candidates.append(object_grid[grid_y][grid_x])
if grid_x + 1 < BLOCKS_SIZE:
if self.vx > 0:
if (
grid[grid_y][grid_x + 1] == TileType.CPU
and self.charType == ObjectType.PLAYER
) or (
grid[grid_y][grid_x + 1] == TileType.PLAYER
and self.charType == ObjectType.CPU
):
candidates.append(object_grid[grid_y][grid_x + 1])
if grid_x - 1 >= 0:
if self.vx <= 0:
if (
grid[grid_y][grid_x - 1] == TileType.CPU
and self.charType == ObjectType.PLAYER
) or (
grid[grid_y][grid_x - 1] == TileType.PLAYER
and self.charType == ObjectType.CPU
):
candidates.append(object_grid[grid_y][grid_x - 1])
if grid_y + 1 < BLOCKS_SIZE:
if self.vy > 0:
if (
grid[grid_y + 1][grid_x] == TileType.CPU
and self.charType == ObjectType.PLAYER
) or (
grid[grid_y + 1][grid_x] == TileType.PLAYER
and self.charType == ObjectType.CPU
):
candidates.append(object_grid[grid_y + 1][grid_x])
if grid_y - 1 >= 0:
if self.vy <= 0:
if (
grid[grid_y - 1][grid_x] == TileType.CPU
and self.charType == ObjectType.PLAYER
) or (
grid[grid_y - 1][grid_x] == TileType.PLAYER
and self.charType == ObjectType.CPU
):
candidates.append(object_grid[grid_y - 1][grid_x])
for candidate in candidates:
if intersects(candidate.rect, self.radius, (self.x, self.y)):
if self.x >= candidate.x and self.x <= (candidate.x + candidate.length):
if (candidate.y + candidate.length) < self.y:
self.vy = self.vy * -1
if self.y < candidate.y:
self.vy = self.vy * -1
if candidate.x > self.x:
self.vx = self.vx * -1.0
if (candidate.x + candidate.length) < self.x:
self.vx = self.vx * -1.0
if candidate.tileType == TileType.PLAYER:
candidate.tileType = TileType.CPU
candidate.color = RIGHT_COLOR
else:
candidate.tileType = TileType.PLAYER
candidate.color = LEFT_COLOR
tgrid_x = int(candidate.x / 100)
tgrid_y = int(candidate.y / 100)
if grid[tgrid_y][tgrid_x] == TileType.PLAYER:
grid[tgrid_y][tgrid_x] = TileType.CPU
else:
grid[tgrid_y][tgrid_x] = TileType.PLAYER
break
self.x = self.x + (self.vx * dt)
self.y = self.y + (self.vy * dt)
# Fix speed if necessary:
if abs(self.vx) < 3:
if self.vx < 0:
self.vx = -5
else:
self.vx = 5
# Normalize to speed
mag = math.sqrt((self.vx * self.vx) + (self.vy * self.vy))
if mag != 0:
self.vx = (self.vx / mag) * self.speed
self.vy = (self.vy / mag) * self.speed
def perform_spawn_hit(self):
forceX = np.random.uniform(low=-50.0, high=50.0)
forceY = np.random.uniform(low=-50.0, high=50.0)
# Impulse dv = (f * dt)/m
# Assuming dt = 1
self.vx = (forceX) / self.mass
self.vy = (forceY) / self.mass
def ball_paddle_collide(ball: Ball, paddle: Paddle):
pRect = pygame.Rect(
paddle.x - paddle.depth / 2,
paddle.y - paddle.length / 2,
paddle.depth,
paddle.length,
)
collide = intersects(pRect, ball.radius, (ball.x, ball.y))
if collide:
ball.vx = (ball.vx * -1)
# Depending on where the ball hit on the paddle
# relative to the center, add more or less vy
mag = (ball.y - paddle.y) / (paddle.length * 1.25)
ball.vy = ball.vy + (mag * 50)
return True
return False
pygame.init()
screen = pygame.display.set_mode(SIZE)
clock = pygame.time.Clock()
running = True
dt = 0
ball_pos_left = pygame.Vector2(screen.get_width() / 4, screen.get_height() / 2)
ball_pos_right = pygame.Vector2((screen.get_width() / 4) * 3, screen.get_height() / 2)
left_ball = Ball(ball_pos_left.x, ball_pos_left.y, 10, RIGHT_COLOR, ObjectType.PLAYER)
right_ball = Ball(ball_pos_right.x, ball_pos_right.y, 10, LEFT_COLOR, ObjectType.CPU)
p1 = Paddle(20, screen.get_height() / 2, PADDLE_LENGTH, 20, RIGHT_COLOR, 5, 300)
p2 = Paddle(
screen.get_width() - 20,
screen.get_height() / 2,
PADDLE_LENGTH,
20,
LEFT_COLOR,
5,
300,
)
p1score = 0
p2score = 0
grid = [[] * BLOCKS_SIZE]
LENGTH = BLOCKS_SIZE
A = TileType.PLAYER
B = TileType.CPU
# Initialize the 2D array with zeros
grid = [[0 for _ in range(LENGTH)] for _ in range(LENGTH)]
# Determine the midpoint of the array
midpoint = LENGTH // 2
# Fill the first half of the array with value A
for i in range(midpoint):
for j in range(LENGTH):
grid[j][i] = A
# Fill the second half of the grid with value B
for i in range(midpoint, LENGTH):
for j in range(LENGTH):
grid[j][i] = B
object_grid = [[0 for _ in range(LENGTH)] for _ in range(LENGTH)]
for i in range(LENGTH):
for j in range(LENGTH):
if grid[j][i] == TileType.PLAYER:
object_grid[j][i] = Tile(i * 100, j * 100, 100, LEFT_COLOR, TileType.PLAYER)
else:
object_grid[j][i] = Tile(i * 100, j * 100, 100, RIGHT_COLOR, TileType.CPU)
BACKGROUND_RIGHT = pygame.Rect(
screen.get_width() / 2, 0, screen.get_width() / 2, screen.get_height()
)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Render grid
for i in range(LENGTH):
for j in range(LENGTH):
t = object_grid[j][i]
draw_rect(screen, t.rect.x, t.rect.y, t.rect.width, t.rect.height, t.color)
# Draw ball
left_ball.draw(screen)
right_ball.draw(screen)
p1.draw(screen)
p2.draw(screen)
# input handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
p1.set_vel(0)
p2.set_vel(0)
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
p1.set_vel(-300.0)
if keys[pygame.K_s]:
p1.set_vel(300.0)
if keys[pygame.K_i]:
p2.set_vel(-300.0)
if keys[pygame.K_k]:
p2.set_vel(300.0)
if keys[pygame.K_h]:
left_ball.perform_spawn_hit()
right_ball.perform_spawn_hit()
if keys[pygame.K_q]:
running = False
# Movement and physics
left_ball.move(grid, object_grid, dt)
right_ball.move(grid, object_grid, dt)
p1.move(dt)
p2.move(dt)
ball_paddle_collide(left_ball, p1)
ball_paddle_collide(right_ball, p2)
# Compute Score
p1score = 0
p2score = 0
for i in grid:
for j in i:
if j == TileType.PLAYER:
p1score += 1
else:
p2score += 1
# if your score is higher, your ball is faster
left_ball.set_speed(HALF_SPEED * (p1score/50.0))
right_ball.set_speed(HALF_SPEED * (p2score/50.0))
pygame.display.flip()
dt = clock.tick(60) / 1000
print("\x1b[1A\x1b[2K", end="")
print(f"scores -> \t {p1score} ({left_ball.speed}, ({left_ball.vx}, {left_ball.vy}) \t {p2score} ({right_ball.speed}, {right_ball.vx})")
pygame.quit()