def __init__(self, parent, nrows, ncols, blocksize): super(DrawabaleTetrisBoard, self).__init__(parent) self.nrows = nrows self.ncols = ncols self.blocksize = blocksize self.nrows = nrows self.ncols = ncols self.blocksize = blocksize self.showgrid = True self.gridwidth = 2 self.gridcolor = QColor(204, 204, 204) # the "sink" is the white border around the actual # tetris grid # self.sinkwidth = 4 self.sinkcolor = 'white' self.block_border_color = 'black' self.bgcolor = QColor(234, 234, 244) self.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.width = self.sinkwidth * 2 + ncols * blocksize + ( ncols + 1) * self.gridwidth self.height = self.sinkwidth * 2 + nrows * blocksize + ( nrows + 1) * self.gridwidth self.board = TetrisBoard(nrows, ncols)
def set_figure(self, figure): self.figure = figure self.board = TetrisBoard(self.nrows, self.ncols) self.board.spawn_figure(figure) self.board.move_figure_down() self.board.move_figure_down() self.update()
def __init__(self, parent, nrows, ncols, blocksize): super(DrawabaleTetrisBoard, self).__init__(parent) self.nrows = nrows self.ncols = ncols self.blocksize = blocksize self.nrows = nrows self.ncols = ncols self.blocksize = blocksize self.showgrid = True self.gridwidth = 2 self.gridcolor = QColor(204, 204, 204) # the "sink" is the white border around the actual # tetris grid # self.sinkwidth = 4 self.sinkcolor = "white" self.block_border_color = "black" self.bgcolor = QColor(234, 234, 244) self.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.width = self.sinkwidth * 2 + ncols * blocksize + (ncols + 1) * self.gridwidth self.height = self.sinkwidth * 2 + nrows * blocksize + (nrows + 1) * self.gridwidth self.board = TetrisBoard(nrows, ncols)
class TetrisPreviewWidget(DrawabaleTetrisBoard): """ A preview window for figures. Only shows a single figure, trying to center it vertically. Note: the success of this method depends on the actual figures that participate in the game. It works well for the default 7 Tetris figures. """ def __init__(self, parent, nrows, ncols, blocksize): super(TetrisPreviewWidget, self).__init__(parent, nrows, ncols, blocksize) self.showgrid = False self.figure = None def set_figure(self, figure): self.figure = figure self.board = TetrisBoard(self.nrows, self.ncols) self.board.spawn_figure(figure) self.board.move_figure_down() self.board.move_figure_down() self.update()
class DrawabaleTetrisBoard(QWidget): """ A base class for tetris board widgets that can draw themselves. """ def __init__(self, parent, nrows, ncols, blocksize): super(DrawabaleTetrisBoard, self).__init__(parent) self.nrows = nrows self.ncols = ncols self.blocksize = blocksize self.nrows = nrows self.ncols = ncols self.blocksize = blocksize self.showgrid = True self.gridwidth = 2 self.gridcolor = QColor(204, 204, 204) # the "sink" is the white border around the actual # tetris grid # self.sinkwidth = 4 self.sinkcolor = 'white' self.block_border_color = 'black' self.bgcolor = QColor(234, 234, 244) self.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.width = self.sinkwidth * 2 + ncols * blocksize + ( ncols + 1) * self.gridwidth self.height = self.sinkwidth * 2 + nrows * blocksize + ( nrows + 1) * self.gridwidth self.board = TetrisBoard(nrows, ncols) def minimumSizeHint(self): return QSize(self.width, self.height) def sizeHint(self): return self.minimumSizeHint() def paintEvent(self, event=None): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.fillRect(0, 0, self.width, self.height, QBrush(self.bgcolor, Qt.SolidPattern)) if self.showgrid: self._draw_grid(painter) self._draw_sink(painter) self._draw_all_blocks(painter) def _draw_sink(self, painter): sink_pen = QPen(QColor(self.sinkcolor)) sink_pen.setWidth(self.sinkwidth) painter.setPen(sink_pen) halfsink = self.sinkwidth / 2 painter.drawLine(halfsink, 0, halfsink, self.height) painter.drawLine(self.width - halfsink, 0, self.width - halfsink, self.height) painter.drawLine(0, halfsink, self.width, halfsink) painter.drawLine(0, self.height - halfsink, self.width, self.height - halfsink) def _draw_grid(self, painter): grid_pen = QPen(QColor(self.gridcolor)) grid_pen.setWidth(self.gridwidth) painter.setPen(grid_pen) # combined size of a block with a single grid line blockgrid_size = self.blocksize + self.gridwidth # horizontal for row in range(self.nrows + 1): painter.drawLine(self.sinkwidth, self.sinkwidth + row * blockgrid_size + 1, self.width - 1 - self.sinkwidth, self.sinkwidth + row * blockgrid_size + 1) # vertical for col in range(self.ncols + 1): painter.drawLine(self.sinkwidth + col * blockgrid_size + 1, self.sinkwidth, self.sinkwidth + col * blockgrid_size + 1, self.height - 1 - self.sinkwidth) def _draw_all_blocks(self, painter): board = self.board.board_with_active_figure() for row in range(self.nrows): for col in range(self.ncols): color = board[row][col] if color != 0: self._draw_block(painter, row, col, color) def _draw_block(self, painter, row, col, color): block_pen = QPen(QColor(self.block_border_color)) block_pen.setWidth(self.gridwidth) painter.setPen(block_pen) blockgrid_size = self.blocksize + self.gridwidth block_rect = QRect(self.sinkwidth + col * blockgrid_size + 1, self.sinkwidth + row * blockgrid_size + 1, blockgrid_size, blockgrid_size) painter.fillRect(block_rect, QBrush(color, Qt.SolidPattern)) painter.drawRect(block_rect)
def restart(self, startfigure): self.board = TetrisBoard(self.nrows, self.ncols) self.board.spawn_figure(startfigure) self.update()
class MainTetrisWidget(DrawabaleTetrisBoard): """ The main tetris window type, with an active figure that can be moved around, dropped, etc. and a bunch of inactive blocks. Supports all the expected tetris operations, like removing completed rows and generating new figures. """ def __init__(self, parent, nrows, ncols, blocksize, startfigure): super(MainTetrisWidget, self).__init__(parent, nrows, ncols, blocksize) self.board.spawn_figure(startfigure) # Keeps track of the amount of rows the active figure # fell in the last "drop" command. This is used to # update the score. # self.last_drop_height = 0 def restart(self, startfigure): self.board = TetrisBoard(self.nrows, self.ncols) self.board.spawn_figure(startfigure) self.update() def keyPressEvent(self, event): if event.key() == Qt.Key_Up: self.board.rotate_figure() elif event.key() == Qt.Key_Down: self.board.move_figure_down() elif event.key() == Qt.Key_Left: self.board.move_figure_left() elif event.key() == Qt.Key_Right: self.board.move_figure_right() elif event.key() == Qt.Key_Space: for i in range(self.nrows): if not self.board.move_figure_down(): self.last_drop_height = i break else: return self.update() Result = namedtuple('Result', 'state completed_rows drop_height') def timer_tick(self, nextfigure): """ One timer tick for the tetris game. Advances the game by one step and returns a result as a namedtuple: result.state: The game state. running - The current figure was moved down by one cell successfully. newfigure - The current figure could no longer be moved down, so a new figure was created. gameover - The current figure could no longer be moved down, and a new figure could not be created. result.completed_rows: A list of row numbers that were completed with the current figure reaching bottom. It is applicable in the "newfigure" state result.drop_height: The amount of lines the figure was dropped in the last drop. """ state = 'running' completed_rows = [] drop_height = 0 if self.board.move_figure_down(): state = 'running' else: completed_rows = self.board.finish_fall() if self.board.spawn_figure(nextfigure): state = 'newfigure' else: state = 'gameover' drop_height = self.last_drop_height self.last_drop_height = 0 self.update() return self.Result(state, completed_rows, drop_height)
class DrawabaleTetrisBoard(QWidget): """ A base class for tetris board widgets that can draw themselves. """ def __init__(self, parent, nrows, ncols, blocksize): super(DrawabaleTetrisBoard, self).__init__(parent) self.nrows = nrows self.ncols = ncols self.blocksize = blocksize self.nrows = nrows self.ncols = ncols self.blocksize = blocksize self.showgrid = True self.gridwidth = 2 self.gridcolor = QColor(204, 204, 204) # the "sink" is the white border around the actual # tetris grid # self.sinkwidth = 4 self.sinkcolor = "white" self.block_border_color = "black" self.bgcolor = QColor(234, 234, 244) self.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) self.width = self.sinkwidth * 2 + ncols * blocksize + (ncols + 1) * self.gridwidth self.height = self.sinkwidth * 2 + nrows * blocksize + (nrows + 1) * self.gridwidth self.board = TetrisBoard(nrows, ncols) def minimumSizeHint(self): return QSize(self.width, self.height) def sizeHint(self): return self.minimumSizeHint() def paintEvent(self, event=None): painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) painter.fillRect(0, 0, self.width, self.height, QBrush(self.bgcolor, Qt.SolidPattern)) if self.showgrid: self._draw_grid(painter) self._draw_sink(painter) self._draw_all_blocks(painter) def _draw_sink(self, painter): sink_pen = QPen(QColor(self.sinkcolor)) sink_pen.setWidth(self.sinkwidth) painter.setPen(sink_pen) halfsink = self.sinkwidth / 2 painter.drawLine(halfsink, 0, halfsink, self.height) painter.drawLine(self.width - halfsink, 0, self.width - halfsink, self.height) painter.drawLine(0, halfsink, self.width, halfsink) painter.drawLine(0, self.height - halfsink, self.width, self.height - halfsink) def _draw_grid(self, painter): grid_pen = QPen(QColor(self.gridcolor)) grid_pen.setWidth(self.gridwidth) painter.setPen(grid_pen) # combined size of a block with a single grid line blockgrid_size = self.blocksize + self.gridwidth # horizontal for row in range(self.nrows + 1): painter.drawLine( self.sinkwidth, self.sinkwidth + row * blockgrid_size + 1, self.width - 1 - self.sinkwidth, self.sinkwidth + row * blockgrid_size + 1, ) # vertical for col in range(self.ncols + 1): painter.drawLine( self.sinkwidth + col * blockgrid_size + 1, self.sinkwidth, self.sinkwidth + col * blockgrid_size + 1, self.height - 1 - self.sinkwidth, ) def _draw_all_blocks(self, painter): board = self.board.board_with_active_figure() for row in range(self.nrows): for col in range(self.ncols): color = board[row][col] if color != 0: self._draw_block(painter, row, col, color) def _draw_block(self, painter, row, col, color): block_pen = QPen(QColor(self.block_border_color)) block_pen.setWidth(self.gridwidth) painter.setPen(block_pen) blockgrid_size = self.blocksize + self.gridwidth block_rect = QRect( self.sinkwidth + col * blockgrid_size + 1, self.sinkwidth + row * blockgrid_size + 1, blockgrid_size, blockgrid_size, ) painter.fillRect(block_rect, QBrush(color, Qt.SolidPattern)) painter.drawRect(block_rect)
class MainTetrisWidget(DrawabaleTetrisBoard): """ The main tetris window type, with an active figure that can be moved around, dropped, etc. and a bunch of inactive blocks. Supports all the expected tetris operations, like removing completed rows and generating new figures. """ def __init__(self, parent, nrows, ncols, blocksize, startfigure): super(MainTetrisWidget, self).__init__(parent, nrows, ncols, blocksize) self.board.spawn_figure(startfigure) # Keeps track of the amount of rows the active figure # fell in the last "drop" command. This is used to # update the score. # self.last_drop_height = 0 def restart(self, startfigure): self.board = TetrisBoard(self.nrows, self.ncols) self.board.spawn_figure(startfigure) self.update() def keyPressEvent(self, event): if event.key() == Qt.Key_Up: self.board.rotate_figure() elif event.key() == Qt.Key_Down: self.board.move_figure_down() elif event.key() == Qt.Key_Left: self.board.move_figure_left() elif event.key() == Qt.Key_Right: self.board.move_figure_right() elif event.key() == Qt.Key_Space: for i in range(self.nrows): if not self.board.move_figure_down(): self.last_drop_height = i break else: return self.update() Result = namedtuple("Result", "state completed_rows drop_height") def timer_tick(self, nextfigure): """ One timer tick for the tetris game. Advances the game by one step and returns a result as a namedtuple: result.state: The game state. running - The current figure was moved down by one cell successfully. newfigure - The current figure could no longer be moved down, so a new figure was created. gameover - The current figure could no longer be moved down, and a new figure could not be created. result.completed_rows: A list of row numbers that were completed with the current figure reaching bottom. It is applicable in the "newfigure" state result.drop_height: The amount of lines the figure was dropped in the last drop. """ state = "running" completed_rows = [] drop_height = 0 if self.board.move_figure_down(): state = "running" else: completed_rows = self.board.finish_fall() if self.board.spawn_figure(nextfigure): state = "newfigure" else: state = "gameover" drop_height = self.last_drop_height self.last_drop_height = 0 self.update() return self.Result(state, completed_rows, drop_height)