Загрузка данных
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()