Source code for BiblioPixelAnimations.game.Tetris

# Based on: https://gist.github.com/kch42/565419

from random import randrange as rand
from bibliopixel.colors import COLORS
from bibliopixel.colors.conversions import hue_helper
from bibliopixel.animation.game import Game


# The configuration
cols = 19
rows = 40
maxfps = 30

color_map = [
    COLORS.Black,
    COLORS.Red,
    COLORS.Orange,
    COLORS.Yellow,
    COLORS.Green,
    COLORS.Blue,
    COLORS.Purple,
    COLORS.Violet,

    COLORS.Cyan,
    COLORS.SeaGreen,
    COLORS.Navy,
    COLORS.YellowGreen,
    COLORS.DarkRed,
    COLORS.Teal,
    COLORS.MediumVioletRed,
]

# Define the shapes of the single parts
tetris_shapes = [
    [[1, 1, 1],
     [0, 1, 0]],

    [[0, 2, 2],
     [2, 2, 0]],

    [[3, 3, 0],
     [0, 3, 3]],

    [[4, 0, 0],
     [4, 4, 4]],

    [[0, 0, 5],
     [5, 5, 5]],

    [[6, 6, 6, 6]],

    [[7, 7],
     [7, 7]],

    [[8]],

    [[9, 0, 0],
     [9, 9, 0],
     [9, 9, 9]],

    [[10, 0, 10],
     [10, 10, 10]],

    [[0, 11, 0],
     [11, 11, 11],
     [0, 11, 0]],

    [[12, 0, 0],
     [12, 12, 12],
     [0, 0, 12]],

    [[13, 13, 13],
     [13, 0, 13],
     [13, 13, 13]],

    [[14, 0, 0],
     [14, 0, 0],
     [14, 14, 14]],
]


[docs]def rotate_clockwise(shape): return [[shape[y][x] for y in range(len(shape))] for x in range(len(shape[0]) - 1, -1, -1)]
[docs]def check_collision(board, shape, offset): off_x, off_y = offset for cy, row in enumerate(shape): for cx, cell in enumerate(row): try: if cell and board[cy + off_y][cx + off_x]: return True except IndexError: return True return False
[docs]def remove_row(board, row): del board[row] return [[0 for i in range(cols)]] + board
[docs]def join_matrixes(mat1, mat2, mat2_off): off_x, off_y = mat2_off for cy, row in enumerate(mat2): for cx, val in enumerate(row): mat1[cy + off_y - 1 ][cx + off_x] += val return mat1
[docs]def new_board(): board = [[0 for x in range(cols)] for y in range(rows)] return board
[docs]class Tetris(Game): def __init__(self, layout, inputDev, evil=False, **kwds): super().__init__(layout, inputDev, **kwds) if (self.width, self.height) != (25, 50): raise Exception( "Sorry, this was lazily written to only work on a 25x50 display :(") if hasattr(self._input_dev, "setLights") and hasattr(self._input_dev, "setLightsOff"): self._input_dev.setLightsOff(5) lights = { "Y": (0, 255, 0), "B": (0, 0, 0), "X": (0, 0, 0), "A": (255, 0, 0), "SELECT": (255, 0, 0) } self._input_dev.setLights(lights) self.setSpeed("drop", 5) self.rlim = cols self._evilMode = evil self._doEvil = False self.next_stone = self._getNextPiece() self.addKeyFunc("LEFT", lambda: self.move(-1), speed=3, hold=True) self.addKeyFunc("RIGHT", lambda: self.move(+1), speed=3, hold=True) self.addKeyFunc("DOWN", lambda: self.drop(True), speed=1, hold=True) self.addKeyFunc(["A"], self.rotate_stone, speed=1, hold=False) self.addKeyFunc(["B", "SELECT"], self.insta_drop, speed=1, hold=False) self.addKeyFunc("START", self.togglePause, speed=1, hold=False) self.init_game()
[docs] def pre_run(self): self._step = 0
def _getNextPiece(self): stop = len(tetris_shapes) if self._doEvil else 7 return tetris_shapes[rand(0, stop)]
[docs] def togglePause(self): self.paused = not self.paused
[docs] def clearLevelUp(self): self.doStart = False if self.levelUp: self.paused = False self.levelUp = False
[docs] def new_stone(self): self.stone = self.next_stone[:] self.next_stone = self._getNextPiece() self.stone_x = int(cols // 2 - len(self.stone[0]) // 2) self.stone_y = 0 if check_collision(self.board, self.stone, (self.stone_x, self.stone_y)): self.gameover = True
[docs] def init_game(self): self.lines_per_level = 6 self.gameover = False self.win = False self.levelUp = True self.doStart = False self.paused = True self.board = new_board() self.new_stone() self.level = 1 self.score = 0 self.lines = 0
[docs] def disp_msg(self, msg, x, y): self.layout.drawText(msg, x, y, font_scale=1, font='6x4', color=COLORS.White)
[docs] def draw_matrix(self, matrix, offset): off_x, off_y = offset for y, row in enumerate(matrix): for x, val in enumerate(row): if val: self.layout.set(off_x + x, off_y + y, color_map[val])
[docs] def add_cl_lines(self, n): linescores = [0, 40, 100, 300, 1200] if n > 0 and self._evilMode and not self._doEvil: self._doEvil = True self.lines += n self.score += linescores[n] * self.level if self.lines >= self.level * self.lines_per_level: self.level += 1 self.levelUp = True self.paused = True s = self.getSpeed("drop") s -= 1 if s <= 0: self.win = True else: self.setSpeed("drop", s)
[docs] def move(self, delta_x): if not self.gameover and not self.paused: new_x = self.stone_x + delta_x if new_x < 0: new_x = 0 if new_x > cols - len(self.stone[0]): new_x = cols - len(self.stone[0]) if not check_collision(self.board, self.stone, (new_x, self.stone_y)): self.stone_x = new_x
[docs] def drop(self, manual): if not self.gameover and not self.paused: self.score += 1 if manual else 0 self.stone_y += 1 if check_collision(self.board, self.stone, (self.stone_x, self.stone_y)): self.board = join_matrixes( self.board, self.stone, (self.stone_x, self.stone_y)) self.new_stone() cleared_rows = 0 while True: for i, row in enumerate(self.board): if 0 not in row: self.board = remove_row( self.board, i) cleared_rows += 1 break else: break self.add_cl_lines(cleared_rows) return True return False
[docs] def insta_drop(self): if not self.gameover and not self.paused: while(not self.drop(True)): pass
[docs] def rotate_stone(self): if not self.gameover and not self.paused: new_stone = rotate_clockwise(self.stone) if not check_collision(self.board, new_stone, (self.stone_x, self.stone_y)): self.stone = new_stone
[docs] def toggle_pause(self): self.paused = not self.paused
[docs] def start_game(self): self.doStart = False if self.gameover or self.win: self.init_game() self.gameover = False
[docs] def step(self, amt=1): if (self.levelUp or self.gameover or self.win) and (self._lastKeys != self._keys) and any(v is True for v in self._keys.itervalues()): self.doStart = True if self.doStart: if not any(v is True for v in self._keys.itervalues()): if self.levelUp: self.clearLevelUp() elif self.gameover or self.win: self.start_game() else: return if not self.doStart: self.handleKeys() self.layout.all_off() if self.gameover: self.layout.all_off() self.layout.drawText("GAME", self.width // 2 - 11, self.height // 2 - 8, color=COLORS.Green) self.layout.drawText("OVER", self.width // 2 - 11, self.height // 2 + 1, color=COLORS.Green) s = "{}".format(self.score) self.layout.drawText(s, self.width // 2 - (len(s) * 4) // 2 + 1, self.height // 2 + 9, font_scale=1, font='6x4', color=COLORS.Green) elif self.win: for x in range(self.width): c = hue_helper( self.width - x, self.width, self._speedStep * 2) self.layout.drawLine(self.width // 2, self.height // 2, x, 0, c) self.layout.drawLine(self.width // 2, self.height // 2, self.width - 1 - x, self.height - 1, c) for y in range(self.height): c = hue_helper(y, self.height, self._speedStep * 2) self.layout.drawLine(self.width // 2, self.height // 2, 0, y, c) self.layout.drawLine(self.width // 2, self.height // 2, self.width - 1, self.height - 1 - y, c) self.layout.drawText("YOU", self.width // 2 - 9, self.height // 2 - 8, color=COLORS.Black, bg=None) self.layout.drawText("WIN!", self.width // 2 - 10, self.height // 2 + 1, color=COLORS.Black, bg=None) else: if self.paused: self.layout.all_off() if self.levelUp: self.layout.drawText( "LVL", self.width // 2 - 8, self.height // 2 - 8, color=COLORS.Green) str_level = "{}".format(self.level) self.layout.drawText( str_level, self.width // 2 - (len(str_level) * 6) // 2 + 1, self.height // 2 + 1, color=COLORS.Green) else: x = self.width // 2 - 2 y = 1 self.layout.drawText("P", x, y + 0, color=COLORS.White) self.layout.drawText("A", x, y + 8, color=COLORS.White) self.layout.drawText("U", x, y + 16, color=COLORS.White) self.layout.drawText("S", x, y + 24, color=COLORS.White) self.layout.drawText("E", x, y + 32, color=COLORS.White) self.layout.drawText("D", x, y + 40, color=COLORS.White) else: self.disp_msg("{}".format(self.score), 1, 1) lines_left = self.level * self.lines_per_level - self.lines for l in range(lines_left): self.layout.set(0, self.height - 1 - l * 2, COLORS.Red) # draw rainbow border self.layout.drawLine(2, 8, cols + 3, 8, colorFunc=lambda pos: hue_helper(pos, cols + 2, self._speedStep * 2)) self.layout.drawLine(2, self.height - 1, cols + 3, self.height - 1, colorFunc=lambda pos: hue_helper(cols + 2 - pos, cols + 2, self._speedStep * 2)) self.layout.drawLine(2, 9, 2, self.height - 2, colorFunc=lambda pos: hue_helper(rows + 2 - pos, rows, self._speedStep * 2)) self.layout.drawLine(cols + 3, 9, cols + 3, self.height - 2, colorFunc=lambda pos: hue_helper(pos, rows, self._speedStep * 2)) # draw current board state self.draw_matrix(self.board, (3, 9)) # draw current block self.draw_matrix( self.stone, (self.stone_x + 3, self.stone_y + 9)) # draw next block self.draw_matrix(self.next_stone, (self.width - 6, 1)) # drop block if self.checkSpeed("drop"): self.drop(False) self._step += amt