breakout.py

Created by ilyas-r

Created on May 09, 2023

7.5 KB

Breakout


Python Game 🎮 v1.0 for NumWorks, all models.

By Ilyas R. May 2023.

A free project carried out as part of the computer science specialty education.

Learn more about Breakout on: nsi.xyz/breakout (FR)

Changelog

Breakout v1.0 - 09/05/2023:
- Initial version


from kandinsky import fill_rect as rec, draw_string as txt, get_pixel as get_p
from ion import keydown
from time import sleep

# Breakout 1.0 NumWorks, 09.04.2023
# Par Ilyas R.
# https://nsi.xyz/breakout <3

dark_c, light_c, bright_c, game_c, void_c = (40,)*3, (142,)*3, (242,)*3, (148, 113, 222), (255,)*3
red, orange, yellow, green = (240, 160, 160), (240, 200, 160), (240, 240, 160), (200, 240, 200)

p_size = 40
p_speed = 3
p_x, p_y = 160, 210
p_color = bright_c

b_size = 7
b_dir = [2, -2]
b_pos = [160, 180]

br_width = 30
br_height = 15

game_speed = 0.01
game_state = "IN_MENU"

chars = (121579, 186351, 234191, 188271, 187117, 252783, 252781, 74903)

points = {"red": 100, "orange": 80, "yellow": 60, "green": 20}
stage, score, pv = 1, 0, 3
level = ""
cuboids = []


def menu():
    rec(0, 0, 320, 222, dark_c)
    rec(0, 200, 320, 22, game_c)
    for k in range(len(chars)):  # Display method inspired by @riko_schraf
        for j in range(19):
            if chars[k] >> j & 1 == 1:
                rec(110 + 12 * k + (j % 3) * 3, 40 + (j // 3) * 3, 3, 3, light_c)
    txt("Score: " + str(score), 160-5*(7+len(max(str(score), str(stage)))), 70, bright_c, dark_c)
    txt("Stage: " + str(stage), 160-5*(7+len(max(str(score), str(stage)))), 90, bright_c, dark_c)
    txt("Lives: " + str(pv), 160-5*(7+len(max(str(score), str(stage)))), 110, bright_c, dark_c)
    if pv > 0:
        txt("Press OK to play", 80, 150, bright_c, dark_c)
    else:
        txt("GAME OVER", 115, 150, bright_c, dark_c)
    txt("Gameplay by nsi.xyz/breakout", 20, 202, bright_c, game_c)


def pad(size, move=0):
    rec(p_x + move + move * (size // 2), p_y, move * p_speed, 3, dark_c)
    rec(p_x - size // 2, p_y, size, 3, p_color)


def level_generator(stg):
    lvl = "".join([" " for _ in range(10)])+"*"*(stg//11+1)*10
    col = set()
    for i in (2, 3, 5, 7):
        if not stg % i:
            for j in range(i, 10, i):
                col.add(j)
    for i in range(stg // 11 + 2):
        for j in range(10):
            if j in col:
                lvl = lvl[:i * 10 + j] + " " + lvl[i * 10 + j + 1:]
    return lvl


def level_constructor():
    global level, cuboids
    rec(0, 0, 320, 222, dark_c)
    level = level_generator(stage)
    for i in range(len(level)):
        if level[i] == "*":
            rec(1 + (br_width + 2) * (i % 10), 1 + (br_height + 2) * (i // 10), br_width, br_height, set_color(i // 10))
    cuboids = [[[1+(br_width+2)*(i % 10), 1+(br_height+2)*(i//10)], [1 + (br_width + 2) * (i % 10) + br_width,
                1 + (br_height + 2) * (i // 10) + br_height]] if level[i] == "*" else [] for i in range(len(level))]
    cuboids += (((0, 0), (0, 222)), ((0, 0), (320, 0)), ((320, 0), (320, 222)))


def set_color(line):
    if line in (0, 1):
        return red
    elif line in (2, 3):
        return orange
    elif line in (4, 5):
        return yellow
    else:
        return green


def speed(stg, hb):
    return max(0.01-(hb*10**-4)-((stg-1)*10**-5), 0)


def new_level():
    global game_state, b_pos, p_x, p_y, b_dir, game_speed
    b_pos, b_dir = [160, 180], [2, -2]
    p_x, p_y = 160, 210
    game_state = "IN_LEVEL"
    level_constructor()
    game_speed = speed(stage, len(cuboids)-sum([1 if not i else 0 for i in cuboids])-3)
    pad(p_size)
    engine()


def velocity(x):
    vec = [(-2, -1), (-2, -2), (-1, -2), (1, -2), (2, -2), (2, -1)]
    i = min(round((x-p_x)/(p_size/2)*3+3), len(vec)-1)
    b_dir[0], b_dir[1] = vec[i]


def collision(x, y):
    global game_state
    if p_y-abs(b_dir[1])-b_size//2 <= y <= p_y+abs(b_dir[1])-b_size//2 and p_x-p_size//2 <= x <= p_x+p_size//2:
        velocity(x)
        rec(p_x - p_size // 2, p_y, p_size, 3, p_color)
    elif y >= 222:
        game_state = "G_OVER"
        return
    elif entity_around(x, y, 15):
        collision_manager(x, y)


def entity_around(x, y, radius):
    if get_p(x, y-radius) != dark_c or get_p(x-radius, y-radius) != dark_c or get_p(x+radius, y-radius) != dark_c:
        return True
    if get_p(x-radius, y) != dark_c or get_p(x+radius, y) != dark_c:
        return True
    if get_p(x, y+radius) != dark_c or get_p(x-radius, y+radius) != dark_c or get_p(x+radius, y+radius) != dark_c:
        return True
    return False


def collision_manager(x, y):
    for i in range(len(cuboids)):
        if cuboids[i] and (i < cuboids[i][0][1]+10 or len(cuboids)-3 <= i <= len(cuboids)):
            h = b_size//2
            x_s, y_s, x_e, y_e = cuboids[i][0][0]-h, cuboids[i][0][1]-h, cuboids[i][1][0]+h, cuboids[i][1][1]+h
            if y_s-abs(b_dir[1]) <= y <= y_s+abs(b_dir[1]) and x_s <= x <= x_e:
                b_dir[1] = -b_dir[1]
                destroy_brick(i)
            elif x_s-abs(b_dir[0]) <= x <= x_s+abs(b_dir[0]) and y_s <= y <= y_e:
                b_dir[0] = -b_dir[0]
                destroy_brick(i)
            elif x_e-abs(b_dir[0]) <= x <= x_e+abs(b_dir[0]) and y_s <= y <= y_e:
                b_dir[0] = -b_dir[0]
                destroy_brick(i)
            elif y_e-abs(b_dir[1]) <= y <= y_e+abs(b_dir[1]) and x_s <= x <= x_e:
                b_dir[1] = -b_dir[1]
                destroy_brick(i)


def get_color(i):
    li = i//10
    return "red" if li in (0, 1) else "orange" if li in (2, 3) else "yellow" if li in (4, 5) else "green"


def destroy_brick(i):
    global score, game_speed
    if type(cuboids[i]) == list:
        cuboids[i] = []
        rec(1 + (br_width + 2) * (i % 10), 1 + (br_height + 2) * (i // 10), br_width, br_height, dark_c)
        score += points[get_color(i)]
        game_speed = speed(stage, len(cuboids) - sum([1 if not i else 0 for i in cuboids]) - 3)


def ball(x, y, width, height, old=0):
    rec(x - width // 2, y - height // 2, width, height, dark_c if old else void_c)
    for i in [(-1, -1,), (-1, 1), (1, -1), (1, 1)]:
        rec(x + i[0] * (width // 2), y + i[1] * (height // 2), 1, 1, dark_c)


def ball_manager():
    old_ball_pos = (b_pos[0], b_pos[1])
    ball(b_pos[0], b_pos[1], b_size, b_size)
    b_pos[0] += b_dir[0]
    b_pos[1] += b_dir[1]
    collision(b_pos[0], b_pos[1])
    sleep(game_speed)
    ball(old_ball_pos[0], old_ball_pos[1], b_size, b_size, 1)


def engine():
    global p_x, pv, game_state, stage
    while game_state not in ("L_CLEAR", "G_OVER"):
        if keydown(0) and p_x > b_size + p_size // 2:
            p_x -= p_speed
            pad(p_size, 1)
        if keydown(3) and p_x < 320 - b_size - p_size // 2:
            p_x += p_speed
            pad(p_size, -1)
        ball_manager()
        if len(cuboids)-sum([1 if not i else 0 for i in cuboids]) == 3:
            game_state = "L_CLEAR"
            stage += 1
            pv += 1
    pv -= 1 if game_state == "G_OVER" else 0
    menu()


def refresh_scoreboard():
    txt("Stage: " + str(stage), 160-5*(7+len(max(str(score), str(stage)))), 90, bright_c, dark_c)


def breakout():
    global stage
    menu()
    last_key = -1
    while not keydown(51):
        if keydown(4) or keydown(52):
            new_level()
        if keydown(39) and last_key != 39:
            stage += 10
            last_key = 39
        if keydown(45) and last_key != 45:
            stage += 1
            last_key = 45
        if keydown(40) and last_key != 40:
            stage = max(stage-10, 1)
            last_key = 40
        if keydown(46) and last_key != 46:
            stage = max(stage-1, 1)
            last_key = 46
        if keydown(39) or keydown(45) or keydown(40) or keydown(46):
            refresh_scoreboard()
        if not (keydown(39) or keydown(45) or keydown(40) or keydown(46)):
            last_key = -1
        if pv == 0:
            menu()
            return


breakout()

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.