dong
This commit is contained in:
commit
c1099cd16c
160
.gitignore
vendored
Normal file
160
.gitignore
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
409
main.py
Normal file
409
main.py
Normal file
@ -0,0 +1,409 @@
|
||||
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()
|
8
pyvenv.cfg
Normal file
8
pyvenv.cfg
Normal file
@ -0,0 +1,8 @@
|
||||
home = /Library/Developer/CommandLineTools/usr/bin
|
||||
implementation = CPython
|
||||
version_info = 3.9.6.final.0
|
||||
virtualenv = 20.25.0
|
||||
include-system-site-packages = false
|
||||
base-prefix = /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9
|
||||
base-exec-prefix = /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.9
|
||||
base-executable = /Library/Developer/CommandLineTools/usr/bin/python3
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
imgui==2.0.0
|
||||
pygame==2.5.2
|
Loading…
x
Reference in New Issue
Block a user