pong.py

Created by azerlap652

Created on January 02, 2025

10.7 KB

Un jeu Pong codé par Aizeir > youtube.com/@aizeir


from math import *
from random import *
from kandinsky import *
from ion import *
from time import sleep, monotonic

# Window
W,H = 320,222
SIZE, WIN = (W,H), (0,0,W,H)
FONTW, FONTH = 10,18

# Utils
def clamp(m,x,M): return max(m,min(x,M))
def proba(n): return randint(1,n)==1
def ints(list): return tuple(int(x) for x in list)
def center_pos(rect): return rect[0]+rect[2]//2, rect[1]+rect[3]//2

def collide_x(r1,r2): return r1[0] < r2[0]+r2[2] and r2[0] < r1[0]+r1[2]
def collide_y(r1,r2): return r1[1] < r2[1]+r2[3] and r2[1] < r1[1]+r1[3]
def collide  (r1,r2): return collide_x(r1,r2) and collide_y(r1,r2)


# - colors
BLACK = (0, 0, 0)
DARK_GRAY = (95, 87, 79)
LIGHT_GRAY = (194, 195, 199)
WHITE = (255, 241, 232)
# 4-7
DARK_BLUE = (29, 43, 83)
BLUE = (41, 173, 255)
DARK_GREEN = (0, 135, 81)
GREEN = (0, 228, 54)
# 8-B
YELLOW = (255, 236, 39)
ORANGE = (255, 163, 0)
RED = (255, 0, 77)
BROWN = (171, 82, 54)
# C-F
PEACH = (255, 204, 170)
PINK = (255, 119, 168)
PURPLE = (126, 37, 83)
LAVENDER = (131, 118, 156)

COLORS = [BLACK, DARK_GRAY, LIGHT_GRAY, WHITE, DARK_BLUE, BLUE, DARK_GREEN, GREEN, YELLOW, ORANGE, RED, BROWN, PEACH, PINK, PURPLE, LAVENDER]

# Image module
def draw_rect(rect, color):
    fill_rect(int(rect[0]),int(rect[1]),int(rect[2]),int(rect[3]),color)

def fill(color=WHITE):
    draw_rect(WIN, color)

def draw_image(img,color=BLACK, X=0,Y=0):
    "( (x,y,w,h[,color]), ... )"
    for r in img:
        draw_rect((X+r[0],Y+r[1], r[2], r[3]), (r[4] if len(r) > 4 else color))

def draw_text(text, x, y, side="topleft", color=BLACK, background=WHITE):
    if "right" in side: x -= len(text)*FONTW
    elif not "left" in side: x -= len(text)*FONTW/2
    if "bottom" in side: y -= FONTH
    elif not "top" in side: y -= FONTH/2
    draw_string(text, int(round(x)),int(round(y)), color, background)


# Time (time: temps de la FRAME ! ne pas utiliser en dehors d'engine)
time,timers = 0,{}
def every(dt):
    if dt not in timers: timers[dt] = time
    return (time - timers[dt]) >= dt
    
def timer(t, dura):
    return (monotonic() - t) >= dura

# update
pressed = {}
just_pressed = {}

def update(keys=[KEY_OK]):
    """ keys: touches inscrites dans just_pressed. BIG ATTENTION: ne JAMAIS utiliser just_pressed dans un "every" """
    global time
    for dt in timers:
        if time-timers[dt] >= dt:
            timers[dt] = time
    time = monotonic()

    for key in keys:
        kd = keydown(key)
        just_pressed[key] = kd and not pressed.get(key)
        pressed[key] = kd

    return True


low_detail = False

def bracket(text, yesno): return ("  {}  ","[ {} ]")[yesno].format(text)

pong = "1111  11  1  1  111\n1  1 1  1 11 1 1    11\n111  1  1 1 11 1 11 11\n1     11  1  1  11".split("\n")
digits = [" 111 \n1   1\n1 1 1\n1   1\n 111 ", "   1\n   1\n   1\n   1\n   1", "1111 \n    1\n 111\n1   \n11111", "1111 \n    1\n 111 \n    1\n1111 ", "1   1\n1   1\n 1111\n    1\n    1", "11111\n1   \n1111 \n    1\n1111 ", " 111 \n1   \n1111\n1   1\n 111 ", "111  \n   1\n   1\n   1\n   1", " 111  \n1   1\n 111 \n1   1\n 111 ", " 111  \n1   1\n 1111\n    1\n 111 "]


# Game
def game(mode):
    bg = BLACK
    
    # Ball
    ballsize, detail = 10, 2
    ball = [50,50, ballsize,ballsize]
    if low_detail:
        ball_img = [(0,0,ballsize,ballsize)]
    else:
        ball_img = ((detail,0,ballsize-2*detail,ballsize), (0,detail,ballsize,ballsize-2*detail))
    vx = vy = def_speed = 3
    
    # Bars
    bar1 = [20,10,12,48]
    bar2 = [W-32,10,12,48]
    speed_coeff = 5
    bar1_speed = bar2_speed = bar_defspeed = def_speed/speed_coeff

    def move_bar(bar, dir):
        dy = max(detail,bar1_speed)
        y = (bar[1], bar[1]+bar[3]-dy)[dir<0]
        draw_rect((bar[0],y,bar[2],dy),bg)
        
        bar[1] += dir * bar1_speed
        bar[1] = clamp(0, bar[1], H-bar[3])

    def draw_bar(bar):
        color = (WHITE,YELLOW)[touching==bar]
        if low_detail:
            image = (bar,)
        else:
            image = ((bar[0]+detail,bar[1],bar[2]-2*detail,bar[3]), (bar[0],bar[1]+detail,bar[2],bar[3]-2*detail))
        draw_image(image, color)

    def bar_touched(bar):
        if not(bar==bar2 and mode==1):
            add_score()
        # touch
        nonlocal touching, just_touched, touch_time,vx
        touching = just_touched = bar
        touch_time = monotonic()
        # movement
        vx = -vx
        if bar==bar1:
            ball[0] = bar[0]+bar[2]
        else:
            ball[0] = bar[0]-ball[2]
        # draw
        draw_rect(bar, BLACK)
        for _ in range(6):
            particle(center_pos(ball), vel=[vx*random(), -10*(random()-.5)])

    # Touch
    just_touched = None
    touching = None
    touch_time = None
    
    # Score
    score = 0
    px = 4
    font = 5
    score_rect = None

    def add_score():
        nonlocal score, vx, vy, bar1_speed,bar2_speed,bar_defspeed,score_rect
        score += 1
        speed = def_speed + def_speed//2 * (score//10)/((score//10)+1)
        vx = copysign(speed, vx)
        vy = copysign(speed, vy)
        bar1_speed = bar1_speed * (speed/speed_coeff) / .5
        bar2_speed = bar2_speed * (speed/speed_coeff) / .5
        bar_defspeed = speed/speed_coeff
        draw_score(score)

    def draw_score(score):
        nonlocal score_rect
        w = ((score//10+1)*(font+1)-1)*px
        score_rect = ((W-w)/2, 20, w, font*px)
        
        draw_rect(score_rect,bg)
        for k,s in enumerate(str(score)):
            img = digits[int(s)].strip("\n").split("\n")
            for i,row in enumerate(img):
                for j,cell in enumerate(row):
                    if cell == " ": continue
                    draw_rect((score_rect[0]+(k*(font+1)+j)*px, score_rect[1]+i*px, px, px), YELLOW)

    # Game Over
    def draw_lose_texts(select):
        draw_text(bracket("retry",select==0), W/2,H*.45,"center",(WHITE,RED)[select==0],bg)
        draw_text(bracket("menu" ,select==1), W/2,H*.55,"center",(WHITE,RED)[select==1],bg)

    # Particles
    particles = []
    def particle(pos,**k):
        p = {'pos':list(pos),'time':monotonic()}
        p.update(k)
        particles.append(p)

    # Draw
    fill(bg)
    draw_bar(bar1)
    draw_bar(bar2)
    draw_score(score)

    # Update
    while update():
        # Ball
        if every(.02):
            # Draw bg
            draw_rect(ball,bg)
            if collide(ball, score_rect):
                draw_score(score)

            # Move
            ball[0] += vx
            ball[1] += vy
        
            # Bar collision
            if collide(ball, bar1):
                bar_touched(bar1)
            elif collide(ball, bar2):
                bar_touched(bar2)
            # Wall collision
            elif ball[0] <= 0 or ball[0]+ball[2] >= W:
                break
            elif ball[1] <= 0 or ball[1]+ball[3] >= H:
                vy = -vy
                ball[1] = clamp(0, ball[1], H-ball[3])
                for _ in range(6): particle(center_pos(ball), vel=[10*(random()-.5),vy*random()])
            
            # Ball clamp
            ball[0] = clamp(0, ball[0], W-ball[2])
            ball[1] = clamp(0, ball[1], H-ball[3])

            # Draw
            draw_image(ball_img,(RED,RED)[touching!=None], ball[0], ball[1])

        # Bar left
        dir1 = keydown(KEY_DOWN) - keydown(KEY_UP)
        if dir1: move_bar(bar1, dir1)
        bar1_speed = (bar_defspeed, min(1,bar1_speed+.05))[dir1]
        
        # Bar right
        dir2 = (dir1, (-1,1)[center_pos(ball)[1]>center_pos(bar2)[1]])[mode==1]
        if dir2: move_bar(bar2, dir2)
        bar2_speed = (bar_defspeed,min(1,bar2_speed+.05))[dir2]
    
        # Touch timer
        touch_end = touching and timer(touch_time, .2)
        if touch_end: touching = None

        # Draw Bars
        if dir1 or (touch_end or just_touched == bar1): draw_bar(bar1)
        if dir2 or (touch_end or just_touched == bar2): draw_bar(bar2)
        just_touched = None

        # Particles
        if every(.01) and particles:
            bin = []
            for p in particles:
                # cache
                draw_rect((p['pos'][0],p['pos'][1],3,3), bg)

                # move
                p['pos'][0] += p['vel'][0]
                p['pos'][1] += p['vel'][1]
                p['vel'][1] += .2

                # particle destroy
                r = (p['pos'][0],p['pos'][1],3,3)
                if timer(p['time'], 1) or (p['pos'][1] > H+5) or collide(r,bar1) or collide(r,bar2) or collide(r,score_rect): bin.append(p); continue
                
                # draw
                draw_rect((p['pos'][0],p['pos'][1],3,3), YELLOW)
            
            for p in bin: particles.remove(p)

    # Game Over
    draw_lose_texts(0)
    select = 0
    input = keydown(KEY_DOWN)-keydown(KEY_UP)
    while update(keys=(KEY_DOWN,KEY_UP)):
        if just_pressed[KEY_UP] and select>0 and not input:
            select = (select-1)
            draw_lose_texts(select)
        if just_pressed[KEY_DOWN] and select<1 and not input:
            select = (select+1)
            draw_lose_texts(select)
        if keydown(KEY_OK):
            if select == 0: break
            elif select == 1: menu()
        if input != keydown(KEY_DOWN)-keydown(KEY_UP):
            input = 0
    game(mode)


# Menu
def menu():
    global low_detail
    bg = BLACK
    
    # texts
    prev_input = keydown(KEY_OK)
    select = 0
    def draw_texts():
        draw_text(bracket('solo',select==0), W/2,H*.5,"center",(WHITE,RED)[select==0],bg)
        draw_text(bracket('vs cpu',select==1), W/2,H*.6,"center",(WHITE,RED)[select==1],bg)
        #draw_text(bracket('2 player',select==2), W/2,H*.7,"center",(WHITE,RED)[select==2],bg)
    def draw_shift():
        draw_text(bracket('shift | low detail',low_detail), W/2,H*.9,"center",(WHITE,RED)[low_detail],bg)
    
    # title
    px = 5
    w = max([len(row) for row in pong])
    h = len(pong)
    x = (W - w*px) / 2
    y = H*.2
    t = monotonic()
    frame = 0

    # draw
    fill(bg)
    draw_texts()
    draw_shift()

    # Loop
    while update(keys=(KEY_SHIFT,KEY_DOWN,KEY_UP)):
        # Title
        if frame <= h*w and (monotonic()-t)**.5*100 > frame:
            for i,row in enumerate(pong):
                for j,cell in enumerate(row):
                    if j*h+i == frame and cell != " ":
                        draw_rect((x+j*px, y+i*px, px, px), RED)
            frame += 1

        # Events
        if keydown(KEY_OK) and not prev_input:
            game(select)
        if prev_input and not keydown(KEY_OK):
            prev_input = False
        if just_pressed[KEY_DOWN] and select<1:
            select = (select+1)
            draw_texts()
        if just_pressed[KEY_UP] and select>0:
            select = (select-1)
            draw_texts()
        if just_pressed[KEY_SHIFT]:
            low_detail = not low_detail
            draw_shift()

# Start
menu()

During your visit to our site, NumWorks needs to install "cookies" or use other technologies to collect data about you in order to:

With the exception of Cookies essential to the operation of the site, NumWorks leaves you the choice: you can accept Cookies for audience measurement by clicking on the "Accept and continue" button, or refuse these Cookies by clicking on the "Continue without accepting" button or by continuing your browsing. You can update your choice at any time by clicking on the link "Manage my cookies" at the bottom of the page. For more information, please consult our cookies policy.