Advertisement
cookertron

Tetris Clone using Python & Pygame

Oct 29th, 2018
362
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 19.34 KB | None | 0 0
  1. # want to join a pygame community? visit fb.com/groups/pygame
  2.  
  3. import math, random, sys, time
  4. import pygame
  5. import pygame.gfxdraw
  6. from pygame.locals import *
  7.  
  8. # define some colors
  9. FUCHSIA = (255,   0, 255)
  10. PURPLE  = (128,   0, 128)
  11. TEAL    = (  0, 128, 128)
  12. LIME    = (  0, 255,   0)
  13. GREEN   = (  0, 128,   0)
  14. OLIVE   = (128, 128,   0)
  15. YELLOW  = (255, 255,   0)
  16. ORANGE  = (255, 165,   0)
  17. RED     = (255,   0,   0)
  18. MAROON  = (128,   0,   0)
  19. SILVER  = (192, 192, 192)
  20. GREY    = (128, 128, 128)
  21. NIGHT   = (32,   32,  32)
  22. BLUE    = (  0,   0, 255)
  23. NAVY    = (  0,   0, 128)
  24. AQUA    = (  0, 255, 255)
  25. WHITE   = (255, 255, 255)
  26. BLACK   = (  0,   0,   0)
  27.  
  28. # define some data
  29. BLOCKS = {1: {'startingY' : [0, 2, 0, 2], 'block' : [[(-1, 0), (0, 0), (1, 0), (2, 0)], [(1, -2), (1, -1), (1, 0), (1, 1)], [(-1, 0), (0, 0), (1, 0), (2, 0)], [(0, -2), (0, -1), (0, 0), (0, 1)]]}, # long one
  30.           2: {'startingY' : [0, 1, 0, 1], 'block' : [[(-1, 0), (0, 0), (1, 0), (0, 1)], [(-1, 0), (0, -1), (0, 0), (0, 1)], [(0, 0), (-1, 1), (0, 1), (1, 1)], [(0, -1), (0, 0), (0, 1), (1, 0)]]}, # T shape
  31.           3: {'startingY' : [0, 1, 0, 1], 'block' : [[(-1, 1), (-1, 0), (0, 0), (1, 0)], [(-1, -1), (0, -1), (0, 0), (0, 1)], [(1, 0), (-1, 1), (0, 1), (1, 1)], [(0, -1), (0, 0), (0, 1), (1, 1)]]}, # L left
  32.           4: {'startingY' : [0, 1, 0, 1], 'block' : [[(-1, 0), (0, 0), (1, 0), (1, 1)], [(0, -1), (0, 0), (0, 1), (-1, 1)], [(-1, 0), (-1, 1), (0, 1), (1, 1)], [(0, -1), (0, 0), (0, 1), (1, -1)]]}, # L right
  33.           5: {'startingY' : [0, 1, 0, 1], 'block' : [[(-1, 0), (0, 0), (0, 1), (1, 1)], [(1, -1), (1, 0), (0, 0), (0, 1)], [(-1, 0), (0, 0), (0, 1), (1, 1)], [(0, -1), (0, 0), (-1, 0), (-1, 1)]]}, # lightening 1
  34.           6: {'startingY' : [0, 1, 0, 1], 'block' : [[(-1, 1), (0, 1), (0, 0), (1, 0)], [(0, -1), (0, 0), (1, 0), (1, 1)], [(-1, 1), (0, 1), (0, 0), (1, 0)], [(-1, -1), (-1, 0), (0, 0), (0, 1)]]}, # lightening 2
  35.           7: {'startingY' : [1], 'block' : [[(-1, -1), (0, -1), (-1, 0), (0, 0)]]}, # square
  36.  
  37.           }
  38.  
  39. BLOCK_COLORS = [BLACK, RED, TEAL, ORANGE, NAVY, PURPLE, GREEN, YELLOW]
  40.  
  41. SCORES = [40, 100, 300, 1200]
  42.  
  43. tetrisFont = {
  44.     'width' : 10,
  45.     'height' : 14,
  46.     'space' : 4,
  47.     'totalWidth' : 14,
  48.     'a' : [[0, 5, 5, 0], [5, 0, 10, 5], [10, 5, 10, 14], [0, 7, 10, 7], [0, 14, 0, 5]],
  49.     'b' : [[0, 0, 5, 0], [5, 0, 10, 4], [10, 4, 5, 7], [5, 7, 10, 10], [10, 10, 5, 14], [5, 14, 0, 14], [0, 14, 0, 0]],
  50.     'c' : [[0, 0, 10, 0], [0, 0, 0, 14], [0, 14, 10, 14]],
  51.     'd' : [[0, 0, 5, 0], [5, 0, 10, 5], [10, 5, 10, 9], [10, 9, 5, 14], [5, 14, 0, 14], [0, 14, 0, 0]],
  52.     'e' : [[0, 0, 10, 0], [0, 0, 0, 14], [0, 7, 8, 7], [0, 14, 10, 14]],
  53.     'f' : [[0, 0, 10, 0], [0, 0, 0, 14], [0, 7, 8, 7]],
  54.     'g' : [[0, 0, 10, 0], [0, 0, 0, 14], [0, 14, 10, 14], [10, 14, 10, 9], [10, 9, 8, 7]],
  55.     'h' : [[0, 0, 0, 14], [0, 7, 10, 7], [10, 0, 10, 14]],
  56.     'i' : [[0, 0, 10, 0], [5, 0, 5, 14], [0, 14, 10, 14]],
  57.     'j' : [[0, 9, 5, 14], [5, 14, 10, 14], [10, 14, 10, 0]],
  58.     'k' : [[0, 0, 0, 14], [0, 7, 10, 0], [0, 7, 10,14]],
  59.     'l' : [[0, 0, 0, 14], [0, 14, 10, 14]],
  60.     'm' : [[0, 14, 0, 0], [0, 0, 5, 5], [5, 5, 10, 0], [10, 0, 10, 14]],
  61.     'n' : [[0, 14, 0, 0], [0, 0, 10, 14], [10, 14, 10, 0]],
  62.     'o' : [[0, 0, 10, 0], [10, 0, 10, 14], [10, 14, 0, 14], [0, 14, 0, 0]],
  63.     'p' : [[0, 14, 0, 0], [0, 0, 10, 0], [10, 0, 10, 7], [10, 7, 0, 9]],
  64.     'q' : [[0, 0, 10, 0], [10, 0, 10, 9], [10, 9, 0, 14], [0, 14, 0, 0], [5, 9, 10, 14]],
  65.     'r' : [[0, 14, 0, 0], [0, 0, 10, 0], [10, 0, 10, 7], [10, 7, 0, 9], [5, 8, 10, 14]],
  66.     's' : [[0, 12, 3, 14], [3, 14, 10, 14], [10, 14, 10, 9], [10, 9, 0, 5], [0, 5, 0, 0], [0, 0, 7, 0], [7, 0, 10, 2]],
  67.     't' : [[0, 0, 10, 0], [5, 0, 5, 14]],
  68.     'u' : [[0, 0, 0, 9], [0, 9, 5, 14], [5, 14, 10,9], [10, 9, 10, 0]],
  69.     'v' : [[0, 0, 5, 14], [5, 14, 10, 0]],
  70.     'w' : [[0, 0, 3, 14], [3, 14, 5, 9], [5, 9, 7, 14], [7, 14, 10, 0]],
  71.     'x' : [[0, 0, 10, 14], [0, 14, 10, 0]],
  72.     'y' : [[0, 0, 5, 7], [10, 0, 5, 7], [5, 7, 5, 14]],
  73.     'z' : [[0, 0, 10, 0], [10, 0, 0, 14], [0, 14, 10, 14]],
  74.     '0' : [[0, 0, 10, 0], [10, 0, 10, 14], [10, 14, 0, 14], [0, 14, 0, 0], [0, 14, 10, 0]],
  75.     '1' : [[3, 3, 5, 0], [5, 0, 5, 14]],
  76.     '2' : [[0, 0, 10, 0], [10, 0, 10, 5], [10, 5, 0, 9], [0, 9, 0, 14], [0, 14, 10, 14]],
  77.     '3' : [[0, 0, 10, 0], [10, 0, 10, 14], [10, 14, 0, 14], [0, 7, 10, 7]],
  78.     '4' : [[0, 0, 0, 7], [0, 7, 10, 7], [10, 0, 10, 14]],
  79.     '5' : [[10, 0, 0, 0], [0, 0, 0, 5], [0, 5, 10, 9], [10, 9, 10, 14], [10, 14, 0, 14]],
  80.     '6' : [[0, 0, 0, 14], [0, 14, 10, 14], [10, 14, 10, 9], [10, 9, 0, 5]],
  81.     '7' : [[0, 0, 10, 0], [10, 0, 10, 7], [10, 7, 5, 14]],
  82.     '8' : [[0, 0, 10, 0], [10, 0, 10, 14], [10, 14, 0, 14], [0, 14, 0, 0], [0, 7, 10, 7]],
  83.     '9' : [[10, 14, 10, 0], [10, 0, 0, 0], [0, 0, 0, 5], [0, 5, 10, 9]],
  84.     ':' : [[5, 3, 5, 5], [5, 9, 5, 11]],
  85.     '.' : [[5, 11, 5, 14]],
  86.     '!' : [[5, 0, 5, 7], [5, 11, 5, 14]],
  87. }
  88.  
  89. # exit the program
  90. def quit():
  91.     for event in pygame.event.get():
  92.         if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
  93.             return True
  94.     return False
  95.  
  96.    
  97. # this function creates a surface using a line "vector" font. the background is transparent
  98. def fontSurface(font, text, scale, fgColor):
  99.     width = math.ceil(len(text) * (font['totalWidth'] * scale))
  100.     height = math.ceil(font['height'] * scale)
  101.  
  102.     surface = pygame.Surface((width, height + 1))
  103.     offset = 0
  104.     for char in text:
  105.         if char != ' ':
  106.             lines = font[char]
  107.             for line in lines:
  108.                 sx, sy, ex, ey = (line[0] * scale) + offset, (line[1] * scale), (line[2] * scale) + offset, (line[3] * scale)
  109.                 pygame.draw.line(surface, fgColor, (sx, sy), (ex, ey), 1)
  110.         offset += font['totalWidth'] * scale
  111.     surface.set_colorkey(0)
  112.     return surface
  113.  
  114. # this function draws the block preview above the score and level
  115. def miniBlock(x, y, blockID):
  116.     global BLOCK_COLORS, BLOCKS
  117.    
  118.     surface = pygame.display.get_surface() # get the primary display surface
  119.    
  120.     block = BLOCKS[blockID]['block'][0] # create a shortcut to the next block, rotation 0
  121.     for bit in block:
  122.         pygame.draw.rect(surface, BLOCK_COLORS[blockID], (x + bit[0] * 7, y + bit[1] * 7, 7, 7), 0) # draw block bits to display
  123.    
  124. # draw a patterned tile
  125. # this class creates a tile of diagonal lines and can fill the entire display with it
  126. # i was going to add a function that drew the tiles in definable area but didn't need it for this project
  127. class tiles:
  128.     def __init__(self):
  129.         global NIGHT, W, H
  130.        
  131.         self.tileSize = 32
  132.         self.tile = pygame.Surface((self.tileSize, self.tileSize))
  133.        
  134.         # draw the lines
  135.         for linePos in range(0, self.tileSize * 2, 8):
  136.             pygame.draw.line(self.tile, NIGHT, (linePos, 0), (linePos - self.tileSize, self.tileSize), 1)
  137.  
  138.         # get the width and height of the display in tiles     
  139.         self.displayWidthInTiles = (W / self.tileSize) * self.tileSize
  140.         if W % self.tileSize > 0: self.displayWidthInTiles += self.tileSize
  141.  
  142.         self.displayHeightInTiles = (H / self.tileSize) * self.tileSize
  143.         if H % self.tileSize > 0: self.displayHeightInTiles += self.tileSize
  144.    
  145.     # fill the display with tiles
  146.     def fillDisplay(self, displaySurf):
  147.         global AREAS, W, H
  148.         for tilePosY in range(0, self.displayHeightInTiles, self.tileSize):
  149.             for tilePosX in range(0, self.displayWidthInTiles, self.tileSize):
  150.                 displaySurf.blit(self.tile, (tilePosX, tilePosY))
  151.         AREAS.append((0, 0, W, H)) # add the entire display to the update list for pygame.display.update(list) in the main loop
  152.  
  153. # this class is responsible for the "zone" in which the blocks appear and fall
  154. class blockZone:
  155.     def __init__(self):
  156.         global BLOCKS, W, H
  157.        
  158.         #define the area in which the blocks fall and stack in
  159.         self.height = (int(H * 0.9722222222222222) / 20) * 20
  160.         self.topMargin = (H - self.height) / 2     
  161.  
  162.         self.blockSize = int(float(self.height) / 20)
  163.        
  164.         self.width = self.blockSize * 10
  165.         self.leftMargin = (W - self.width) / 2
  166.        
  167.         # the display surface for the blocks to be draw to
  168.         self.bzSurf = pygame.Surface((self.width, self.height))
  169.    
  170.         # this array is to hold block information 0 = no block, 1+ = block
  171.         self.stack = [[0 for x in range(10)] for y in range(20)]
  172.        
  173.         # starting variables
  174.         self.currentBlock = random.randint(1, 7)
  175.         self.rotation = 0
  176.         self.blockPosX = 5
  177.         self.blockPosY = BLOCKS[self.currentBlock]['startingY'][self.rotation]
  178.         self.nextBlock = random.randint(1, 7)
  179.        
  180.     # this functions blits the block zone surface to the primary display
  181.     def blitToDisplay(self, displaySurf):
  182.         global AREAS
  183.         displaySurf.blit(self.bzSurf, (self.leftMargin, self.topMargin))
  184.         AREAS.append((self.leftMargin, self.topMargin, self.width, self.height)) # add the zone rect to the dislay update list
  185.    
  186.     # draw stack takes the information from the stack array and draws a colored rectangle at x * blocksize and y * blocksize
  187.     # on to the block zone surface
  188.     def drawStack(self):
  189.         global BLOCK_COLORS
  190.         for x in range(10):
  191.             for y in range(20):
  192.                 pygame.draw.rect(self.bzSurf, BLOCK_COLORS[self.stack[y][x]], (x * self.blockSize, y * self.blockSize, self.blockSize, self.blockSize), 0)
  193.    
  194.     # draw blocks is probably mislabelled as it doesn't draw the block to any surface but writes the blocks ID to the stack
  195.     # if the delete parameter is true then the current block is removed from the stack by filling the stack with 0's
  196.     def drawBlock(self, delete = False):
  197.         global BLOCKS, BLOCK_COLORS
  198.        
  199.         block = BLOCKS[self.currentBlock]['block'][self.rotation]
  200.         if delete:
  201.             blockID = 0
  202.         else:
  203.             blockID = self.currentBlock
  204.            
  205.         for bit in block:
  206.             y = self.blockPosY + bit[1]
  207.             self.stack[y][self.blockPosX + bit[0]] = blockID
  208.    
  209.     # fall moves the block down one square
  210.     # because falling can trigger collisions the function checks for these in three ways:
  211.     # falling: does blocky+1 encounters stacked blocks or the block zone floor?
  212.     # full lines: if a block joins the stack and completes lines
  213.     # spawn: if a block spawn into the stack (game over)
  214.     def fall(self):
  215.         global BLOCKS, previousScore, gameOver
  216.         self.drawBlock(True) # delete the current block from the stack so it doesn't detect itself
  217.         if self.collisionOnFall(): # if block y + 1 encounters the stack
  218.             self.drawBlock() # redraw the block onto the stack
  219.            
  220.             # test for complete lines
  221.             self.testForFullLines()
  222.            
  223.             # set up new block to spawn
  224.             self.currentBlock = self.nextBlock
  225.             self.nextBlock = random.randint(1, 7)
  226.             self.rotation = 0
  227.             self.blockPosY = BLOCKS[self.currentBlock]['startingY'][0]
  228.             self.blockPosX = 5
  229.            
  230.             # test for spawn collision (game over)
  231.             gameOver = self.collisionOnMove(False)
  232.             self.drawBlock() # draw newly spawn block to the stack
  233.            
  234.             # because calling the fall() function is fairly low down in the main
  235.             # loop the drawStack has to be called otherwise the newly spawned block
  236.             # isn't shown
  237.             if gameOver:
  238.                 self.drawStack()
  239.            
  240.             # make previous score different to the actual to trigger a display update of the score 
  241.             previousScore = -1
  242.         else:
  243.             # if no collision has taken place then it's ok to fall
  244.             self.blockPosY += 1
  245.             self.drawBlock()
  246.    
  247.     # collision on fall tests for collision if the current blocks y pos is incremented by one
  248.     def collisionOnFall(self):
  249.         global BLOCKS
  250.         block = BLOCKS[self.currentBlock]['block'][self.rotation]
  251.        
  252.         tempY = self.blockPosY + 1 # no need to change the real blockPosY as it's just a test
  253.         # scroll through the current block's bit to see if they "hit" a block on the stack
  254.         for bit in block:
  255.             x = self.blockPosX + bit[0]
  256.             y = tempY + bit[1]
  257.             if y > 19: return True # has the block reached the bottom of the block zone
  258.             if self.stack[y][x] > 0: return True # a hit has occurred
  259.        
  260.         # no collision has taken place
  261.         return False
  262.    
  263.     # this function does two things. It temporarily rotates the block and/or
  264.     # performs a collision test at the block's current location
  265.     # if the rotate parameter is False no the block will not be rotated before testing for collision
  266.     def collisionOnMove(self, rotate = True):
  267.         global BLOCKS
  268.        
  269.         if rotate:
  270.             block = BLOCKS[self.currentBlock]['block'][(self.rotation + 1) % len(BLOCKS[self.currentBlock]['block'])]
  271.         else:
  272.             block = BLOCKS[self.currentBlock]['block'][self.rotation]
  273.            
  274.         for bit in block:
  275.             x = self.blockPosX + bit[0]
  276.             y = self.blockPosY + bit[1]
  277.             if x > 9 or x < 0 or y > 19 or y < 0: return True # check if the block is outside of the block zone
  278.             if self.stack[y][x] > 0: return True # collision with stacked blocks
  279.         # no collision
  280.         return False
  281.    
  282.     # when a block has settled onto the stack, the stack is tested for complete lines
  283.     # to do this a loop searches for a 0 in the x array of the stack: if 0 in stack[y]
  284.     # for example [1, 2, 2, 1, 5, 0, 0, 0, 0, 1] would not be a complete line because it has a zero in it
  285.     def testForFullLines(self):
  286.         global SCORES, score, level, lines, FPS, SPF
  287.        
  288.         popList = [] # this is to keep track of the complete lines that need removing from the stack
  289.         for y in range(19, 0, -1): # start at the bottom of the stack and work upwards
  290.             if 0 in self.stack[y]: # ignore lines with 0 in
  291.                 continue
  292.             popList.append(y) # add the line number to the popList
  293.         # if lines need deleting then they can't be deleted while iterating through the list
  294.         # because it messes with the list's indexing so to counteract this a second for loop
  295.         # goes through the popList and "pop"s them of the stack
  296.         if popList:
  297.             for y in popList: # pop the complete lines off the stack
  298.                 self.stack.pop(y)
  299.             for y in popList: # insert the same number of blank lines at the beginning of the stack, index 0
  300.                 self.stack.insert(0, [0 for x in range(10)])
  301.            
  302.             # update the score and the completed line count. every 10 completed lines increases the level
  303.             # and speed of the falling blocks
  304.             score += SCORES[len(popList) - 1] * level
  305.             lines += len(popList)
  306.             if lines >= 10: # next level?
  307.                 lines %= 10
  308.                 level += 1
  309.                 FPS += 0.3 # increase the frame rate by 0.3fps
  310.     # rotates the current block if there's no collision in doing so        
  311.     def rotateBlock(self):
  312.         global BLOCKS
  313.         if not self.collisionOnMove(): # test for collision
  314.             self.rotation += 1
  315.             self.rotation %= len(BLOCKS[self.currentBlock]['block'])
  316.    
  317. # define display surface           
  318. W, H = 1280, 720
  319. HW, HH = W / 2, H / 2
  320.  
  321. # initialise display
  322. pygame.init()
  323. FONT = pygame.font.SysFont(None, 72)
  324. DS = pygame.display.set_mode((W, H))
  325. pygame.display.set_caption("Tetris Clone")
  326. FPS = 1
  327. SPF = 1.00 / FPS
  328.  
  329. # the areas of the display that need updating
  330. AREAS = []
  331.  
  332. # create the background tiles instance
  333. TILES = tiles()
  334. TILES.fillDisplay(DS)
  335.  
  336. BZ = blockZone()
  337. BZ.drawBlock()
  338.  
  339. # draw the tetris logo against the side of the stack window
  340. logo = fontSurface(tetrisFont, "tetris", 8, WHITE)
  341. verticalLogo = pygame.transform.rotate(logo, 90)
  342. DS.blit(verticalLogo, (BZ.leftMargin - 10 - verticalLogo.get_rect().width, 10))
  343.  
  344. # copy a section of the background for the score and level
  345. scorePosX = BZ.leftMargin + BZ.width + 10
  346. scoreRect = (scorePosX, BZ.topMargin, 48, BZ.height)
  347. scoreBackground = pygame.Surface(scoreRect[2:4])
  348. scoreBackground.blit(DS, (0, 0), scoreRect)
  349.  
  350. # track the score
  351. previousScore = -1
  352. score = 0
  353.  
  354. # track the level
  355. level = 1
  356.  
  357. # track number of lines completed
  358. lines = 0
  359.  
  360. # monitor key held
  361. keyHeld = False
  362.  
  363. # is it curtains?
  364. gameOver = False
  365.  
  366. # start FPS monitoring
  367. FPSTime = time.time()
  368.  
  369. # main loop
  370. while not quit() and not gameOver: # esc key or x triggers a game over
  371.     k = pygame.key.get_pressed() # get the pressed keys
  372.    
  373.     if k[K_DOWN]:
  374.         SPF = 0.05 # increase the frame rate to speed up the falling block
  375.     else:
  376.         SPF = 1.00 / FPS # set the frame rate of the falling block
  377.  
  378.     # because the main loop is running at full frames per second, hold a key down and moving
  379.     # a block or rotating it would be ridiculously fast so the action can't be repeated until
  380.     # the player and released the key and pressed it again. keyHeld needs to be False
  381.     if k[K_LEFT] and not keyHeld:
  382.         BZ.drawBlock(True) # delete the current block from the stack
  383.         BZ.blockPosX -= 1 # move the current block left
  384.         if BZ.collisionOnMove(False): # check for collision
  385.             BZ.blockPosX += 1 # if collision return position to original location before pressing left
  386.         BZ.drawBlock() # redraw block to the stack
  387.    
  388.     # same as above but to the right
  389.     if k[K_RIGHT] and not keyHeld:
  390.         BZ.drawBlock(True)         
  391.         BZ.blockPosX += 1
  392.         if BZ.collisionOnMove(False):
  393.             BZ.blockPosX -= 1
  394.         BZ.drawBlock()
  395.    
  396.     # rotate the current block
  397.     if k[K_UP] and not keyHeld:
  398.         BZ.drawBlock(True)
  399.         BZ.rotateBlock()
  400.         BZ.drawBlock()
  401.    
  402.     # save a snapshot of the game and exit (for debugging really)
  403.     if k[K_RETURN]:
  404.         pygame.image.save(DS, "screenshot.png")
  405.         pygame.quit()
  406.         sys.exit()
  407.  
  408.     # when a key is pressed it has to be released and pressed again to repeat the action.
  409.     # when calling pygame.key.get_pressed() a list is returned representing all the keys on the keyboard.
  410.     # each key on the keyboard has it's own place in the list. spacebar for instance is at index 32,
  411.     # so k[32] will give you the value 1 if space is pressed or 0 if it isn't. because the only keys
  412.     # used in the game are the arrow keys we can test them all at once to see if they're pressed.
  413.     # k[273:277] represent all 4 arrow keys in the list. If any of them are pressed then the keyHeld is
  414.     # set to True and no other action can be taken by the player until they release the key and press it again.
  415.     if True in k[273:277]:
  416.         keyHeld = True
  417.     else:
  418.         keyHeld = False
  419.  
  420.     # draw the stack the block zone surface
  421.     BZ.drawStack()
  422.     BZ.blitToDisplay(DS) # blit the block zone surface to the primary display
  423.  
  424.     # if the score has changed then cover the score area with tiled background we grabbed before the main loop
  425.     # create the score and level surface, rotate it and blit it to the primary display.
  426.     # draw the mini block preview to the primary display and add the score area to the AREAS list so it's
  427.     # updated when pygame.display.update() is called.
  428.     if score != previousScore:
  429.         previousScore = score
  430.         scoreSurf = fontSurface(tetrisFont, "score: {0}  level: {1}".format(score, level), 2, WHITE)
  431.        
  432.         DS.blit(scoreBackground, (scorePosX, BZ.topMargin))
  433.         DS.blit(pygame.transform.rotate(scoreSurf, -90), (scorePosX + 10, BZ.topMargin + 48))
  434.         miniBlock(scorePosX + 17, BZ.topMargin + 10, BZ.nextBlock)
  435.         AREAS.append(scoreRect)
  436.    
  437.     # this limits the falling blocks to a frame rate:
  438.     if time.time() > FPSTime + SPF:
  439.         BZ.fall()
  440.         FPSTime = time.time()
  441.    
  442.     # only update the areas of the display listed in the AREAS list (saves CPU)
  443.     pygame.display.update(AREAS)
  444.     AREAS = []
  445.        
  446. # game over section, display "game over" and "press esc to quit" over the block zone
  447. gameOverScale = float(BZ.width) / 140
  448. gameOverSurface = fontSurface(tetrisFont, "game over!", gameOverScale, WHITE)
  449. DS.blit(gameOverSurface, (BZ.leftMargin + 10, BZ.topMargin + 10))
  450.  
  451. pressEscapeScale = float(BZ.width) / 238
  452. pressEscapeSurface = fontSurface(tetrisFont, "press esc to quit", pressEscapeScale, WHITE)
  453. DS.blit(pressEscapeSurface, (BZ.leftMargin, BZ.topMargin + 20 + gameOverSurface.get_rect().height))
  454.  
  455. # update the block zone area
  456. pygame.display.update([(BZ.leftMargin, BZ.topMargin + 10, BZ.width, BZ.height)])
  457.  
  458. # wait until esc is pressed
  459. while not quit():
  460.     pass
  461.  
  462. # blit the block zone over the "game over" and "escape to quit" text for the screenshot
  463. DS.blit(BZ.bzSurf, (BZ.leftMargin, BZ.topMargin))
  464. # save the display surface to disk using a timestamp for the filename. use png in this case
  465. # for smaller files sizes
  466. pygame.image.save(DS, "screenshot-{0}.png".format(time.strftime("%Y%m%d%H%M%S")))
  467.  
  468. # quit 
  469. pygame.quit()
  470. sys.exit()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement