Загрузка данных


import pygame as pg
import random
import time
import sys
from pygame.locals import *

pg.init()
pg.mixer.init()

fps = 25
window_w, window_h = 600, 500
block = 20
cup_w, cup_h = 10, 20

side_margin = int((window_w - cup_w * block) / 2)
top_margin = window_h - (cup_h * block) - 5

black = (0, 0, 0)
white = (255, 255, 255)
gray = (128, 128, 128)
red = (255, 0, 0)
green = (0, 255, 0)
blue = (0, 0, 255)
cyan = (0, 255, 255)
magenta = (255, 0, 255)
yellow = (255, 255, 0)
orange = (255, 165, 0)

colors = (
    black,
    cyan,
    yellow,
    magenta,
    orange,
    blue,
    red,
    green
)
lightcolors = (
    black,
    (0, 200, 200),
    (200, 200, 0),
    (200, 0, 200),
    (200, 100, 0),
    (0, 0, 200),
    (200, 0, 0),
    (0, 200, 0)
)

empty = 'o'

figures = {
    'I': [
        ['ooooo', 'ooooo', 'oxxxo', 'ooooo', 'ooooo'],
        ['ooxoo', 'ooxoo', 'ooxoo', 'ooxoo', 'ooooo'],
        ['ooooo', 'ooooo', 'oxxxo', 'ooooo', 'ooooo'],
        ['ooxoo', 'ooxoo', 'ooxoo', 'ooxoo', 'ooooo']
    ],
    'O': [
        ['ooooo', 'ooooo', 'oxxoo', 'oxxoo', 'ooooo']
    ],
    'T': [
        ['ooooo', 'ooooo', 'oxxxo', 'ooxoo', 'ooooo'],
        ['ooooo', 'ooxoo', 'ooxxo', 'ooxoo', 'ooooo'],
        ['ooooo', 'ooxoo', 'oxxxo', 'ooooo', 'ooooo'],
        ['ooooo', 'ooxoo', 'ooxxo', 'ooxoo', 'ooooo']
    ],
    'S': [
        ['ooooo', 'ooooo', 'ooxxo', 'oxxoo', 'ooooo'],
        ['ooooo', 'ooxoo', 'ooxxo', 'oooxo', 'ooooo'],
        ['ooooo', 'ooooo', 'ooxxo', 'oxxoo', 'ooooo'],
        ['ooooo', 'ooxoo', 'ooxxo', 'oooxo', 'ooooo']
    ],
    'Z': [
        ['ooooo', 'ooooo', 'oxxoo', 'ooxxo', 'ooooo'],
        ['ooooo', 'oooxo', 'ooxxo', 'ooxoo', 'ooooo'],
        ['ooooo', 'ooooo', 'oxxoo', 'ooxxo', 'ooooo'],
        ['ooooo', 'oooxo', 'ooxxo', 'ooxoo', 'ooooo']
    ],
    'J': [
        ['ooooo', 'ooxoo', 'ooxoo', 'oxxxo', 'ooooo'],
        ['ooooo', 'oooxo', 'oooxo', 'ooxxo', 'ooooo'],
        ['ooooo', 'oxxxo', 'ooxoo', 'ooxoo', 'ooooo'],
        ['ooooo', 'ooxxo', 'ooxoo', 'ooxoo', 'ooooo']
    ],
    'L': [
        ['ooooo', 'ooxoo', 'ooxoo', 'oxxxo', 'ooooo'],
        ['ooooo', 'ooxxo', 'ooxoo', 'ooxoo', 'ooooo'],
        ['ooooo', 'oxxxo', 'ooxoo', 'ooxoo', 'ooooo'],
        ['ooooo', 'ooxoo', 'ooxoo', 'ooxxo', 'ooooo']
    ]
}

fig_colors = {
    'I': 1, 'O': 2, 'T': 3, 'S': 4, 'Z': 5, 'J': 6, 'L': 7
}

fig_w, fig_h = 5, 5

side_freq = 0.15
down_freq = 0.1
base_fall_speed = 0.5

def empty_cup():
    return [[empty for _ in range(cup_h)] for _ in range(cup_w)]

def convert_coords(x, y):
    return (side_margin + x * block, top_margin + y * block)

def draw_block(block_x, block_y, color, pixelx=None, pixely=None):
    if color == empty:
        return
    if pixelx is None and pixely is None:
        pixelx, pixely = convert_coords(block_x, block_y)
    pg.draw.rect(display_surf, colors[color], (pixelx + 1, pixely + 1, block - 1, block - 1), 0, 3)
    pg.draw.rect(display_surf, lightcolors[color], (pixelx + 1, pixely + 1, block - 4, block - 4), 0, 3)
    pg.draw.circle(display_surf, colors[color], (pixelx + block // 2, pixely + block // 2), 5)

def draw_fig(fig):
    shape = fig['shape']
    rotation = fig['rotation']
    color = fig['color']
    x = fig['x']
    y = fig['y']
    for i in range(fig_w):
        for j in range(fig_h):
            if figures[shape][rotation][j][i] == 'x':
                draw_block(x + i, y + j, color)

def draw_next_fig(fig):
    shape = fig['shape']
    rotation = fig['rotation']
    color = fig['color']
    start_x = cup_w * block + side_margin + 30
    start_y = top_margin + 100
    for i in range(fig_w):
        for j in range(fig_h):
            if figures[shape][rotation][j][i] == 'x':
                pixelx = start_x + i * block
                pixely = start_y + j * block
                draw_block(0, 0, color, pixelx, pixely)

def draw_cup(cup):
    for x in range(cup_w):
        for y in range(cup_h):
            if cup[x][y] != empty:
                draw_block(x, y, cup[x][y])

def draw_title():
    font = pg.font.Font(None, 36)
    text = font.render("TETRIS", True, white)
    text_rect = text.get_rect()
    text_rect.centerx = window_w // 2
    text_rect.y = top_margin - 40
    display_surf.blit(text, text_rect)

def draw_info(points, level):
    font = pg.font.Font(None, 24)
    text_score = font.render(f"Score: {points}", True, white)
    text_level = font.render(f"Level: {level}", True, white)
    text_score_rect = text_score.get_rect()
    text_level_rect = text_level.get_rect()
    text_score_rect.topright = (window_w - 20, top_margin + 10)
    text_level_rect.topright = (window_w - 20, top_margin + 40)
    display_surf.blit(text_score, text_score_rect)
    display_surf.blit(text_level, text_level_rect)

def draw_pause():
    pause_surf = pg.Surface((window_w, window_h), pg.SRCALPHA)
    pause_surf.fill((0, 0, 255, 127))
    display_surf.blit(pause_surf, (0, 0))

def is_completed(cup, y):
    for x in range(cup_w):
        if cup[x][y] == empty:
            return False
    return True

def clear_completed(cup):
    removed = 0
    y = cup_h - 1
    while y >= 0:
        if is_completed(cup, y):
            for push_down in range(y, 0, -1):
                for x in range(cup_w):
                    cup[x][push_down] = cup[x][push_down - 1]
            for x in range(cup_w):
                cup[x][0] = empty
            removed += 1
        else:
            y -= 1
    return removed

def add_to_cup(cup, fig):
    shape = fig['shape']
    rotation = fig['rotation']
    color = fig['color']
    x = fig['x']
    y = fig['y']
    for i in range(fig_w):
        for j in range(fig_h):
            if figures[shape][rotation][j][i] == 'x':
                cup[x + i][y + j] = color

def in_cup(x, y):
    return 0 <= x < cup_w and 0 <= y < cup_h

def check_pos(cup, fig, adjX=0, adjY=0):
    shape = fig['shape']
    rotation = fig['rotation']
    x = fig['x'] + adjX
    y = fig['y'] + adjY
    for i in range(fig_w):
        for j in range(fig_h):
            if figures[shape][rotation][j][i] == 'x':
                if not in_cup(x + i, y + j):
                    return False
                if cup[x + i][y + j] != empty:
                    return False
    return True

def get_new_fig():
    shape = random.choice(list(figures.keys()))
    return {
        'shape': shape,
        'rotation': 0,
        'color': fig_colors[shape],
        'x': cup_w // 2 - 2,
        'y': -2
    }

def calc_speed(points):
    level = points // 10 + 1
    fall_speed = base_fall_speed / level
    if fall_speed < 0.05:
        fall_speed = 0.05
    return level, fall_speed

def play_sound(sound_name):
    try:
        sound = pg.mixer.Sound(f"sounds/{sound_name}.wav")
        sound.play()
    except:
        pass

def show_text(text, font, color, x, y, center=True):
    text_surf = font.render(text, True, color)
    text_rect = text_surf.get_rect()
    if center:
        text_rect.center = (x, y)
    else:
        text_rect.topleft = (x, y)
    display_surf.blit(text_surf, text_rect)

def run_tetris(difficulty, player_name):
    global display_surf, fps_clock, base_fall_speed
    cup = empty_cup()
    points = 0
    level, fall_speed = calc_speed(points)
    falling_fig = get_new_fig()
    next_fig = get_new_fig()
    last_fall = time.time()
    last_side_move = time.time()
    last_move_down = time.time()
    going_left = False
    going_right = False
    going_down = False
    paused = False
    game_over = False

    if difficulty == "Easy":
        base_fall_speed = 0.6
    elif difficulty == "Medium":
        base_fall_speed = 0.4
    else:
        base_fall_speed = 0.25

    try:
        pg.mixer.music.load("sounds/background.mp3")
        pg.mixer.music.play(-1)
    except:
        pass

    while not game_over:
        for event in pg.event.get():
            if event.type == QUIT:
                stop_game()
            if event.type == KEYDOWN:
                if event.key == K_SPACE:
                    paused = not paused
                    play_sound("pause")
                if not paused:
                    if event.key == K_LEFT:
                        if check_pos(cup, falling_fig, adjX=-1):
                            falling_fig['x'] -= 1
                            going_left = True
                            going_right = False
                            last_side_move = time.time()
                    elif event.key == K_RIGHT:
                        if check_pos(cup, falling_fig, adjX=1):
                            falling_fig['x'] += 1
                            going_right = True
                            going_left = False
                            last_side_move = time.time()
                    elif event.key == K_UP:
                        old_rot = falling_fig['rotation']
                        falling_fig['rotation'] = (falling_fig['rotation'] + 1) % len(figures[falling_fig['shape']])
                        if not check_pos(cup, falling_fig):
                            falling_fig['rotation'] = old_rot
                        play_sound("rotate")
                    elif event.key == K_DOWN:
                        going_down = True
                        if check_pos(cup, falling_fig, adjY=1):
                            falling_fig['y'] += 1
                            last_move_down = time.time()
                            play_sound("move")
                    elif event.key == K_RETURN:
                        going_down = False
                        going_left = False
                        going_right = False
                        for i in range(1, cup_h):
                            if not check_pos(cup, falling_fig, adjY=i):
                                break
                        falling_fig['y'] += i - 1
                        play_sound("drop")
            elif event.type == KEYUP:
                if event.key == K_LEFT:
                    going_left = False
                elif event.key == K_RIGHT:
                    going_right = False
                elif event.key == K_DOWN:
                    going_down = False

        if not paused and not game_over:
            if (going_left or going_right) and time.time() - last_side_move > side_freq:
                if going_left and check_pos(cup, falling_fig, adjX=-1):
                    falling_fig['x'] -= 1
                elif going_right and check_pos(cup, falling_fig, adjX=1):
                    falling_fig['x'] += 1
                last_side_move = time.time()
            if going_down and time.time() - last_move_down > down_freq:
                if check_pos(cup, falling_fig, adjY=1):
                    falling_fig['y'] += 1
                    last_move_down = time.time()
                else:
                    going_down = False
            if time.time() - last_fall > fall_speed:
                if not check_pos(cup, falling_fig, adjY=1):
                    add_to_cup(cup, falling_fig)
                    lines = clear_completed(cup)
                    if lines > 0:
                        points += [0, 40, 100, 300, 1200][lines] * level
                        play_sound("clear")
                    level, fall_speed = calc_speed(points)
                    falling_fig = next_fig
                    next_fig = get_new_fig()
                    last_fall = time.time()
                    if not check_pos(cup, falling_fig):
                        game_over = True
                        play_sound("gameover")
                        pg.mixer.music.stop()
                else:
                    falling_fig['y'] += 1
                    last_fall = time.time()

        display_surf.fill(black)
        draw_title()
        draw_cup(cup)
        if falling_fig and not game_over:
            draw_fig(falling_fig)
        draw_next_fig(next_fig)
        draw_info(points, level)

        if paused:
            draw_pause()
            font = pg.font.Font(None, 48)
            show_text("PAUSED", font, white, window_w // 2, window_h // 2)

        pg.display.update()
        fps_clock.tick(fps)

    return points

def stop_game():
    pg.quit()
    sys.exit()

def menu():
    global display_surf, fps_clock
    display_surf = pg.display.set_mode((window_w, window_h))
    pg.display.set_caption("Tetris")
    fps_clock = pg.time.Clock()

    font_title = pg.font.Font(None, 64)
    font_text = pg.font.Font(None, 36)
    font_input = pg.font.Font(None, 32)

    input_active = False
    player_name = "Player"
    difficulty = "Medium"
    difficulties = ["Easy", "Medium", "Hard"]
    diff_index = 1

    buttons = {
        "start": pg.Rect(window_w//2 - 80, 280, 160, 40),
        "quit": pg.Rect(window_w//2 - 80, 350, 160, 40)
    }

    clock = pg.time.Clock()
    running = True

    while running:
        display_surf.fill(black)
        show_text("TETRIS", font_title, white, window_w//2, 70)
        show_text(f"Name: {player_name}", font_text, white, window_w//2, 150)
        show_text(f"Difficulty: {difficulty}", font_text, white, window_w//2, 200)

        mouse_pos = pg.mouse.get_pos()
        for btn_name, rect in buttons.items():
            color = gray if rect.collidepoint(mouse_pos) else white
            pg.draw.rect(display_surf, color, rect, 2)
            text = font_text.render(btn_name.capitalize(), True, color)
            text_rect = text.get_rect(center=rect.center)
            display_surf.blit(text, text_rect)

        show_text("Press ENTER to change name", font_input, gray, window_w//2, 420)
        show_text("Press LEFT/RIGHT to change difficulty", font_input, gray, window_w//2, 450)

        for event in pg.event.get():
            if event.type == QUIT:
                stop_game()
            if event.type == KEYDOWN:
                if event.key == K_RETURN:
                    input_active = True
                if event.key == K_LEFT:
                    diff_index = (diff_index - 1) % 3
                    difficulty = difficulties[diff_index]
                if event.key == K_RIGHT:
                    diff_index = (diff_index + 1) % 3
                    difficulty = difficulties[diff_index]
            if event.type == MOUSEBUTTONDOWN:
                if buttons["start"].collidepoint(mouse_pos):
                    score = run_tetris(difficulty, player_name)
                    show_game_over(score, player_name)
                    running = False
                if buttons["quit"].collidepoint(mouse_pos):
                    stop_game()

        if input_active:
            name = ""
            input_rect = pg.Rect(window_w//2 - 100, 170, 200, 30)
            while input_active:
                display_surf.fill(black)
                show_text("Enter your name:", font_text, white, window_w//2, 120)
                pg.draw.rect(display_surf, white, input_rect, 2)
                name_surf = font_input.render(name, True, white)
                display_surf.blit(name_surf, (input_rect.x+5, input_rect.y+5))
                pg.display.update()
                for ev in pg.event.get():
                    if ev.type == QUIT:
                        stop_game()
                    if ev.type == KEYDOWN:
                        if ev.key == K_RETURN:
                            if name.strip():
                                player_name = name.strip()
                            input_active = False
                        elif ev.key == K_BACKSPACE:
                            name = name[:-1]
                        else:
                            if len(name) < 15 and ev.unicode.isprintable():
                                name += ev.unicode
                clock.tick(30)
        pg.display.update()
        clock.tick(30)

def show_game_over(score, name):
    font = pg.font.Font(None, 48)
    font_small = pg.font.Font(None, 36)
    buttons = {
        "restart": pg.Rect(window_w//2 - 160, 350, 140, 40),
        "menu": pg.Rect(window_w//2 + 20, 350, 140, 40)
    }

    waiting = True
    while waiting:
        display_surf.fill(black)
        show_text("GAME OVER", font, red, window_w//2, 100)
        show_text(f"{name}, your score: {score}", font_small, white, window_w//2, 180)
        show_text(f"Final level: {score//10 + 1}", font_small, white, window_w//2, 230)

        mouse_pos = pg.mouse.get_pos()
        for btn_name, rect in buttons.items():
            color = gray if rect.collidepoint(mouse_pos) else white
            pg.draw.rect(display_surf, color, rect, 2)
            text = font_small.render(btn_name.capitalize(), True, color)
            text_rect = text.get_rect(center=rect.center)
            display_surf.blit(text, text_rect)

        for event in pg.event.get():
            if event.type == QUIT:
                stop_game()
            if event.type == MOUSEBUTTONDOWN:
                if buttons["restart"].collidepoint(mouse_pos):
                    waiting = False
                    menu()
                if buttons["menu"].collidepoint(mouse_pos):
                    waiting = False
                    menu()
        pg.display.update()
        fps_clock.tick(30)

if __name__ == "__main__":
    display_surf = pg.display.set_mode((window_w, window_h))
    pg.display.set_caption("Tetris")
    fps_clock = pg.time.Clock()
    menu()