377 lines
12 KiB
Python
377 lines
12 KiB
Python
# -----------------------------------------------------------------------------
|
|
#
|
|
# 8 Ball Pool
|
|
# Language - Python
|
|
# Modules - pygame, sys, random, math
|
|
#
|
|
# Controls - Mouse, the length of Stick is propotional to Force applied
|
|
#
|
|
# By - Jatin Kumar Mandav
|
|
#
|
|
# Website - https://jatinmandav.wordpress.com
|
|
#
|
|
# YouTube Channel - https://www.youtube.com/channel/UCdpf6Lz3V357cIZomPwjuFQ
|
|
# Twitter - @jatinmandav
|
|
#
|
|
# -----------------------------------------------------------------------------
|
|
|
|
import pygame
|
|
import sys
|
|
from math import *
|
|
import random
|
|
|
|
pygame.init()
|
|
width = 660
|
|
height = 360
|
|
outerHeight = 400
|
|
margin = 30
|
|
display = pygame.display.set_mode((width, outerHeight))
|
|
pygame.display.set_caption("8 Ball Pool")
|
|
clock = pygame.time.Clock()
|
|
|
|
background = (51, 51, 51)
|
|
white = (236, 240, 241)
|
|
|
|
gray = (123, 125, 125)
|
|
|
|
black = (23, 32, 42)
|
|
yellow = (244, 208, 63)
|
|
blue = (52, 152, 219)
|
|
red = (203, 67, 53)
|
|
purple = (136, 78, 160)
|
|
orange = (230, 126, 34)
|
|
green = (40, 180, 99)
|
|
brown = (100, 30, 22)
|
|
stickColor = (249, 231, 159)
|
|
|
|
colors = [yellow, blue, red, purple, orange, green, brown, black, yellow, blue, red, purple, orange, green, brown]
|
|
|
|
balls = []
|
|
noBalls = 15
|
|
radius = 10
|
|
friction = 0.005
|
|
|
|
# Ball Class
|
|
class Ball:
|
|
def __init__(self, x, y, speed, color, angle, ballNum):
|
|
self.x = x + radius
|
|
self.y = y + radius
|
|
self.color = color
|
|
self.angle = angle
|
|
self.speed = speed
|
|
self.ballNum = ballNum
|
|
self.font = pygame.font.SysFont("Agency FB", 10)
|
|
|
|
# Draws Balls on Display Window
|
|
def draw(self, x, y):
|
|
pygame.draw.ellipse(display, self.color, (x - radius, y - radius, radius*2, radius*2))
|
|
if self.color == black or self.ballNum == "cue":
|
|
ballNo = self.font.render(str(self.ballNum), True, white)
|
|
display.blit(ballNo, (x - 5, y - 5))
|
|
else:
|
|
ballNo = self.font.render(str(self.ballNum), True, black)
|
|
if self.ballNum > 9:
|
|
display.blit(ballNo, (x - 6, y - 5))
|
|
else:
|
|
display.blit(ballNo, (x - 5, y - 5))
|
|
|
|
# Moves the Ball around the Screen
|
|
def move(self):
|
|
self.speed -= friction
|
|
if self.speed <= 0:
|
|
self.speed = 0
|
|
self.x = self.x + self.speed*cos(radians(self.angle))
|
|
self.y = self.y + self.speed*sin(radians(self.angle))
|
|
|
|
if not (self.x < width - radius - margin):
|
|
self.x = width - radius - margin
|
|
self.angle = 180 - self.angle
|
|
if not(radius + margin < self.x):
|
|
self.x = radius + margin
|
|
self.angle = 180 - self.angle
|
|
if not (self.y < height - radius - margin):
|
|
self.y = height - radius - margin
|
|
self.angle = 360 - self.angle
|
|
if not(radius + margin < self.y):
|
|
self.y = radius + margin
|
|
self.angle = 360 - self.angle
|
|
|
|
# Pocket Class
|
|
class Pockets:
|
|
def __init__(self, x, y, color):
|
|
self.r = margin/2
|
|
self.x = x + self.r + 10
|
|
self.y = y + self.r + 10
|
|
self.color = color
|
|
|
|
# Draws the Pockets on Pygame Window
|
|
def draw(self):
|
|
pygame.draw.ellipse(display, self.color, (self.x - self.r, self.y - self.r, self.r*2, self.r*2))
|
|
|
|
# Checks if ball has entered the Hole
|
|
def checkPut(self):
|
|
global balls
|
|
ballsCopy = balls[:]
|
|
for i in range(len(balls)):
|
|
dist = ((self.x - balls[i].x)**2 + (self.y - balls[i].y)**2)**0.5
|
|
if dist < self.r + radius:
|
|
if balls[i] in ballsCopy:
|
|
if balls[i].ballNum == 8:
|
|
gameOver()
|
|
else:
|
|
ballsCopy.remove(balls[i])
|
|
|
|
balls = ballsCopy[:]
|
|
|
|
# Cue Stick Class
|
|
class CueStick:
|
|
def __init__(self, x, y, length, color):
|
|
self.x = x
|
|
self.y = y
|
|
self.length = length
|
|
self.color = color
|
|
self.tangent = 0
|
|
|
|
# Applies force to Cue Ball
|
|
def applyForce(self, cueBall, force):
|
|
cueBall.angle = self.tangent
|
|
cueBall.speed = force
|
|
|
|
# Draws Cue Stick on Pygame Window
|
|
def draw(self, cuex, cuey):
|
|
self.x, self.y = pygame.mouse.get_pos()
|
|
self.tangent = (degrees(atan2((cuey - self.y), (cuex - self.x))))
|
|
pygame.draw.line(display, white, (cuex + self.length*cos(radians(self.tangent)), cuey + self.length*sin(radians(self.tangent))), (cuex, cuey), 1)
|
|
pygame.draw.line(display, self.color, (self.x, self.y), (cuex, cuey), 3)
|
|
|
|
|
|
# Checks Collision
|
|
def collision(ball1, ball2):
|
|
dist = ((ball1.x - ball2.x)**2 + (ball1.y - ball2.y)**2)**0.5
|
|
if dist <= radius*2:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
# Checks if Cue Ball hits any Ball
|
|
def checkCueCollision(cueBall):
|
|
for i in range(len(balls)):
|
|
if collision(cueBall, balls[i]):
|
|
if balls[i].x == cueBall.x:
|
|
angleIncline = 2*90
|
|
else:
|
|
u1 = balls[i].speed
|
|
u2 = cueBall.speed
|
|
|
|
balls[i].speed = ((u1*cos(radians(balls[i].angle)))**2 + (u2*sin(radians(cueBall.angle)))**2)**0.5
|
|
cueBall.speed = ((u2*cos(radians(cueBall.angle)))**2 + (u1*sin(radians(balls[i].angle)))**2)**0.5
|
|
|
|
tangent = degrees((atan((balls[i].y - cueBall.y)/(balls[i].x - cueBall.x)))) + 90
|
|
angle = tangent + 90
|
|
|
|
balls[i].angle = (2*tangent - balls[i].angle)
|
|
cueBall.angle = (2*tangent - cueBall.angle)
|
|
|
|
balls[i].x += (balls[i].speed)*sin(radians(angle))
|
|
balls[i].y -= (balls[i].speed)*cos(radians(angle))
|
|
cueBall.x -= (cueBall.speed)*sin(radians(angle))
|
|
cueBall.y += (cueBall.speed)*cos(radians(angle))
|
|
|
|
|
|
# Checks Collision Between Balls
|
|
def checkCollision():
|
|
for i in range(len(balls)):
|
|
for j in range(len(balls) - 1, i, -1):
|
|
if collision(balls[i], balls[j]):
|
|
if balls[i].x == balls[j].x:
|
|
angleIncline = 2*90
|
|
else:
|
|
u1 = balls[i].speed
|
|
u2 = balls[j].speed
|
|
|
|
balls[i].speed = ((u1*cos(radians(balls[i].angle)))**2 + (u2*sin(radians(balls[j].angle)))**2)**0.5
|
|
balls[j].speed = ((u2*cos(radians(balls[j].angle)))**2 + (u1*sin(radians(balls[i].angle)))**2)**0.5
|
|
|
|
tangent = degrees((atan((balls[i].y - balls[j].y)/(balls[i].x - balls[j].x)))) + 90
|
|
angle = tangent + 90
|
|
|
|
balls[i].angle = (2*tangent - balls[i].angle)
|
|
balls[j].angle = (2*tangent - balls[j].angle)
|
|
|
|
balls[i].x += (balls[i].speed)*sin(radians(angle))
|
|
balls[i].y -= (balls[i].speed)*cos(radians(angle))
|
|
balls[j].x -= (balls[j].speed)*sin(radians(angle))
|
|
balls[j].y += (balls[j].speed)*cos(radians(angle))
|
|
|
|
def border():
|
|
pygame.draw.rect(display, gray, (0, 0, width, 30))
|
|
pygame.draw.rect(display, gray, (0, 0, 30, height))
|
|
pygame.draw.rect(display, gray, (width - 30, 0, width, height))
|
|
pygame.draw.rect(display, gray, (0, height - 30, width, height))
|
|
|
|
def score():
|
|
font = pygame.font.SysFont("Agency FB", 30)
|
|
|
|
pygame.draw.rect(display, (51, 51, 51), (0, height, width, outerHeight))
|
|
for i in range(len(balls)):
|
|
balls[i].draw((i + 1)*2*(radius + 1), height + radius + 10)
|
|
|
|
text = font.render("Remaining Balls: " + str(len(balls)), True, stickColor)
|
|
display.blit(text, (width/2 + 50, height + radius/2))
|
|
|
|
|
|
def reset():
|
|
global balls, noBalls
|
|
noBalls = 15
|
|
balls = []
|
|
|
|
s = 70
|
|
|
|
b1 = Ball(s, height/2 - 4*radius, 0, colors[0], 0, 1)
|
|
b2 = Ball(s + 2*radius, height/2 - 3*radius, 0, colors[1], 0, 2)
|
|
b3 = Ball(s, height/2 - 2*radius, 0, colors[2], 0, 3)
|
|
b4 = Ball(s + 4*radius, height/2 - 2*radius, 0, colors[3], 0, 4)
|
|
b5 = Ball(s + 2*radius, height/2 - 1*radius, 0, colors[4], 0, 5)
|
|
b6 = Ball(s, height/2, 0, colors[5], 0, 6)
|
|
b7 = Ball(s + 6*radius, height/2 - 1*radius, 0, colors[6], 0, 7)
|
|
b8 = Ball(s + 4*radius, height/2, 0, colors[7], 0, 8)
|
|
b9 = Ball(s + 8*radius, height/2, 0, colors[8], 0, 9)
|
|
b10 = Ball(s + 6*radius, height/2 + 1*radius, 0, colors[9], 0, 10)
|
|
b11 = Ball(s + 2*radius, height/2 + 1*radius, 0, colors[10], 0, 11)
|
|
b12 = Ball(s, height/2 + 2*radius, 0, colors[11], 0, 12)
|
|
b13 = Ball(s + 4*radius, height/2 + 2*radius, 0, colors[12], 0, 13)
|
|
b14 = Ball(s + 2*radius, height/2 + 3*radius, 0, colors[13], 0, 14)
|
|
b15 = Ball(s, height/2 + 4*radius, 0, colors[14], 0, 15)
|
|
|
|
balls.append(b1)
|
|
balls.append(b2)
|
|
balls.append(b3)
|
|
balls.append(b4)
|
|
balls.append(b5)
|
|
balls.append(b6)
|
|
balls.append(b7)
|
|
balls.append(b8)
|
|
balls.append(b9)
|
|
balls.append(b10)
|
|
balls.append(b11)
|
|
balls.append(b12)
|
|
balls.append(b13)
|
|
balls.append(b14)
|
|
balls.append(b15)
|
|
|
|
|
|
|
|
def gameOver():
|
|
font = pygame.font.SysFont("Agency FB", 75)
|
|
if len(balls) == 0:
|
|
text = font.render("You Won!", True, (133, 193, 233))
|
|
else:
|
|
text = font.render("You Lost! Black in Hole!", True, (241, 148, 138))
|
|
|
|
while True:
|
|
for event in pygame.event.get():
|
|
if event.type == pygame.QUIT:
|
|
close()
|
|
if event.type == pygame.KEYDOWN:
|
|
if event.key == pygame.K_q:
|
|
close()
|
|
|
|
if event.key == pygame.K_r:
|
|
poolTable()
|
|
display.blit(text, (50, height/2))
|
|
|
|
pygame.display.update()
|
|
clock.tick()
|
|
|
|
def close():
|
|
pygame.quit()
|
|
sys.exit()
|
|
|
|
# Main Function
|
|
def poolTable():
|
|
loop = True
|
|
|
|
reset()
|
|
|
|
noPockets = 6
|
|
pockets = []
|
|
|
|
p1 = Pockets(0, 0, black)
|
|
p2 = Pockets(width/2 - p1.r*2, 0, black)
|
|
p3 = Pockets(width - p1.r - margin - 5, 0, black)
|
|
p4 = Pockets(0, height - margin - 5 - p1.r, black)
|
|
p5 = Pockets(width/2 - p1.r*2, height - margin - 5 - p1.r, black)
|
|
p6 = Pockets(width - p1.r - margin - 5, height - margin - 5 - p1.r, black)
|
|
|
|
pockets.append(p1)
|
|
pockets.append(p2)
|
|
pockets.append(p3)
|
|
pockets.append(p4)
|
|
pockets.append(p5)
|
|
pockets.append(p6)
|
|
|
|
cueBall = Ball(width/2, height/2, 0, white, 0, "cue")
|
|
cueStick = CueStick(0, 0, 100, stickColor)
|
|
|
|
|
|
start = 0
|
|
end = 0
|
|
|
|
while loop:
|
|
for event in pygame.event.get():
|
|
if event.type == pygame.QUIT:
|
|
close()
|
|
if event.type == pygame.KEYDOWN:
|
|
if event.key == pygame.K_q:
|
|
close()
|
|
|
|
if event.key == pygame.K_r:
|
|
poolTable()
|
|
|
|
if event.type == pygame.MOUSEBUTTONDOWN:
|
|
start = [cueBall.x, cueBall.y]
|
|
x, y = pygame.mouse.get_pos()
|
|
end = [x ,y]
|
|
dist = ((start[0] - end[0])**2 + (start[1] - end[1])**2)**0.5
|
|
force = dist/10.0
|
|
if force > 10:
|
|
force = 10
|
|
|
|
cueStick.applyForce(cueBall, force)
|
|
|
|
|
|
display.fill(background)
|
|
|
|
cueBall.draw(cueBall.x, cueBall.y)
|
|
cueBall.move()
|
|
|
|
if not (cueBall.speed > 0):
|
|
|
|
cueStick.draw(cueBall.x, cueBall.y)
|
|
|
|
for i in range(len(balls)):
|
|
balls[i].draw(balls[i].x, balls[i].y)
|
|
|
|
for i in range(len(balls)):
|
|
balls[i].move()
|
|
|
|
checkCollision()
|
|
checkCueCollision(cueBall)
|
|
border()
|
|
|
|
for i in range(noPockets):
|
|
pockets[i].draw()
|
|
|
|
for i in range(noPockets):
|
|
pockets[i].checkPut()
|
|
|
|
if len(balls) == 1 and balls[0].ballNum == 8:
|
|
gameOver()
|
|
|
|
score()
|
|
|
|
pygame.display.update()
|
|
clock.tick(60)
|
|
|
|
poolTable()
|