Created
October 31, 2022 08:11
-
-
Save MrShameer/43a986d3885c44e59bf983889da4be54 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import random | |
import sys | |
from PySide2 import QtCore, QtGui, QtWidgets | |
class Tetris(QtWidgets.QMainWindow): | |
def __init__(self, *args, **kwargs): | |
super(Tetris, self).__init__(*args, **kwargs) | |
self.initUI() | |
def initUI(self): | |
"""initiates application UI""" | |
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): | |
"""centers the window on the screen""" | |
screen = QtWidgets.QDesktopWidget().screenGeometry() | |
size = self.geometry() | |
self.move(int((screen.width() - size.width()) / 2), | |
int((screen.height() - size.height()) / 2)) | |
class Board(QtWidgets.QFrame): | |
msg2Statusbar = QtCore.Signal(str) | |
BoardWidth = 10 | |
BoardHeight = 22 | |
Speed = 300 | |
def __init__(self, parent): | |
super(Board, self).__init__(parent) | |
self.initBoard() | |
def initBoard(self): | |
"""initiates board""" | |
self.timer = QtCore.QBasicTimer() | |
self.isWaitingAfterLine = False | |
self.curX = 0 | |
self.curY = 0 | |
self.numLinesRemoved = 0 | |
self.board = [] | |
self.setFocusPolicy(QtCore.Qt.StrongFocus) | |
self.isStarted = False | |
self.isPaused = False | |
self.clearBoard() | |
def shapeAt(self, x, y): | |
"""determines shape at the board position""" | |
return self.board[(y * Board.BoardWidth) + x] | |
def setShapeAt(self, x, y, shape): | |
"""sets a shape at the board""" | |
self.board[(y * Board.BoardWidth) + x] = shape | |
def squareWidth(self): | |
"""returns the width of one square""" | |
return self.contentsRect().width() // Board.BoardWidth | |
def squareHeight(self): | |
"""returns the height of one square""" | |
return self.contentsRect().height() // Board.BoardHeight | |
def start(self): | |
"""starts game""" | |
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): | |
"""pauses game""" | |
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): | |
"""paints all shapes of the game""" | |
painter = QtGui.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): | |
"""processes key press events""" | |
if not self.isStarted or self.curPiece.shape() == Tetrominoe.NoShape: | |
super(Board, self).keyPressEvent(event) | |
return | |
key = event.key() | |
if key == QtCore.Qt.Key_P: | |
self.pause() | |
return | |
if self.isPaused: | |
return | |
elif key == QtCore.Qt.Key_Left: | |
self.tryMove(self.curPiece, self.curX - 1, self.curY) | |
elif key == QtCore.Qt.Key_Right: | |
self.tryMove(self.curPiece, self.curX + 1, self.curY) | |
elif key == QtCore.Qt.Key_Down: | |
self.tryMove(self.curPiece.rotateRight(), self.curX, self.curY) | |
elif key == QtCore.Qt.Key_Up: | |
self.tryMove(self.curPiece.rotateLeft(), self.curX, self.curY) | |
elif key == QtCore.Qt.Key_Space: | |
self.dropDown() | |
elif key == QtCore.Qt.Key_D: | |
self.oneLineDown() | |
else: | |
super(Board, self).keyPressEvent(event) | |
def timerEvent(self, event): | |
"""handles timer 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): | |
"""clears shapes from the board""" | |
for i in range(Board.BoardHeight * Board.BoardWidth): | |
self.board.append(Tetrominoe.NoShape) | |
def dropDown(self): | |
"""drops down a shape""" | |
newY = self.curY | |
while newY > 0: | |
if not self.tryMove(self.curPiece, self.curX, newY - 1): | |
break | |
newY -= 1 | |
self.pieceDropped() | |
def oneLineDown(self): | |
"""goes one line down with a shape""" | |
if not self.tryMove(self.curPiece, self.curX, self.curY - 1): | |
self.pieceDropped() | |
def pieceDropped(self): | |
"""after dropping shape, remove full lines and create new shape""" | |
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): | |
"""removes all full lines from the board""" | |
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): | |
"""creates a new shape""" | |
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): | |
"""tries to move a shape""" | |
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): | |
"""draws a square of a shape""" | |
colorTable = [0x000000, 0xCC6666, 0x66CC66, 0x6666CC, | |
0xCCCC66, 0xCC66CC, 0x66CCCC, 0xDAAA00] | |
color = QtGui.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): | |
"""returns shape""" | |
return self.pieceShape | |
def setShape(self, shape): | |
"""sets a 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): | |
"""chooses a random shape""" | |
self.setShape(random.randint(1, 7)) | |
def x(self, index): | |
"""returns x coordinate""" | |
return self.coords[index][0] | |
def y(self, index): | |
"""returns y coordinate""" | |
return self.coords[index][1] | |
def setX(self, index, x): | |
"""sets x coordinate""" | |
self.coords[index][0] = x | |
def setY(self, index, y): | |
"""sets y coordinate""" | |
self.coords[index][1] = y | |
def minX(self): | |
"""returns min x value""" | |
m = self.coords[0][0] | |
for i in range(4): | |
m = min(m, self.coords[i][0]) | |
return m | |
def maxX(self): | |
"""returns max x value""" | |
m = self.coords[0][0] | |
for i in range(4): | |
m = max(m, self.coords[i][0]) | |
return m | |
def minY(self): | |
"""returns min y value""" | |
m = self.coords[0][1] | |
for i in range(4): | |
m = min(m, self.coords[i][1]) | |
return m | |
def maxY(self): | |
"""returns max y value""" | |
m = self.coords[0][1] | |
for i in range(4): | |
m = max(m, self.coords[i][1]) | |
return m | |
def rotateLeft(self): | |
"""rotates shape to the left""" | |
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): | |
"""rotates shape to the right""" | |
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 | |
App = QtWidgets.QApplication.instance() | |
if App is None: | |
App = QtWidgets.QApplication(sys.argv) | |
tetris = Tetris() | |
App.exec_() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment