Как сделать тетрис на python
Перейти к содержимому

Как сделать тетрис на python

  • автор:

Как ребенок может сделать игру тетрис на Python

В этой статье мы расскажем о том, как с помощью специального модуля Pygame даже ребенок сможет создать «Тетрис» на Python.

Онлайн-школа программирования для детей Pixel

Тетрис — популярная аркадная игра, которая была разработана и выпущена русским программистом Алексеем Пажитновым в 1984 году. Она быстро стала мировым хитом и получила огромную популярность по всему миру.

Суть игры довольно проста, но именно это позволяет ей вызывать интерес у людей и по сей день. К тому же ее можно создать самому, используя возможности языка программирования Python («Питон»).

Если программирование для школьников вам не знакомо — не беда. Мы пошагово расскажем о том, что нужно делать для создания игры. Главное — повторять все со 100% точностью. Если вам удобнее воспринимать урок в формате видео, то под статьей мы прикрепили ролик, где преподаватель школы Pixel подробно объясняет, что необходимо установить, запустить и нажать, чтобы ваш проект заработал.

Python для школьников: создаем свой «Тетрис» с нуля

Напоминаем, что для начала кодинга игр на «Пайтоне» нам понадобится модуль Pygame. Для его установки следует запустить Windows PowerShell (приложение доступно по поиску при нажатии кнопки «Пуск») и прописать без кавычек «Pip install pygame», после этого нажать Enter.

Начинаем «Тетрис» на Python с импорта необходимых библиотек: pygame, random, copy. Особенно нам важна библиотека copy, так как она позже поможет нам в создании последних штрихов игры.

После этого запускаем Visual Studio Code, paygame и задаем два параметра: I_max и J_max.

Для первого параметра указываем число 11, а для второго — 21. В итоге у нас появляется 11 и 20 ячеек.

Создание «Тетрис» на Python для детей: добавляем переменные

Следующий шаг для того, чтобы ребенку сделать игру самому — создание переменных. Мы должны добавить их с параметрами экрана 300 на 600.

Затем приступим к формированию экрана, где указываем переменные и описываем класс Clock (), который будет отвечать за управление временем, а в нашем случае — за частоту его обновления. Также установим название экрана.

Кроме того, чтобы самому сделать игру «Тетрис» нужно вычислить ширину и высоту каждой ячейки нашей сетки. Делим параметр screen на I_max — 1 и J_max — 1. Это объясняется тем, что каждая точка в области соответствует одной ячейке, и количество точек на единицу меньше количества ячеек. Для определения частоты обновления кадров мы создаем переменную, равную 60.

Если вы читаете текст здесь и у вас что-то не получается, то внизу страницы будет указан полный код для запуска проекта. Проверьте все данные или скопируйте часть параметров для самопроверки.

Продолжаем онлайн-урок на Python для детей: запускаем цикл

Теперь предстоит создать список, в котором будут храниться настройки сетки (именно она и будет отображаться на экране).

Также с помощью цикла мы проверим все схемы сетки, включив их во все списки. Внутрь него достаточно установить только единицу, после чего мы укажем остальные параметры для каждой ячейки.

В следующем цикле заполняем существующие списки сущностями прямоугольников — rect, и указываем цвет для каждого. В итоге у вас получится большой список для каждой ячейки. Также требуется прописать координаты прямоугольников с указанием их местоположения — применяем умножение номера точки на шаг.

По завершении запускаем бесконечный цикл.

Создание игры на Python с нуля: заливка экрана

После запуска бесконечного цикла нам предстоит заняться заливкой холста с его покадровым обновлением, а также создать сетку.

Необходимо пройтись по всем прописанным строчками и создать рисунок каждого квадрата.

Здесь двойка — это цвет, единица — сущность уже существующего четырехугольника, а ноль — это тип заполнения. В заполнении цифра 1 создаст границу, а 0 сделает заливку сплошной. После этого добавляем обновление.

«Тетрис» для детей на Python: детали

В верхнем правом углу нажимаем на треугольник (Play), который отобразит написанный код через консоль. Должна быть видна белая сетка на черном фоне. Если это так — вы на правильном пути.

Для более правильной работы кода добавим событие «нажатие на крестик». Это прописывается ниже строки game=True.

Чтобы вспомнить о том, какие фигуры (детали) используются в игре, с ними можно ознакомиться в интернете, например, в «Википедии». Всего в «Тетрисе» используются 7 элементов, каждый из которых состоит из 4 кубов.

Рисовать детали мы будем интересным способом: запишем координаты шагов относительно центра, то есть точки 00, и далее будем исходить из того, что 3-й элемент — это 00.

Следующий шаг — сделать список с сущностями деталей. С этой целью мы создаем список det.

Каждую фигуру необходимо наполнить сущностями четырех квадратов. При этом координаты мы будем брать из ранее созданного списка. Умножаем их на шаг и ставим в центр экрана сверху.

Теперь можно вывести список в консоль для проверки. В итоге внизу экрана можно будет увидеть информацию о наличии 7 деталей, в каждой из которых будет по 4 квадратика.

Сделаем случайный выбор детали из нашего списка и настроим отрисовку детали в программе. Напишем условие, чтобы на каждый кадр запускался цикл по четырем прямоугольникам внутри случайно выбранной детали. То есть в каждый кадр будет прорисовано 4 прямоугольника из списка случайно выбранной детали. Запускаем консоль для проверки — должна появиться деталь вверху экрана. При каждом новом запуске консоли будет появляться случайная фигура — так и должно быть.

Настройка движения детали в игре на языке Python для детей

Для настройки движения детали, перед ее отрисовкой мы должны снова написать последовательность, которая будет проходиться по четырем элементам. Движение будет происходить очень просто: нужно взять det_choice и поменять координаты у всех четырех квадратов на шаг dx и dy.

Можно, например, прописать строчку 1*dx. Если после этого запустить консоль (напоминаем о кнопке со значком треугольника в верхнем правом углу), то фигура будет двигаться очень быстро вправо, едва заметно. Если поменять значение на минус один, произойдет быстрое смещение влево.

Далее следует создать еще один event, который сработает при нажатии кнопки. Сейчас, нажав на кнопку (которая должна сдвигать фигуру влево), мы меняем значение delta_x на -1. Для правой стрелки делаем аналогично, только со значением +1. Проверяем. Фигуры должны передвигаться более плавно.

Следующий пункт — настройка границ, запрет выхода из них. Добавьте условие, что если фигура «пробралась» за грань рабочей области, delta_x будет обнулено и движение не произойдет.

Теперь необходимо настроить аналогичное перемещение по оси Y. Если до этого момента вы все сделали правильно, то фигура после запуска консоли должна быстро переместиться сверху вниз. Чтобы это исправить, важно включить перемещение только на каждые 30 обновлений. Теперь после запуска консоли фигура будет достаточно медленно передвигаться сверху вниз.

Добавление ускорения и границ по оси Y

В уроке от онлайн-школы Pixel мы решили рассказать о том, как добавить ускорение падения фигуры, чтобы это еще больше напоминало оригинальный «Тетрис». Чтобы клавишу можно было зажать, требуется запустить get_pressed(). Если нажатая кнопка совпадает с pygame. K_DOWN (кнопка, назначенная на движение вниз), то число обновлений должно быть равно 31.

Если сейчас проверить функцию и нажать стрелку вниз, фигура «улетит» за нижнюю границу консоли. Чтобы этого не происходило, обозначим границы экрана по оси Y. После настройки фигура остановится на нижней границе. Проверить это легко: запускаем консоль и дожидаемся падения фигуры.

После остановки движения важно, чтобы вверху консоли появилась новая фигура. Для этого необходимо закрасить клетки сетки (где осталась деталь) белым цветом. То есть вы должны задать значение заливки и сделать его равным нулю, а цвет поменять с серого на белый. После чего необходимо настроить новый выбор случайного элемента.

Чтобы закрасить клетки, необходимо определиться с индексами клеток. Следует записать две переменные X и Y — и уже в этих переменных менять заданные параметры. После чего прописываем условие для выбора случайной детали. Проверьте написанный код через консоль — детали должны падать сверху вниз, как в «Тетрисе».

В итоге становится заметна одна проблема — во время игры выпадают исключительно семь фигур, а новые не появляются.

Это стало итогом того, как именно происходит сохранение информации при использовании языка программирования «Питон». Когда менялись данные о местоположении, менялся и список. Чтобы исключить проблему, важно сделать копию фигур из списка и изменить данные местоположения копии, а не оригинальной детали. Именно поэтому потребуются возможности библиотеки copy, о которой мы писали в самом начале.

После этого снова потребуется запуск консоли для проверки. Если все выполнено верно, новые фигуры появляются в количестве более 7 штук. Но возникает новая проблема — они не ставятся друг на друга!

Так как в онлайн-школе Pixel преподают программирование для детей, то мы знаем способ, с помощью которого любой ребенок сможет справиться с этой ситуацией. Добавляем еще одно условие нижней грани — если нижняя ячейка имеет цвет, то мы останавливаем движение. В результате фигура остается на месте.

На этом этапе основные игровые механики уже реализованы, но требуется добавить некоторые функции и особенности, которые есть в оригинальной игре. Самые важные моменты, которые должны быть реализованы — поворот фигур и «сжигание» заполненного ряда.

Добавляем возможность поворота в будущую игру

Вы уже находитесь на финишной прямой создания игры для детей на Python. Сейчас мы расскажем, как добавить 2 важные игровые механики, после чего можно будет поиграть в свой собственный «Тетрис».

Начнем с поворота — сделаем его по стандартной математической формуле поворота на 90 градусов в декартовой системе координат. То есть относительно центра в (0,0). Центр у вас должен быть записан в третьей ячейке, поэтому прописываем как центр второй индекс det_choice [2]. При этом поворот мы привязываем к нажатию стрелки вверх.

Также необходимо создать еще одну проверку на нажатие клавиши стрелки вверх — при ее нажатии переменная rotate становится равной True. Таким образом выполняется условие и мы проходимся по всем элементам наших деталей и смещаем их относительно центра вращения, после чего переписываем rotate на False. Проверяем. Если все выполнено верно, фигуры должны поворачиваться и заполнять пустые места «на дне». Но если фигуры сложились в один ряд, этот ряд не исчезнет. Чтобы он исчезал, пропишем еще несколько условий.

Для этого запустим цикл, который будет проверять насколько у нас заполнен каждый ряд. Будем идти снизу вверх и слева направо, и считать число заполненных ячеек. Они будут заполнены, когда параметр заполнения равен нулю. Но если хотя бы одна ячейка не будет равна нулю, это значит, что вся полоска уже точно не заполнена, и можно завершить цикл на этом ряду и проверять следующий.

Теперь добавляем условие — если число заполненных ячеек равно числу ячеек в целом, то мы запускаем еще два цикла. Сначала все ячейки верхнего ряда делаем незакрашенными, а после, в следующем цикле, все ячейки рядов смещаем на один ряд вниз, начиная с того ряда, где мы нашли заполненность.

Запускаем финальную проверку. Если все сделано верно, то вы сможете поиграть в игру, которую написали сами!

Теперь вы знаете, как именно проходит обучение детей IT в школе программирования Pixel. Мы даем задания, которые интересно и полезно выполнять будущим программистам. Больше уроков вы можете на нашем Youtube-канале.

Если вы хотите, чтобы ваш ребенок изучал востребованные навыки и в будущем смог стать IT-специалистом, отправляйте его на курсы обучения в нашу IT онлайн-школу. Здесь он сможет изучить Python, Roblox, Scratch и многое другое. Курсы проходят как в онлайн, так и в офлайн-формате, в группах или индивидуально. Подходящий вариант найдется для каждого.

Полностью сегодняшний и многие другие уроки в видео-формате можно посмотреть тут.

Полный код игры «Тетрис» на Python:

import pygame import random import copy pygame.init() I_max = 11 J_max = 21 screen_x = 300 screen_y = 600 screen = pygame.display.set_mode((screen_x, screen_y)) clock = pygame.time.Clock() pygame.display.set_caption(«Tetris Pixel Game») dx = screen_x/(I_max — 1) dy = screen_y/(J_max — 1) fps = 60 grid = [] for i in range(0, I_max): grid.append([]) for j in range(0, J_max): grid[i].append([1]) for i in range(0, I_max): for j in range(0, J_max): grid[i][j].append(pygame.Rect(i*dx, j*dy, dx, dy)) grid[i][j].append(pygame.Color(«Gray»)) details = [ [[-2, 0], [-1, 0], [0, 0], [1, 0]], [[-1, 1], [-1, 0], [0, 0], [1, 0]], [[1, 1], [-1, 0], [0, 0], [1, 0]], [[-1, 1], [0, 1], [0, 0], [-1, 0]], [[1, 0], [1, 1], [0, 0], [-1, 0]], [[0, 1], [-1, 0], [0, 0], [1, 0]], [[-1, 1], [0, 1], [0, 0], [1, 0]], ] det = [[],[],[],[],[],[],[]] for i in range(0, len(details)): for j in range(0, 4): det[i].append(pygame.Rect(details[i][j][0]*dx + dx*(I_max//2), details[i][j][1]*dy, dx, dy)) detail = pygame.Rect(0, 0, dx, dy) det_choice = copy.deepcopy(random.choice(det)) count = 0 game = True rotate = False while game: delta_x = 0 delta_y = 1 for event in pygame.event.get(): if event.type == pygame.QUIT: exit() if event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: delta_x = -1 elif event.key == pygame.K_RIGHT: delta_x = 1 elif event.key == pygame.K_UP: rotate = True key = pygame.key.get_pressed() if key[pygame.K_DOWN]: count = 31 * fps screen.fill(pygame.Color(«Black»)) for i in range(0, I_max): for j in range(0, J_max): pygame.draw.rect(screen, grid[i][j][2], grid[i][j][1], grid[i][j][0]) #границы for i in range(4): if ((det_choice[i].x + delta_x * dx < 0) or (det_choice[i].x + delta_x * dx >= screen_x)): delta_x = 0 if ((det_choice[i].y + dy >= screen_y) or (grid[int(det_choice[i].x//dx)][int(det_choice[i].y//dy) + 1][0] == 0)): delta_y = 0 for i in range(4): x = int(det_choice[i].x // dx) y = int(det_choice[i].y // dy) grid[x][y][0] = 0 #закрашиваем квадратик grid[x][y][2] = pygame.Color(«White») detail.x = 0 detail.y = 0 det_choice = copy.deepcopy(random.choice(det)) #передвижение по x for i in range(4): det_choice[i].x += delta_x*dx count += fps #передвижение по y if count > 30 * fps: for i in range(4): det_choice[i].y += delta_y*dy count = 0 for i in range(4): detail.x = det_choice[i].x detail.y = det_choice[i].y pygame.draw.rect(screen, pygame.Color(«White»), detail) C = det_choice[2] #центр СК if rotate == True: for i in range(4): x = det_choice[i].y — C.y y = det_choice[i].x — C.x det_choice[i].x = C.x — x det_choice[i].y = C.y + y rotate = False for j in range(J_max — 1, -1, -1): #цикл по рядам снизу вверх count_cells = 0 for i in range(0, I_max): #цикл по столбцам справа налево if grid[i][j][0] == 0: count_cells += 1 elif grid[i][j][0] == 1: break if count_cells == (I_max — 1): for l in range(0, I_max): grid[l][0][0] = 1 #все ячейки первого ряда for k in range(j, -1, -1): for l in range(0, I_max): grid[l][k][0] = grid[l][k-1][0] pygame.display.flip() clock.tick(fps)

Создание игры «Тетрис» в PyQt5 [Урок №12]

16 марта 2015 г. Archy Просмотров: 30992 RSS 11
Изучение PyQt5 PyQt, QColor, QFrame, QMainWindow, QPainter, QtCore, QtGui, QtWidgets, showMessage, statusBar, примеры PyQt

Создание игры «Тетрис» в PyQt5

Игра Тетрис – одна из самых популярных когда-либо созданных компьютерных игр. Оригинальная игра была разработана и запрограммирована русским программистом Алексеем Пажитновым в 1985 году. С тех пор, Тетрис доступен на почти каждой компьютерной платформе в множестве вариаций.

Тетрисом называется игра-головоломка с падающими блоками. В этой игре, мы имеет семь разных фигур, называемых как: S-фигура, Z-фигура, T-фигура, L-фигура, фигура-линия, фигура «Зеркальная L», и фигура-квадрат. Каждая из этих фигур формируется с помощью четырёх квадратиков. Фигуры падают вниз на доску. Цель игры Тетрис – перемещать и вращать фигуры так, чтобы их приземлилось как можно больше. Если мы сумеем сформировать ряд, ряд разрушается и мы получаем очки. Мы играем в Тетрис до тех пор, пока мы не достигнем верха.

Тетромино

Рисунок: Тетромино

PyQt5 – инструментарий, разработанный для создания приложений. Существуют другие библиотеки, которые нацелены на создание компьютерный игр. Несмотря на это, PyQt5 и другие библиотеки для приложений могут быть использованы для создания простых игр. Создание компьютерной игры – отличный способ повышения навыков программирования.

Разработка

Мы не имеем изображений для нашего Тетриса, мы рисуем тетромино, используя доступное в программном инструментарии PyQt5 API рисования. Позади каждой компьютерной игры, имеется математическая модель. Так же и в Тетрисе.

Некоторые идеи, применяющиеся в игре:

Мы используем QtCore.QBasicTimer(), чтобы создать игровой цикл.

Тетромино рисуются

Фигуры перемещаются по принципу «кубик за кубиком» (не «пиксель за пикселем»).

Математически, доска – это просто список чисел.

Код содержит четыре класса: Tetris, Board, Tetrominoe и Shape. Класс Tetris организовывает игру. Board – это то, где пишется игровая логика. Класс Tetrominoe содержит имена всех частей тетриса и класс Shape содержит код для частей тетриса. Для генерации случайной фигуры мы импортировали модуль random для генерации случайных данных в Python.

#!/usr/bin/python3 # -*- coding: utf-8 -*- import sys, random from PyQt5.QtWidgets import QMainWindow, QFrame, QDesktopWidget, QApplication from PyQt5.QtCore import Qt, QBasicTimer, pyqtSignal from PyQt5.QtGui import QPainter, QColor class Tetris(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): self.tboard = Board(self) self.setCentralWidget(self.tboard) self.statusbar = self.statusBar() self.tboard.msg2Statusbar[str].connect(self.statusbar.showMessage) self.tboard.start() self.resize(180, 380) self.center() self.setWindowTitle('Tetris') self.show() def center(self): screen = QDesktopWidget().screenGeometry() size = self.geometry() self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2) class Board(QFrame): msg2Statusbar = pyqtSignal(str) BoardWidth = 10 BoardHeight = 22 Speed = 300 def __init__(self, parent): super().__init__(parent) self.initBoard() def initBoard(self): self.timer = QBasicTimer() self.isWaitingAfterLine = False self.curX = 0 self.curY = 0 self.numLinesRemoved = 0 self.board = [] self.setFocusPolicy(Qt.StrongFocus) self.isStarted = False self.isPaused = False self.clearBoard() def shapeAt(self, x, y): return self.board[(y * Board.BoardWidth) + x] def setShapeAt(self, x, y, shape): self.board[(y * Board.BoardWidth) + x] = shape def squareWidth(self): return self.contentsRect().width() // Board.BoardWidth def squareHeight(self): return self.contentsRect().height() // Board.BoardHeight def start(self): if self.isPaused: return self.isStarted = True self.isWaitingAfterLine = False self.numLinesRemoved = 0 self.clearBoard() self.msg2Statusbar.emit(str(self.numLinesRemoved)) self.newPiece() self.timer.start(Board.Speed, self) def pause(self): if not self.isStarted: return self.isPaused = not self.isPaused if self.isPaused: self.timer.stop() self.msg2Statusbar.emit("paused") else: self.timer.start(Board.Speed, self) self.msg2Statusbar.emit(str(self.numLinesRemoved)) self.update() def paintEvent(self, event): painter = QPainter(self) rect = self.contentsRect() boardTop = rect.bottom() - Board.BoardHeight * self.squareHeight() for i in range(Board.BoardHeight): for j in range(Board.BoardWidth): shape = self.shapeAt(j, Board.BoardHeight - i - 1) if shape != Tetrominoe.NoShape: self.drawSquare(painter, rect.left() + j * self.squareWidth(), boardTop + i * self.squareHeight(), shape) if self.curPiece.shape() != Tetrominoe.NoShape: for i in range(4): x = self.curX + self.curPiece.x(i) y = self.curY - self.curPiece.y(i) self.drawSquare(painter, rect.left() + x * self.squareWidth(), boardTop + (Board.BoardHeight - y - 1) * self.squareHeight(), self.curPiece.shape()) def keyPressEvent(self, event): if not self.isStarted or self.curPiece.shape() == Tetrominoe.NoShape: super(Board, self).keyPressEvent(event) return key = event.key() if key == Qt.Key_P: self.pause() return if self.isPaused: return elif key == Qt.Key_Left: self.tryMove(self.curPiece, self.curX - 1, self.curY) elif key == Qt.Key_Right: self.tryMove(self.curPiece, self.curX + 1, self.curY) elif key == Qt.Key_Down: self.tryMove(self.curPiece.rotateRight(), self.curX, self.curY) elif key == Qt.Key_Up: self.tryMove(self.curPiece.rotateLeft(), self.curX, self.curY) elif key == Qt.Key_Space: self.dropDown() elif key == Qt.Key_D: self.oneLineDown() else: super(Board, self).keyPressEvent(event) def timerEvent(self, event): if event.timerId() == self.timer.timerId(): if self.isWaitingAfterLine: self.isWaitingAfterLine = False self.newPiece() else: self.oneLineDown() else: super(Board, self).timerEvent(event) def clearBoard(self): for i in range(Board.BoardHeight * Board.BoardWidth): self.board.append(Tetrominoe.NoShape) def dropDown(self): newY = self.curY while newY > 0: if not self.tryMove(self.curPiece, self.curX, newY - 1): break newY -= 1 self.pieceDropped() def oneLineDown(self): if not self.tryMove(self.curPiece, self.curX, self.curY - 1): self.pieceDropped() def pieceDropped(self): for i in range(4): x = self.curX + self.curPiece.x(i) y = self.curY - self.curPiece.y(i) self.setShapeAt(x, y, self.curPiece.shape()) self.removeFullLines() if not self.isWaitingAfterLine: self.newPiece() def removeFullLines(self): numFullLines = 0 rowsToRemove = [] for i in range(Board.BoardHeight): n = 0 for j in range(Board.BoardWidth): if not self.shapeAt(j, i) == Tetrominoe.NoShape: n = n + 1 if n == 10: rowsToRemove.append(i) rowsToRemove.reverse() for m in rowsToRemove: for k in range(m, Board.BoardHeight): for l in range(Board.BoardWidth): self.setShapeAt(l, k, self.shapeAt(l, k + 1)) numFullLines = numFullLines + len(rowsToRemove) if numFullLines > 0: self.numLinesRemoved = self.numLinesRemoved + numFullLines self.msg2Statusbar.emit(str(self.numLinesRemoved)) self.isWaitingAfterLine = True self.curPiece.setShape(Tetrominoe.NoShape) self.update() def newPiece(self): self.curPiece = Shape() self.curPiece.setRandomShape() self.curX = Board.BoardWidth // 2 + 1 self.curY = Board.BoardHeight - 1 + self.curPiece.minY() if not self.tryMove(self.curPiece, self.curX, self.curY): self.curPiece.setShape(Tetrominoe.NoShape) self.timer.stop() self.isStarted = False self.msg2Statusbar.emit("Game over") def tryMove(self, newPiece, newX, newY): for i in range(4): x = newX + newPiece.x(i) y = newY - newPiece.y(i) if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight: return False if self.shapeAt(x, y) != Tetrominoe.NoShape: return False self.curPiece = newPiece self.curX = newX self.curY = newY self.update() return True def drawSquare(self, painter, x, y, shape): colorTable = [0x000000, 0xCC6666, 0x66CC66, 0x6666CC, 0xCCCC66, 0xCC66CC, 0x66CCCC, 0xDAAA00] color = QColor(colorTable[shape]) painter.fillRect(x + 1, y + 1, self.squareWidth() - 2, self.squareHeight() - 2, color) painter.setPen(color.lighter()) painter.drawLine(x, y + self.squareHeight() - 1, x, y) painter.drawLine(x, y, x + self.squareWidth() - 1, y) painter.setPen(color.darker()) painter.drawLine(x + 1, y + self.squareHeight() - 1, x + self.squareWidth() - 1, y + self.squareHeight() - 1) painter.drawLine(x + self.squareWidth() - 1, y + self.squareHeight() - 1, x + self.squareWidth() - 1, y + 1) class Tetrominoe(object): NoShape = 0 ZShape = 1 SShape = 2 LineShape = 3 TShape = 4 SquareShape = 5 LShape = 6 MirroredLShape = 7 class Shape(object): coordsTable = ( ((0, 0), (0, 0), (0, 0), (0, 0)), ((0, -1), (0, 0), (-1, 0), (-1, 1)), ((0, -1), (0, 0), (1, 0), (1, 1)), ((0, -1), (0, 0), (0, 1), (0, 2)), ((-1, 0), (0, 0), (1, 0), (0, 1)), ((0, 0), (1, 0), (0, 1), (1, 1)), ((-1, -1), (0, -1), (0, 0), (0, 1)), ((1, -1), (0, -1), (0, 0), (0, 1)) ) def __init__(self): self.coords = [[0,0] for i in range(4)] self.pieceShape = Tetrominoe.NoShape self.setShape(Tetrominoe.NoShape) def shape(self): return self.pieceShape def setShape(self, shape): table = Shape.coordsTable[shape] for i in range(4): for j in range(2): self.coords[i][j] = table[i][j] self.pieceShape = shape def setRandomShape(self): self.setShape(random.randint(1, 7)) def x(self, index): return self.coords[index][0] def y(self, index): return self.coords[index][1] def setX(self, index, x): self.coords[index][0] = x def setY(self, index, y): self.coords[index][1] = y def minX(self): m = self.coords[0][0] for i in range(4): m = min(m, self.coords[i][0]) return m def maxX(self): m = self.coords[0][0] for i in range(4): m = max(m, self.coords[i][0]) return m def minY(self): m = self.coords[0][1] for i in range(4): m = min(m, self.coords[i][1]) return m def maxY(self): m = self.coords[0][1] for i in range(4): m = max(m, self.coords[i][1]) return m def rotateLeft(self): if self.pieceShape == Tetrominoe.SquareShape: return self result = Shape() result.pieceShape = self.pieceShape for i in range(4): result.setX(i, self.y(i)) result.setY(i, -self.x(i)) return result def rotateRight(self): if self.pieceShape == Tetrominoe.SquareShape: return self result = Shape() result.pieceShape = self.pieceShape for i in range(4): result.setX(i, -self.y(i)) result.setY(i, self.x(i)) return result if __name__ == '__main__': app = QApplication([]) tetris = Tetris() sys.exit(app.exec_())

Игра немного упрощается для более легкого понимания. Игра начинается сразу же после её запуска. Мы можем приостановить игру, нажимая клавишу p. Клавиша Space будет немедленно бросать блок тетриса на дно. Игра идёт на постоянной скорости, ускорение не реализуется. Очки – это число линий, который мы удалили.

self.tboard = Board(self) self.setCentralWidget(self.tboard)

Экземпляр класса Board создаётся и устанавливается так, чтобы быть центральным виджетом приложения.

self.statusbar = self.statusBar() self.tboard.msg2Statusbar[str].connect(self.statusbar.showMessage)

Мы создаём строку состояния, где мы будем отображать сообщения. Мы будем отображать три возможных сообщения: количество уже удалённых линий, сообщение паузы, или сообщение «Игра окончена». msgStatusbar – это пользовательский сигнал, который реализуется в классе Board. showMessage() – это встроенный метод, который отображает сообщение в строке состояния.

self.tboard.start()

Эта строка инициирует игру.

class Board(QFrame): msg2Statusbar = pyqtSignal(str) . 

Создаётся пользовательский сигнал. msgStatusbar – это сигнал, который срабатывает, когда мы хотим написать сообщение или очки в строку состояния.

BoardWidth = 10 BoardHeight = 22 Speed = 300

Это переменные класса Board. BoardWidth и BoardHeight определяют размер доски в блоках. Speed определяет скорость игры. Каждые 300 мс будет начинаться цикл новой игры.

. self.curX = 0 self.curY = 0 self.numLinesRemoved = 0 self.board = [] . 

В методе initBoard() мы инициализируем несколько важных переменных. Переменная self.board – это список чисел от 0 до 7. Она представляет местоположение различных фигур и оставляет фигуры на доске.

def shapeAt(self, x, y): return self.board[(y * Board.BoardWidth) + x]

Метод shapeAt() определяет тип фигуры в данном блоке.

def squareWidth(self): return self.contentsRect().width() // Board.BoardWidth

Доска может динамически менять размер. Как следствие, размер блока может меняться. squareWidth() вычисляет ширину простого квадратика в пикселях и возвращает её. Board.BoardWidth – это размер доски в блоках.

for i in range(Board.BoardHeight): for j in range(Board.BoardWidth): shape = self.shapeAt(j, Board.BoardHeight - i - 1) if shape != Tetrominoe.NoShape: self.drawSquare(painter, rect.left() + j * self.squareWidth(), boardTop + i * self.squareHeight(), shape)

Рисование игры разделяется на два шага. Первым шагом, мы рисуем все фигуры, или оставляем фигуры, которые были сброшены на дно доски. Все квадратики запоминаются в списке переменных self.board. Доступ к переменной получают, используя метод shapeAt().

if self.curPiece.shape() != Tetrominoe.NoShape: for i in range(4): x = self.curX + self.curPiece.x(i) y = self.curY - self.curPiece.y(i) self.drawSquare(painter, rect.left() + x * self.squareWidth(), boardTop + (Board.BoardHeight - y - 1) * self.squareHeight(), self.curPiece.shape())

Следующий шаг – это рисование упавших вниз частей.

elif key == Qt.Key_Right: self.tryMove(self.curPiece, self.curX + 1, self.curY)

В методе keyPressEvent(), мы проверяем нажатые клавиши. Если мы нажали клавишу правой стрелки, мы пробуем передвинуть часть вправо. Мы говорим «пробуем», поскольку часть может быть не способна перемещаться.

elif key == Qt.Key_Up: self.tryMove(self.curPiece.rotateLeft(), self.curX, self.curY)

Клавиша стрелки вверх будет поворачивать падающую часть влево.

elif key == Qt.Key_Space: self.dropDown()

Клавиша «Пробел» будет немедленно бросать падающую часть на дно.

elif key == Qt.Key_D: self.oneLineDown()

Нажимая клавишу «d», часть спустится вниз на один блок. Это может быть использовано, чтобы слегка ускорить падение части.

def tryMove(self, newPiece, newX, newY): for i in range(4): x = newX + newPiece.x(i) y = newY - newPiece.y(i) if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight: return False if self.shapeAt(x, y) != Tetrominoe.NoShape: return False self.curPiece = newPiece self.curX = newX self.curY = newY self.update() return True

В методе tryMove(), мы пробуем переместить наши фигуры. Если фигура находится на краю доски или примыкает к некоторой другой части, мы возвращаем значение «Ложь». В противном случае, мы перемещаем текущую падающую часть в новую позицию.

def timerEvent(self, event): if event.timerId() == self.timer.timerId(): if self.isWaitingAfterLine: self.isWaitingAfterLine = False self.newPiece() else: self.oneLineDown() else: super(Board, self).timerEvent(event)

В событии таймера, мы либо создаём новую часть после предыдущей части, что упала на дно, либо мы передвигаем падающую часть на одну линию вниз.

def clearBoard(self): for i in range(Board.BoardHeight * Board.BoardWidth): self.board.append(Tetrominoe.NoShape)

Метод clearBoard() очищает доску путём установки Tetrominoe.Noshape на каждый блок доски.

def removeFullLines(self): numFullLines = 0 rowsToRemove = [] for i in range(Board.BoardHeight): n = 0 for j in range(Board.BoardWidth): if not self.shapeAt(j, i) == Tetrominoe.NoShape: n = n + 1 if n == 10: rowsToRemove.append(i) rowsToRemove.reverse() for m in rowsToRemove: for k in range(m, Board.BoardHeight): for l in range(Board.BoardWidth): self.setShapeAt(l, k, self.shapeAt(l, k + 1)) numFullLines = numFullLines + len(rowsToRemove) . 

Если часть ударяет дно, мы вызываем метод removeFullLines(). Мы обнаруживаем все полные линии и удаляем их. Мы делаем это, передвигая все линии выше на текущую полную линию, что удаляется на одну линию вниз. Обратите внимание, что мы развернули порядок удаляемых линий. В противном случае, это не будет работать правильно. В нашем случае, мы используем нехитрую гравитацию. Это означает, что части могут парить над пустыми промежутками.

def newPiece(self): self.curPiece = Shape() self.curPiece.setRandomShape() self.curX = Board.BoardWidth // 2 + 1 self.curY = Board.BoardHeight - 1 + self.curPiece.minY() if not self.tryMove(self.curPiece, self.curX, self.curY): self.curPiece.setShape(Tetrominoe.NoShape) self.timer.stop() self.isStarted = False self.msg2Statusbar.emit("Game over")

Метод newPiece() случайным образом создаёт новую часть тетриса. Если часть не может прийти в свою начальную позицию, игра заканчивается.

class Tetrominoe(object): NoShape = 0 ZShape = 1 SShape = 2 LineShape = 3 TShape = 4 SquareShape = 5 LShape = 6 MirroredLShape = 7

Класс Tetrominoe содержит в себе имена всех возможных фигур. Мы также имеем NoShape для пустого пространства.

Класс Shape хранит информацию о частях тетриса.

class Shape(object): coordsTable = ( ((0, 0), (0, 0), (0, 0), (0, 0)), ((0, -1), (0, 0), (-1, 0), (-1, 1)), . ) . 

Набор coordsTable содержит в себе все возможные значения координат наших частей тетриса. Это шаблон, из которого все части берут свои значения координат.

self.coords = [[0,0] for i in range(4)]

После создания, мы создаём пустой список координат. Список будет хранить координаты частей тетриса.

Координаты

Рисунок: Координаты

Изображение выше поможет понять значения координат немного больше. Для примера, набор (0, -1), (0, 0), (-1, 0), (-1, -1) представляет Z-фигуру. Схема иллюстрирует фигуру.

def rotateLeft(self): if self.pieceShape == Tetrominoe.SquareShape: return self result = Shape() result.pieceShape = self.pieceShape for i in range(4): result.setX(i, self.y(i)) result.setY(i, -self.x(i)) return result

Метод rotateLeft() поворачивает часть влево. Квадрат не должен поворачиваться. Вот почему мы просто возвращаем ссылку на текущий объект. Новая часть создаётся и её координаты устанавливаются в одну из повернутых частей.

Тетрис

Рисунок: Тетрис

Это была игра Тетрис в PyQt5.

Еще записи по теме

  • Рисование в PyQt5 [Урок №10]
  • События и сигналы в PyQt5 [Урок №5]
  • Создание собственного виджета в PyQt5 [Урок №11]
  • Диалоги в PyQt5 [Урок №6]
  • Перетаскивание «Drag’n’drop» в PyQt5 [Урок №9]
  • Виджеты PyQt5 [Урок №7]
  • Меню и панели инструментов в PyQt5 [Урок №3]

6 развлекательных проектов на Python: от шаверма-бота до игры в слова

Все, кто кодят на Python, знают, что это далеко не скучный и серьезный язык. При определенной доле фантазии на нем можно написать логику работы любого приложения, даже совершенно гиковского. И есть немало примеров, когда такие пет-проекты становились не только полем развития навыков разработчика, но и коммерческой идеей. Под катом собрали шесть подобных задач от Selectel — для вдохновения и прокачки навыков. Сохраняйте статью в закладки: пригодится всем, кто разрабатывает на Python.

Выберите интересующий вас проект:

Shawarma as a service

Суть проекта

Полтора года назад в Selectel появилась традиция есть шаверму по четвергам. Акция, названная Шавадеем, быстро обрела популярность. Но с увеличением количества участников ее организационные моменты — в частности, сбор и отправка заказов — становились все сложнее. Чтобы решить эту проблему, наш разработчик Вова написал Telegram-бота, который автоматизирует организацию события.

Для создания TG-бота автор использовал фреймворк Python Telegram Bot 20.0, а для веб-интерфейса — FastAPI. На тот момент как раз вышло обновление WebApp. С его помощью Вова заменил меню DurgerKing’а на директивы для шаблонизатора Jinja2 и добавил страницу с описанием каждой позиции.

Что вы узнаете из текста

  • Откуда пошла традиция есть шаверму по четвергам.
  • С каким проблемами в разработке столкнулся наш разработчик и как с ними боролся.
  • Сколько людей остались без еды за все время работы бота.

«Minecraft» на Python

Суть проекта

Если вам надоело просто играть в Minecraft, предлагаем создать собственный мир из кубов с помощью библиотеки Ursina Engine. В статье автор показывает, как работать с объектами Entity, настроить взаимодействие с предметами и добавить элементы «атмосферы» в игровой процесс. Отдельное внимание он уделил генерации Minecraft-подобного мира. Для этого воспользовался матрицей из шумов Перлина, которая «отрисовывает» ландшафт игры.

Сама разработка довольно простая, поскольку движок консолидирует всю рутинную работу. Разработчику остается только импортировать необходимые объекты и проработать логику игры.

Что вы узнаете из текста

  • Как написать простой GUI игры на базе Ursina Engine.
  • Как сгенерировать «бесконечный» ландшафт, не понизив FPS.
  • Как добавить наблюдателя с помощью встроенного объекта FirstPersonController.

Мод для Cyberpunk 2077

Суть проекта

Наши коллеги решили сделать амбициозный проект — VR-жилет, который «проецирует» ощущения урона персонажа на тело игрока. Чтобы осуществить эту идею, им нужен был мод, который отправляет данные из Cyberpunk 2077 на сам жилет. Однако информации по созданию моддинга мало, не говоря уже о готовых решениях. Поэтому решили самостоятельно разработать мод с помощью отправки данных по сети и UDP-датаграмм.

Для создания мода нашему разработчику пришлось прошерстить исходники игры, Lua, C++ и Python. А также выбрать подходящий редактор кода, среди которых — WolvenKit, RED4ext, REDmod, Cyber Engine Tweaks и другие. Две последние он использовал для разработки мода, а RED4ext — для отправки данных в формате JSON.

Консоль Cyber Engine Tweaks.

Что вы узнаете из текста

  • Как узнать скрипт игровой логики с помощью Lua.
  • Как научить Lua выполнять код при совершении событий в игре.
  • Какая архитектура подойдет для работы со сложными структурами.

Динозаврик Google на тачбаре

Суть проекта

С 2016 года у некоторых моделей MacBook Pro есть сенсорная OLED-панель — тачбар. Она не только заменяет привычные функциональные клавиши, но и позволяет разрабатывать собственные. Наш технический писатель Влад решил воспользоваться этой функцией и создать мини-игру с бегущим динозавриков от Google.

В статье автор показал, как с помощью Python и открытой библиотеки PyTouchBar спроектировать свою небольшую игру. И все это без погружения в драйверы и разработку на Swift.

Представление игровой сцены.

Что вы узнаете из текста

  • Как настроить игровую сцену с помощью числовых чанков.
  • Как научить динозаврика прыгать через кактус.
  • Как добавить подсчет набранных очков после окончания игры.

Рускоязычная игра в слова с ИИ

Суть проекта

На первый взгляд игра contexto кажется простой: нужно угадать загаданное слово. Однако нашему коллеге потребовалось 33 попытки и одна подсказка, чтобы найти верное. Осложнялось все тем, что игра доступна только на португальском и английском языках. Поэтому автор решил сделать свою версию — «Русо контексто».

Чтобы локализовать игру на русский, автор использовал модель от RusVectores, обученную на Национальном Корпусе Русского Языка. А для очистки модели от неподходящих слов — эмбеддинг Navec. Для статистики: только 36 269 токенов из 248 978 прошли «сито» эмбеддинга.

Что вы узнаете из текста

  • Зачем к словам добавлять теги _NOUN или _VERB.
  • Как получить начальную форму слова с помощью pymystem3.
  • Как настроить бэкенд, чтобы играть без интернета.

Тетрис в QR-коде

Суть проекта

Разработчик Вова вдохновился гифкой и сделал настоящий тетрис в QR-коде. А также рассказал, как устроена его топология. К слову, код полностью рабочий — наведите на него камеру и убедитесь сами.

Для реализации задумки автор использовал Python, а для генерации QR-кодов — библиотеку qrcode. Она позволяет получить доступ к QR-коду как к двумерному массиву булевых значений, где каждый элемент соответствует пикселю. Однако qrcode умеет создавать только статические изображения, поэтому для анимированных он использовал библиотеку imageio.

Что вы узнаете из текста

  • Как настроить размер QR-кода с помощью функции y=ax^2+bx+c.
  • Как использовать numpy для решения систем уравнений в Python.
  • Как создать свою последовательность действий из одной фигуры.

Напиши игру тетрис

Поскольку игра тетрис довольно сложная, создание ее может занять много времени и требует определенных навыков. Код игры в Python может быть длинным и сложным. Он будет включать в себя возможности для создания фигур, перемещения и поворота их, а также отслеживания их падения с последующим заполнением ряда и очками.

Пример простой игры тетрис на Python можно найти на просторах интернета или в различных учебниках и книгах по программированию на Python.

Игра тетрис на Python

import pygame import random pygame.init() # Константы bw, bh = 20, 20 board_w, board_h = 10, 20 board_x, board_y = 200, 100 screen_w, screen_h = board_x + board_w*bw + bw*5, board_y + board_h*bh + bh*5 colors = < 0: (0, 0, 0), 1: (255, 0, 0), 2: (0, 255, 0), 3: (0, 0, 255), 4: (255, 255, 0), 5: (255, 0, 255), 6: (0, 255, 255), 7: (200, 150, 50) >tetriminos = < 0: [], # Пустая фигура 1: [(-1, 0), (0, 0), (1, 0), (2, 0)], # I 2: [(-1, 1), (-1, 0), (0, 0), (1, 0)], # J 3: [(1, 1), (-1, 0), (0, 0), (1, 0)], # L 4: [(-1, 0), (0, 0), (0, 1), (1, 1)], # O 5: [(0, -1), (0, 0), (-1, 0), (1, -1)], # S 6: [(-1, -1), (-1, 0), (0, 0), (1, 0)], # T 7: [(0, -1), (0, 0), (-1, 1), (1, 0)] # Z ># Создание экрана screen = pygame.display.set_mode((screen_w, screen_h)) pygame.display.set_caption("Тетрис") clock = pygame.time.Clock() board = [[0]*board_w for _ in range(board_h)] def get_tetrimino(): return < "id": random.randint(1, 7), "pos": (board_w//2 - 2, 0), "rot": 0 >def draw_board(): for i in range(board_h): for j in range(board_w): pygame.draw.rect(screen, colors[board[i][j]], (board_x + j*bw + bw, board_y + i*bh + bh, bw, bh)) def draw_tetrimino(tetrimino): for block in tetriminos[tetrimino["id"]]: x, y = block[0] + tetrimino["pos"][0], block[1] + tetrimino["pos"][1] pygame.draw.rect(screen, colors[tetrimino["id"]], (board_x + x * bw + bw, board_y + y * bh + bh, bw, bh)) def check_collision(tetrimino): for block in tetriminos[tetrimino["id"]]: x, y = block[0] + tetrimino["pos"][0], block[1] + tetrimino["pos"][1] if x < 0 or x >= board_w or y >= board_h or (y >= 0 and board[y][x]): return True return False def add_tetrimino(tetrimino): for block in tetriminos[tetrimino["id"]]: x, y = block[0] + tetrimino["pos"][0], block[1] + tetrimino["pos"][1] board[y][x] = tetrimino["id"] def remove_full_rows(): full_rows = [i for i in range(board_h) if all(board[i])] if full_rows: for i in full_rows: board.pop(i) board.insert(0, [0]*board_w) def main(): tetrimino = get_tetrimino() game_over = False while not game_over: # Обработка событий for event in pygame.event.get(): if event.type == pygame.QUIT: game_over = True elif event.type == pygame.KEYDOWN: if event.key == pygame.K_q: game_over = True elif event.key == pygame.K_UP: new_rot = (tetrimino["rot"] + 1) % 4 temp = if not check_collision(temp): tetrimino["rot"] = new_rot elif event.key == pygame.K_DOWN: temp = if not check_collision(temp): tetrimino["pos"] = (tetrimino["pos"][0], tetrimino["pos"][1] + 1) elif event.key == pygame.K_LEFT: temp = if not check_collision(temp): tetrimino["pos"] = (tetrimino["pos"][0] - 1, tetrimino["pos"][1]) elif event.key == pygame.K_RIGHT: temp = if not check_collision(temp): tetrimino["pos"] = (tetrimino["pos"][0] + 1, tetrimino["pos"][1]) # Падение фигуры temp = if check_collision(temp): add_tetrimino(tetrimino) remove_full_rows() tetrimino = get_tetrimino() if check_collision(tetrimino): game_over = True else: tetrimino = temp # Отрисовка screen.fill((255, 255, 255)) draw_board() draw_tetrimino(tetrimino) pygame.display.flip() # Ограничение FPS clock.tick(10) pygame.quit() if __name__ == '__main__': main() 

В результате выполнения программы можно запустить игру тетрис в окне Pygame. Игра будет состоять из фигур, которые будут падать сверху вниз и возможностью их перемещения и поворота. При заполнении ряда, он будет удаляться и игрок будет получать очки. Если ряды заполнятся до верху, игра окончена.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *