from math import * from kandinsky import * from ion import * from random import * from time import monotonic, sleep WIDTH = 320 HEIGHT = 222 gridWidth = 10 gridHeight = 20 squareSize = HEIGHT // gridHeight gridOffset = (WIDTH - gridWidth * squareSize) // 2 borderSize = 1 startBlockX = gridWidth // 2 dangerDistance = gridHeight // 2.5 statOffset = gridOffset + (gridWidth + 2) * squareSize linePoint = [100, 300, 500, 800] comboPoint = 50 palette = {' ':'#b0b0b0', 'I':'#007ace', 'O':'#efaa00', 'T':'#660066', 'L':'#b45700', 'J':'#000073', 'Z':'#990000', 'S':'#009700'} bgColor = '#141414' dangerColor = '#683b3b' sparkColor = '#ffffff' deadBlockColor = '#303030' textColor = '#eeeeee' frameColors = ('#363536', '#454545') tetromino = { 'I':( ((-2, 0), (-1, 0), (0, 0), (1, 0)), ((0, -1), (0, 0), (0, 1), (0, 2)), ((-2, 1), (-1, 1), (0, 1), (1, 1)), ((-1, -1), (-1, 0), (-1, 1), (-1, 2)) ), 'O':( ((0, 0), (0, 1), (-1, 0), (-1, 1)), ), 'T':( ((-1, 0), (0, 0), (1, 0), (0, 1)), ((-1, 0), (0, 0), (0, -1), (0, 1)), ((-1, 0), (0, 0), (0, -1), (1, 0)), ((0, 1), (0, 0), (0, -1), (1, 0)) ), 'L':( ((-1, 1), (-1, 0), (0, 0), (1, 0)), ((-1, -1), (0, -1), (0, 0), (0, 1)), ((-1, 0), (0, 0), (1, 0), (1, -1)), ((0, -1), (0, 0), (0, 1), (1, 1)) ), 'J':( ((-1, 0), (0, 0), (1, 0), (1, 1)), ((0, -1), (0, 0), (0, 1), (-1, 1)), ((-1, -1), (-1, 0), (0, 0), (1, 0)), ((1, -1), (0, -1), (0, 0), (0, 1)) ), 'Z':( ((-1, -1), (0, -1), (0, 0), (1, 0)), ((1, -1), (1, 0), (0, 0), (0, 1)), ((-1, 0), (0, 0), (0, 1), (1, 1)), ((0, -1), (0, 0), (-1, 0), (-1, 1)) ), 'S':( ((-1, 0), (0, 0), (0, -1), (1, -1)), ((0, -1), (0, 0), (1, 0), (1, 1)), ((-1, 1), (0, 1), (0, 0), (1, 0)), ((-1, -1), (-1, 0), (0, 0), (0, 1)) ) } def createArray(w, h, e): return [[e]*w for _ in range(h)] def copyArray(array): newArray = [] for i in array: newArray.append(i.copy()) return newArray def notOverlap(block, xOffset, yOffset): for square in block: x = square[0] + xOffset y = square[1] + yOffset if not (x >=0 and x < gridWidth and y >= 0 and y < gridHeight and grid[y][x] == ' '): return False return True def findNewBlock(): global blockQueue, blockDiscard, blockShape if keydown(KEY_IMAGINARY): blockShape = 'I' else: if len(blockQueue) < 2: for square in blockDiscard: blockQueue.append(square) blockDiscard = [] blockShape = blockQueue.pop(0) blockDiscard.insert(randint(0, 0), blockShape) drawNextBlock() def generateNewBlock(): global blockQueue, blockShape, blockX, blockY, blockRotation, game, startTimeBlockFall findNewBlock() blockX = startBlockX blockY = - min(tetromino[blockShape][0], key=lambda x: x[1])[1] blockRotation = 0 if not notOverlap(tetromino[blockShape][blockRotation], blockX, blockY): game = False startTimeBlockFall = monotonic() def moveBlock(direction): global timeMove, blockX timeMove = monotonic() + 0.1 if notOverlap(tetromino[blockShape][blockRotation], blockX + direction, blockY): blockX += direction def rotateBlock(): global blockRotation newRotation = blockRotation + 1 if newRotation == len(tetromino[blockShape]): newRotation = 0 if notOverlap(tetromino[blockShape][newRotation], blockX, blockY): blockRotation = newRotation def blockFall(): global blockX, blockY, danger if notOverlap(tetromino[blockShape][blockRotation], blockX, blockY + 1): blockY += 1 else: for square in tetromino[blockShape][blockRotation]: grid[square[1] + blockY][square[0] + blockX] = blockShape checkLine() danger = grid.count([' '] * gridWidth) <= dangerDistance generateNewBlock() def checkLine(): global grid, displayGrid, score, level, totalLines, fallDelay, combo deletedLine = [] for i in range(gridHeight): if not ' ' in grid[i]: deletedLine.append(i) linesCount = len(deletedLine) if linesCount: for i in reversed(deletedLine): grid.pop(i) for i in range(linesCount): grid.insert(0, [' '] * gridWidth) for i in range(linesCount): for c in (sparkColor, palette[' ']): for y in deletedLine: for x in range(gridWidth): fill_rect(x * squareSize + borderSize + gridOffset, (y * squareSize) + borderSize, squareSize - borderSize, squareSize - borderSize, c) sleep(0.1) combo += 1 score += linePoint[linesCount -1] * (level +1) + comboPoint * combo * level totalLines += linesCount level = totalLines // 10 fallDelay = calculateFallDelay(level) updateInterface() displayGrid = copyArray(grid) else: combo = -1 fill_rect(WIDTH - 85, 130, 70, 40, bgColor) def calculateFallDelay(level): level = min(level, 15) return (-0.007 * level + 0.807) ** (level -1) def drawInterface(): fill_rect(0, 0, WIDTH, HEIGHT, bgColor) drawFrame(10, 20, 80, 40) drawFrame(20, 9, 60, 20) draw_string('Score', 25, 10, textColor, frameColors[0]) drawFrame(15, 80, 70, 40) draw_string('Level', 25, 82, textColor, frameColors[0]) drawFrame(15, 130, 70, 40) draw_string('Lines', 25, 131, textColor, frameColors[0]) drawFrame(WIDTH - 90, 20, 80, 60) drawFrame(WIDTH - 80, 9, 60, 20) draw_string('Next', WIDTH - 70, 10, textColor, frameColors[0]) updateInterface() def updateInterface(): draw_string(str(score), 20, 35, textColor, frameColors[0]) draw_string(str(level), 25, 100, textColor, frameColors[0]) draw_string(str(totalLines), 25, 150, textColor, frameColors[0]) if combo > 0: drawFrame(WIDTH - 85, 130, 70, 40) draw_string('Combo', WIDTH - 75, 131, textColor, frameColors[0]) draw_string(str(combo), WIDTH - 75, 150, textColor, frameColors[0]) def drawFrame(x, y, width, height): fill_rect(x, y, width, height, frameColors[1]) fill_rect(x + borderSize, y + borderSize, width - 2 * borderSize, height - 2 * borderSize, frameColors[0]) def drawSquare(x, y, color): fill_rect(x * squareSize + borderSize + gridOffset, y * squareSize + borderSize, squareSize - borderSize, squareSize - borderSize, color) def drawNextBlock(): fill_rect(WIDTH - 80, 30, 60, 40, frameColors[0]) nextBlock = tetromino[blockQueue[0]][0] squaresX = [i[0] for i in nextBlock] squareMinX = min(squaresX) blockSquareWidth = max(squaresX) - squareMinX + 1 squaresY = [i[1] for i in nextBlock] squareMinY = min(squaresY) blockSquareHeight = max(squaresY) - squareMinY + 1 for square in nextBlock: x, y = square fill_rect(int((x - squareMinX - blockSquareWidth / 2) * squareSize + borderSize + WIDTH - 50), int((y - squareMinY - blockSquareHeight / 2) * squareSize + borderSize + 50), squareSize - borderSize, squareSize - borderSize, palette[blockQueue[0]]) def drawGrid(): for y in range(gridHeight): for x in range(gridWidth): if displayGrid[y][x] == ' ': if danger: drawSquare(x, y, dangerColor) else: drawSquare(x, y, palette[' ']) else: drawSquare(x, y, palette[displayGrid[y][x]]) def gameOver(): global game game = False for y in reversed(range(gridHeight)): for x in range(gridWidth): fill_rect(x * squareSize + borderSize + gridOffset, (y * squareSize) + borderSize, squareSize - borderSize, squareSize - borderSize, deadBlockColor) sleep(0.05) drawFrame(110, 95, 100, 30) draw_string('Game Over', 115, 100, dangerColor, frameColors[0]) while keydown(KEY_OK) or keydown(KEY_EXE): pass while not (keydown(KEY_OK) or keydown(KEY_EXE)): pass def mainMenu(): fill_rect(0, 0, WIDTH, HEIGHT, bgColor) drawFrame(30, 55, 250, 80) draw_string('Press OK', 120, 160, textColor, bgColor) title = ( 'ZZZ OOO TTT LL I SS', ' Z O T L L I S ', ' Z OO T LL I S ', ' Z O T L L I S', ' Z OOO T L L I SS ' ) for y in range(len(title)): for x in range(len(title[0])): if title[y][x] != ' ': drawSquare(x - 6, y + 6, palette[title[y][x]]) while keydown(KEY_OK) or keydown(KEY_EXE): pass while not (keydown(KEY_OK) or keydown(KEY_EXE)): pass while True: mainMenu() grid = createArray(gridWidth, gridHeight, ' ') displayGrid = createArray(gridWidth, gridHeight, ' ') game = True score = 0 level = 0 totalLines = 0 combo = -1 fallDelay = calculateFallDelay(0) blockQueue = [] for i in tetromino.keys(): blockQueue.insert(randint(0, len(list(tetromino.keys()))), i) blockShape = ' ' blockDiscard = [] drawInterface() blockX = 0 blockY = 0 blockRotation = 0 generateNewBlock() danger = False startTimeBlockFall = monotonic() timeMove = 0 rotationKeyUp = False while game: displayGrid = copyArray(grid) if monotonic() >= timeMove: if keydown(KEY_LEFT): moveBlock(-1) if keydown(KEY_RIGHT): moveBlock(1) if keydown(KEY_DOWN): timeMove = monotonic() + 0.1 blockFall() if rotationKeyUp: if keydown(KEY_OK) or keydown(KEY_EXE): rotationKeyUp = False rotateBlock() elif not (keydown(KEY_OK) or keydown(KEY_EXE)): rotationKeyUp = True if monotonic() >= startTimeBlockFall + fallDelay: startTimeBlockFall = monotonic() blockFall() for square in tetromino[blockShape][blockRotation]: displayGrid[square[1] + blockY][square[0] + blockX] = blockShape drawGrid() gameOver()