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


https://drive.google.com/drive/u/0/folders/1IcvS0LwuyEIEuzeF_hav0L_I3g0_cnXQ

import random
import sys
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.graphics import Color, Rectangle, Line
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.core.audio import SoundLoader

# --- ШАБЛОНЫ ФИГУР ---
S = [['.....', '.....', '..00.', '.00..', '.....'], ['.....', '..0..', '..00.', '...0.', '.....']]
Z = [['.....', '.....', '.00..', '..00.', '.....'], ['.....', '...0.', '..00.', '..0..', '.....']]
I = [['..0..', '..0..', '..0..', '..0..', '.....'], ['.....', '0000.', '.....', '.....', '.....']]
O = [['.....', '.....', '.00..', '.00..', '.....']]
J = [['.....', '.0...', '.000.', '.....', '.....'], ['.....', '..00.', '..0..', '..0..', '.....'], ['.....', '.....', '.000.', '...0.', '.....'], ['.....', '..0..', '..0..', '.00..', '.....']]
L = [['.....', '...0.', '.000.', '.....', '.....'], ['.....', '..0..', '..0..', '..00.', '.....'], ['.....', '.....', '.000.', '.0...', '.....'], ['.....', '.00..', '..0..', '..0..', '.....']]
T = [['.....', '..0..', '.000.', '.....', '.....'], ['.....', '..0..', '..00.', '..0..', '.....'], ['.....', '.....', '.000.', '..0..', '.....'], ['.....', '..0..', '.00..', '..0..', '.....']]

SHAPES = [S, Z, I, O, J, L, T]
COLORS = [
    (0, 1, 0.6, 1),    # Салатовый
    (1, 0.2, 0.4, 1),  # Розово-красный
    (0, 0.8, 1, 1),    # Голубой
    (1, 0.8, 0, 1),    # Золотой
    (1, 0.6, 0, 1),    # Оранжевый
    (0.4, 0.4, 1, 1),  # Синий
    (0.8, 0.3, 1, 1)   # Фиолетовый
]

class TetrisGame(Widget):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.cols, self.rows = 10, 20
        self.block_size = 28
        
        self.grid = [[None for _ in range(self.cols)] for _ in range(self.rows)]
        self.score = 0
        self.level = 1
        
        # Загрузка звуков (оставлены твои названия файлов)
        self.bg_music = SoundLoader.load('bg_music.mp3.mp3')
        self.drop_sound = SoundLoader.load('drop.mp3.mp3')
        self.level_up_sound = SoundLoader.load('level_up.mp3.mp3')
        
        if self.bg_music:
            self.bg_music.loop = True
            self.bg_music.volume = 0.5
            
        self.current_piece = self.get_new_piece()
        self.next_piece = self.get_new_piece()
        self.held_piece = None
        self.can_hold = True
        
        self.score_label = Label(text="SCORE: 0 | LEVEL: 1", font_size=18, bold=True, color=(1, 1, 1, 1))
        self.hold_label = Label(text="HOLD (C)\nROT (Z)", font_size=14, color=(0, 1, 1, 1), halign='center')
        self.next_label = Label(text="NEXT", font_size=14, color=(0, 1, 1, 1))
        
        self.add_widget(self.score_label)
        self.add_widget(self.hold_label)
        self.add_widget(self.next_label)
        
        self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
        self._keyboard.bind(on_key_down=self._on_keyboard_down)
        
        self.fall_speed = 0.4
        self.event = None  # Ивент запускается только когда экран активен

    def start_game(self):
        if not self.event:
            self.event = Clock.schedule_interval(self.update, self.fall_speed)
        if self.bg_music:
            self.bg_music.play()

    def pause_game(self):
        if self.event:
            self.event.cancel()
            self.event = None
        if self.bg_music:
            self.bg_music.stop()

    def get_new_piece(self):
        shape = random.choice(SHAPES)
        color = COLORS[SHAPES.index(shape)]
        return {'x': 3, 'y': 0, 'shape': shape, 'rotation': 0, 'color': color}

    def convert_shape(self, piece):
        positions = []
        format = piece['shape'][piece['rotation'] % len(piece['shape'])]
        for i, line in enumerate(format):
            for j, column in enumerate(list(line)):
                if column == '0':
                    positions.append((piece['x'] + j, piece['y'] + i))
        return [(p[0], p[1] - 2) for p in positions]

    def valid_space(self, piece):
        for x, y in self.convert_shape(piece):
            if x < 0 or x >= self.cols or y >= self.rows:
                return False
            if y >= 0 and self.grid[y][x] is not None:
                return False
        return True

    def lock_piece(self):
        if self.drop_sound: self.drop_sound.play()
            
        for x, y in self.convert_shape(self.current_piece):
            if y >= 0:
                self.grid[y][x] = self.current_piece['color']
            else:
                self.game_over()
                return
                
        lines_cleared = 0
        for i in range(self.rows - 1, -1, -1):
            if None not in self.grid[i]:
                del self.grid[i]
                self.grid.insert(0, [None for _ in range(self.cols)])
                lines_cleared += 1
                
        if lines_cleared > 0:
            self.score += lines_cleared * 15
            new_level = self.score // 100 + 1
            
            if new_level > self.level:
                self.level = new_level
                if self.level_up_sound: self.level_up_sound.play()
                    
            self.score_label.text = f"SCORE: {self.score} | LEVEL: {self.level}"
            self.fall_speed = max(0.08, 0.4 - (self.level - 1) * 0.04)
            
            if self.event:
                self.event.cancel()
                self.event = Clock.schedule_interval(self.update, self.fall_speed)
            
        self.current_piece = self.next_piece
        self.next_piece = self.get_new_piece()
        self.can_hold = True

    def update(self, dt):
        self.current_piece['y'] += 1
        if not self.valid_space(self.current_piece):
            self.current_piece['y'] -= 1
            self.lock_piece()
        self.draw_board()

    def game_over(self):
        if self.event:
            self.event.cancel()
            self.event = None
        if self.bg_music:
            self.bg_music.stop()
        self.score_label.text = f"GAME OVER! SCORE: {self.score}"
        self.score_label.color = (1, 0.2, 0.2, 1)

    def _keyboard_closed(self):
        self._keyboard.unbind(on_key_down=self._on_keyboard_down)
        self._keyboard = None

    def _on_keyboard_down(self, keyboard, keycode, text, modifiers):
        if not self.event: return True  # Не реагируем на кнопки, если игра на паузе
        
        key = keycode[1]
        if key == 'left':
            self.current_piece['x'] -= 1
            if not self.valid_space(self.current_piece): self.current_piece['x'] += 1
        elif key == 'right':
            self.current_piece['x'] += 1
            if not self.valid_space(self.current_piece): self.current_piece['x'] -= 1
        elif key == 'down':
            self.current_piece['y'] += 1
            if not self.valid_space(self.current_piece): self.current_piece['y'] -= 1
            self.score += 1
        elif key == 'up':
            self.current_piece['rotation'] += 1
            if not self.valid_space(self.current_piece): self.current_piece['rotation'] -= 1
        elif key == 'z':
            self.current_piece['rotation'] -= 1  # Крутим в обратную сторону
            if not self.valid_space(self.current_piece): self.current_piece['rotation'] += 1
        elif key == 'spacebar':
            while self.valid_space(self.current_piece):
                self.current_piece['y'] += 1
            self.current_piece['y'] -= 1
            self.lock_piece()
        elif key == 'c' and self.can_hold:
            if self.held_piece is None:
                self.held_piece = {'x': 3, 'y': 0, 'shape': self.current_piece['shape'], 'rotation': 0, 'color': self.current_piece['color']}
                self.current_piece = self.next_piece
                self.next_piece = self.get_new_piece()
            else:
                temp = {'x': 3, 'y': 0, 'shape': self.current_piece['shape'], 'rotation': 0, 'color': self.current_piece['color']}
                self.current_piece = {'x': 3, 'y': 0, 'shape': self.held_piece['shape'], 'rotation': 0, 'color': self.held_piece['color']}
                self.held_piece = temp
            self.can_hold = False
            
        self.draw_board()
        return True

    def draw_mini_piece(self, piece, offset_x, offset_y):
        if not piece: return
        Color(*piece['color'])
        format = piece['shape'][piece['rotation'] % len(piece['shape'])]
        p_size = 14
        for i, line in enumerate(format):
            for j, column in enumerate(list(line)):
                if column == '0':
                    rect_y = offset_y - i * p_size
                    rect_x = offset_x + j * p_size
                    Rectangle(pos=(rect_x, rect_y), size=(p_size - 1, p_size - 1))

    def draw_board(self):
        self.canvas.before.clear()
        self.canvas.clear()
        
        self.score_label.size = self.score_label.texture_size
        self.score_label.pos = (Window.width // 2 - self.score_label.width // 2, Window.height - 60)
        
        self.hold_label.size = self.hold_label.texture_size
        self.hold_label.pos = (40, Window.height - 120)
        
        self.next_label.size = self.next_label.texture_size
        self.next_label.pos = (Window.width - 80, Window.height - 120)
        
        board_w = self.cols * self.block_size
        board_h = self.rows * self.block_size
        offset_x = (Window.width - board_w) / 2
        offset_y = (Window.height - board_h) / 2 - 40

        with self.canvas:
            Color(0.08, 0.08, 0.12, 1)
            Rectangle(pos=(0, 0), size=(Window.width, Window.height))
            
            Color(0.12, 0.12, 0.18, 1)
            Rectangle(pos=(offset_x, offset_y), size=(board_w, board_h))
            
            Color(0.2, 0.8, 1, 1)
            Line(rectangle=(offset_x, offset_y, board_w, board_h), width=1.2)

            for y in range(self.rows):
                for x in range(self.cols):
                    if self.grid[y][x]:
                        Color(*self.grid[y][x])
                        rect_y = offset_y + (self.rows - 1 - y) * self.block_size
                        rect_x = offset_x + x * self.block_size
                        Rectangle(pos=(rect_x + 1, rect_y + 1), size=(self.block_size - 2, self.block_size - 2))

            Color(*self.current_piece['color'])
            for x, y in self.convert_shape(self.current_piece):
                if y >= 0:
                    rect_y = offset_y + (self.rows - 1 - y) * self.block_size
                    rect_x = offset_x + x * self.block_size
                    Rectangle(pos=(rect_x + 1, rect_y + 1), size=(self.block_size - 2, self.block_size - 2))
                    
            if self.held_piece:
                self.draw_mini_piece(self.held_piece, 30, Window.height - 150)
            self.draw_mini_piece(self.next_piece, Window.width - 90, Window.height - 150)


# --- ЭКРАНЫ ПРИЛОЖЕНИЯ ---

class MenuScreen(Screen):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        
        # Темный фон меню
        with self.canvas.before:
            Color(0.08, 0.08, 0.12, 1)
            self.bg = Rectangle(pos=self.pos, size=Window.size)
        self.bind(pos=self.update_bg, size=self.update_bg)

        layout = BoxLayout(orientation='vertical', padding=60, spacing=25)
        
        title = Label(text="TETRIS", font_size=58, bold=True, color=(0, 1, 0.8, 1), size_hint=(1, 0.4))
        
        btn_play = Button(text="Играть", font_size=24, bold=True, background_color=(0, 0.8, 0.6, 1), color=(1,1,1,1), size_hint=(1, 0.2))
        btn_play.bind(on_press=self.start_game)
        
        btn_settings = Button(text="Настройки", font_size=20, background_color=(0.3, 0.4, 0.5, 1), color=(1,1,1,1), size_hint=(1, 0.2))
        btn_settings.bind(on_press=self.go_settings)
        
        btn_exit = Button(text="Выход", font_size=20, background_color=(0.8, 0.2, 0.2, 1), color=(1,1,1,1), size_hint=(1, 0.2))
        btn_exit.bind(on_press=self.exit_app)
        
        layout.add_widget(title)
        layout.add_widget(btn_play)
        layout.add_widget(btn_settings)
        layout.add_widget(btn_exit)
        self.add_widget(layout)
        
    def update_bg(self, *args):
        self.bg.pos = self.pos
        self.bg.size = self.size

    def start_game(self, instance):
        self.manager.current = 'game'
        
    def go_settings(self, instance):
        self.manager.current = 'settings'
        
    def exit_app(self, instance):
        sys.exit()


class SettingsScreen(Screen):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        
        with self.canvas.before:
            Color(0.08, 0.08, 0.12, 1)
            self.bg = Rectangle(pos=self.pos, size=Window.size)
        self.bind(pos=self.update_bg, size=self.update_bg)

        layout = BoxLayout(orientation='vertical', padding=60, spacing=20)
        
        title = Label(text="НАСТРОЙКИ\n(В разработке)", font_size=32, bold=True, halign="center", color=(1, 1, 1, 1), size_hint=(1, 0.6))
        
        btn_back = Button(text="Назад", font_size=20, background_color=(0.4, 0.4, 0.4, 1), size_hint=(1, 0.2))
        btn_back.bind(on_press=self.go_back)
        
        layout.add_widget(title)
        layout.add_widget(btn_back)
        self.add_widget(layout)

    def update_bg(self, *args):
        self.bg.pos = self.pos
        self.bg.size = self.size

    def go_back(self, instance):
        self.manager.current = 'menu'


class GameScreen(Screen):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        layout = FloatLayout()
        self.game_widget = TetrisGame()
        
        # Кнопка возврата поверх игры (мобильный UI)
        self.btn_back = Button(
            text="< Меню", 
            font_size=14,
            bold=True,
            size_hint=(None, None), 
            size=(80, 40), 
            pos_hint={'x': 0.02, 'top': 0.98},
            background_color=(0.8, 0.2, 0.2, 1),
            color=(1, 1, 1, 1)
        )
        self.btn_back.bind(on_press=self.go_back)
        
        layout.add_widget(self.game_widget)
        layout.add_widget(self.btn_back)
        self.add_widget(layout)

    def on_enter(self):
        # Запускаем логику и музыку только когда перешли на экран игры
        self.game_widget.start_game()
        
    def on_leave(self):
        # Ставим на паузу при выходе в меню
        self.game_widget.pause_game()

    def go_back(self, instance):
        self.manager.current = 'menu'


class TetrisApp(App):
    def build(self):
        Window.size = (480, 760)
        
        sm = ScreenManager()
        sm.add_widget(MenuScreen(name='menu'))
        sm.add_widget(GameScreen(name='game'))
        sm.add_widget(SettingsScreen(name='settings'))
        
        return sm

if __name__ == '__main__':
    TetrisApp().run()