cookertron

Tetris Stackoverflow question

Apr 25th, 2021
187
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 12.49 KB | None | 0 0
  1. import sys, time, random, os, base64
  2. import pygame
  3.  
  4. BLACK = (0, 0, 0)
  5. WHITE = (255, 255, 255)
  6. RED = (128, 0, 0)
  7. GREEN = (0, 128, 0)
  8. BLUE = (0, 0, 128)
  9. DARK_GRAY = (32, 32, 32)
  10. LOVELY_BLUE = (22, 54, 83)
  11.  
  12. SPRITE_DATA_FILENAME = "blocks.png"
  13. SPRITE_DATA =   b'iVBORw0KGgoAAAANSUhEUgAAACoAAAAGCAMAAACCc/wkAAAAAXNSR0IArs4c6QAAAGBQTFRFAAA\
  14.                AIiA0RSg8Zjkxj1Y733Em2aBm7sOa+/I2meVQar4wN5RuS2kvUkskMjw5Pz90MGCCW27hY5v/X83k\
  15.                y9v8////m623hH6HaWpqWVZSdkKKrDIy2Vdj13u6j5dKim8w+2O8zwAAACB0Uk5TAP///////////\
  16.                /////////////////////////////+Smq12AAAAPklEQVQYlWMQJRowiDIAAYRGIxmgJEQVhIDwEB\
  17.                KiUA1gBFYFUQYXYhBF0oBQCrMHyQEMokgkQgOSCxiI9xYACEgN8+zH0H8AAAAASUVORK5CYII='
  18.  
  19. SHAPES = [
  20.     [
  21.         [
  22.             [0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0] # O
  23.         ]
  24.     ],
  25.     [
  26.         [
  27.             [0, 0, 0, 0], [2, 2, 2, 2], [0, 0, 0, 0], [0, 0, 0, 0] # I
  28.         ],
  29.         [
  30.             [0, 0, 2, 0], [0, 0, 2, 0], [0, 0, 2, 0], [0, 0, 2, 0]
  31.         ]
  32.     ],
  33.     [
  34.         [
  35.             [0, 0, 0, 0], [0, 0, 3, 3], [0, 3, 3, 0], [0, 0, 0, 0] # S
  36.         ],
  37.         [
  38.             [0, 0, 3, 0], [0, 0, 3, 3], [0, 0, 0, 3], [0, 0, 0, 0]
  39.         ]        
  40.     ],
  41.     [
  42.         [
  43.             [0, 0, 0, 0], [0, 4, 4, 0], [0, 0, 4, 4], [0, 0, 0, 0] # Z
  44.         ],
  45.         [
  46.             [0, 0, 0, 4], [0, 0, 4, 4], [0, 0, 4, 0], [0, 0, 0, 0]
  47.         ]        
  48.     ],    
  49.     [
  50.         [
  51.             [0, 0, 0, 0], [0, 5, 5, 5], [0, 5, 0, 0], [0, 0, 0, 0] # L
  52.         ],
  53.         [
  54.             [0, 0, 5, 0], [0, 0, 5, 0], [0, 0, 5, 5], [0, 0, 0, 0]
  55.         ],
  56.         [
  57.             [0, 0, 0, 5], [0, 5, 5, 5], [0, 0, 0, 0], [0, 0, 0, 0]
  58.         ],
  59.         [
  60.             [0, 5, 5, 0], [0, 0, 5, 0], [0, 0, 5, 0], [0, 0, 0, 0]
  61.         ]
  62.     ],
  63.     [
  64.         [
  65.             [0, 0, 0, 0], [0, 6, 6, 6], [0, 0, 0, 6], [0, 0, 0, 0] # J
  66.         ],
  67.         [
  68.             [0, 0, 6, 6], [0, 0, 6, 0], [0, 0, 6, 0], [0, 0, 0, 0]
  69.         ],
  70.         [
  71.             [0, 6, 0, 0], [0, 6, 6, 6], [0, 0, 0, 0], [0, 0, 0, 0]
  72.         ],
  73.         [
  74.             [0, 0, 6, 0], [0, 0, 6, 0], [0, 6, 6, 0], [0, 0, 0, 0]
  75.         ]
  76.     ],
  77.     [
  78.         [
  79.             [0, 0, 0, 0], [0, 7, 7, 7], [0, 0, 7, 0], [0, 0, 0, 0] # T
  80.         ],
  81.         [
  82.             [0, 0, 7, 0], [0, 0, 7, 7], [0, 0, 7, 0], [0, 0, 0, 0]
  83.         ],
  84.         [
  85.             [0, 0, 7, 0], [0, 7, 7, 7], [0, 0, 0, 0], [0, 0, 0, 0]
  86.         ],
  87.         [
  88.             [0, 0, 7, 0], [0, 7, 7, 0], [0, 0, 7, 0], [0, 0, 0, 0]
  89.         ]
  90.     ],        
  91. ]
  92.  
  93. # handy way of working with coordinate integers
  94. class vect:
  95.     def __init__(s, x, y = None):
  96.         if type(x) is tuple:
  97.             s.x = int(x[0])
  98.             s.y = int(x[1])
  99.         else:
  100.             s.x = int(x)
  101.             if y == None:
  102.                 s.y = s.x
  103.             else:
  104.                 s.y = int(y)
  105.         s.v = (s.x, s.y)
  106.    
  107.     def __add__(s, n):
  108.         if type(n) is tuple:
  109.             return vect(s.x + n[0], s.y + n[1])
  110.         elif type(n) is vect:
  111.             return vect(s.x + n.x, s.y + n.y)
  112.         else:
  113.             return vect(s.x + n, s.y + n)
  114.  
  115.     def __sub__(s, n):
  116.         if type(n) is tuple:
  117.             return vect(s.x - n[0], s.y - n[1])
  118.         elif type(n) is vect:
  119.             return vect(s.x - n.x, s.y - n.y)
  120.         else:
  121.             return vect(s.x - n, s.y - n)
  122.  
  123.     def __mul__(s, n):
  124.         if type(n) is tuple:
  125.             return vect(s.x * n[0], s.y * n[1])
  126.         elif type(n) is vect:
  127.             return vect(s.x * n.x, s.y * n.y)
  128.         else:
  129.             return vect(s.x * n, s.y * n)
  130.  
  131.     def __truediv__(s, n):
  132.         if type(n) is tuple:
  133.             return vect(s.x / n[0], s.y / n[1])
  134.         elif type(n) is vect:
  135.             return vect(s.x / n.x, s.y / n.y)
  136.         else:
  137.             return vect(s.x / n, s.y / n)
  138.  
  139.     def __str__(s):
  140.         return "({}, {})".format(s.x, s.y)
  141.  
  142.     def setX(s, x):
  143.         s.x = x
  144.         s.v = (x, s.y)
  145.    
  146.     def setY(s, y):
  147.         s.y = y
  148.         s.v = (s.x, y)
  149.  
  150. class spritesheet:
  151.     def __init__(s, surface, cols, rows, colorkey = None):
  152.         # sprite sheet surface
  153.         s.sheet = surface
  154.        
  155.         # get width and height
  156.         s.sheetSize = vect(surface.get_rect().size)
  157.        
  158.         # n rows and n columns
  159.         s.grid = vect(cols, rows)
  160.        
  161.         # list to references the subsurfaces of the spritesheet.
  162.         s.sprites = []
  163.  
  164.         # size of each individual sprite
  165.         s.cellSize = s.sheetSize / s.grid
  166.  
  167.         # iterate through spritesheet to define individual subsurfaces
  168.         for y in range(rows):
  169.             for x in range(cols):
  170.                 # get position of each sprite
  171.                 pos = vect(x, y) * s.cellSize
  172.                
  173.                 # add subsprite to the list
  174.                 s.sprites.append(surface.subsurface((pos.v, s.cellSize.v)))
  175.  
  176.     # return a sprite by id
  177.     def get(s, id):
  178.         return s.sprites[id]
  179.  
  180.  
  181. class shape:
  182.     def __init__(s, id):
  183.         global SHAPES
  184.  
  185.         # shape id
  186.         s.id = id
  187.  
  188.         # rotation
  189.         s.r = 0
  190.  
  191.         # maxium number of available shape rotations
  192.         s.maxr = len(SHAPES[id])
  193.  
  194.         # if the top row of the shape is blank then y = -1
  195.         if sum(SHAPES[id][0][0]):
  196.             s.pos = vect(3, 0)
  197.         else:
  198.             s.pos = vect(3, -1)
  199.  
  200.     def draw(s, addBlur = False):
  201.         global PAS # play area surface
  202.         global SHAPES
  203.  
  204.         for y in range(4):
  205.             for x in range(4):
  206.                 bid = SHAPES[s.id][s.r][y][x] # block sprite id
  207.                 # blit sprite to play area surface if bid > 0
  208.                 if bid:
  209.                     if addBlur:
  210.                         PAS.blit(SPRITES.get(7), ((s.pos + vect(x, y - 1)) * SPRITES.cellSize).v)
  211.                     PAS.blit(SPRITES.get(bid - 1), ((s.pos + vect(x, y)) * SPRITES.cellSize).v)
  212.  
  213.     def collision(s, offset, rOffset = None):
  214.         global PLAY_AREA, PLAY_AREA_SIZE, SHAPES
  215.  
  216.         # check if collision involves shape rotation
  217.         if rOffset == None:
  218.             # grab a copy of the shape data if using current rotation value
  219.             shapeData = SHAPES[s.id][s.r]
  220.         else:
  221.             # adjust rotation if it has gone out of bounds
  222.             rotation = s.r + rOffset
  223.             if rotation >= s.maxr: rotation = 0
  224.             elif rotation < 0: rotation = s.maxr - 1
  225.  
  226.             # grab a copy of the shape data using new rotation value
  227.             shapeData = SHAPES[s.id][rotation]
  228.  
  229.         # iterate throught the shape data to determine:
  230.         # 1. has the shape collided with the wall?
  231.         # 2. has the shape collided with the floor?
  232.         # 3. does the shape rotate out of the ceiling?
  233.         # 3. has the shape collided with the stack?
  234.  
  235.         for y in range(4):
  236.             for x in range(4):
  237.                 # calculate the absolute position of each block within the shape.
  238.                 pos = s.pos + vect(x, y) + offset
  239.  
  240.                 # if the shape data is a block ie has a value >0
  241.                 if shapeData[y][x]:
  242.                     if pos.x < 0 or pos.x > PLAY_AREA_SIZE.x - 1: return "WALL"
  243.                     if pos.y > PLAY_AREA_SIZE.y - 1: return "FLOOR"
  244.                     if pos.y < 0: return "CEILING"
  245.                     if PLAY_AREA[pos.y][pos.x]: return "STACK"
  246.        
  247.         # no collision detected
  248.         return "OK"
  249.  
  250.     # add the shape to play areas data
  251.     def solidify(s):
  252.         global SHAPES, PLAY_AREA
  253.         for y in range(4):
  254.  
  255.             for x in range(4):
  256.                 # get the block id from the shape data
  257.                 bid = SHAPES[s.id][s.r][y][x]
  258.                 # if the block data >0
  259.                 if bid:
  260.                     # get the absolution position of the shape's block in the play area
  261.                     pos = s.pos + (x, y)
  262.                     # copy the block data to the play area
  263.                     PLAY_AREA[pos.y][pos.x] = bid
  264.  
  265.     # increment the rotation clockwise by one
  266.     def rotate(s):
  267.         s.r += 1
  268.         if s.r >= s.maxr: s.r = 0
  269.        
  270.  
  271. # start pygame and create primary display surface (PDS)
  272. pygame.init()
  273. PDR = pygame.Rect(0, 0, 1024, 720)
  274. PDS = pygame.display.set_mode(PDR.size)
  275. FPS = 30
  276.  
  277. # check if the blocks.png exists. If not then create it
  278. if not os.path.exists(SPRITE_DATA_FILENAME):
  279.     print("Writing title image...", end = "")
  280.     data = base64.b64decode(SPRITE_DATA)
  281.     r = open(SPRITE_DATA_FILENAME, "bw")
  282.     r.write(data)
  283.     r.close()
  284.     print("Done!")
  285.  
  286. # load the spritesheet
  287. BLOCK_SPRITES = pygame.image.load("blocks.png").convert_alpha()
  288. SPRITES = spritesheet(BLOCK_SPRITES, 8, 1)
  289.  
  290. # define the scale of the graphics
  291. SCALE = vect(5)
  292.  
  293. # define the size in blocks of the play area
  294. PLAY_AREA_SIZE = vect(10, 20)
  295.  
  296. # define the play area data. A 2 dimensional list
  297. PLAY_AREA = [[0] * PLAY_AREA_SIZE.x for y in range(PLAY_AREA_SIZE.y)]
  298.  
  299. # the size of play area surface in pixels
  300. PLAY_AREA_PIXEL_SIZE = PLAY_AREA_SIZE * SPRITES.cellSize
  301.  
  302. # scaled size of the play area in pixels
  303. SCALED_PLAY_AREA_PIXEL_SIZE = PLAY_AREA_PIXEL_SIZE * SCALE
  304. SCALED_PLAY_AREA_PLACEMENT = (vect(PDR.size) - SCALED_PLAY_AREA_PIXEL_SIZE) / 2
  305.  
  306. # create play area surface
  307. PAS = pygame.Surface(PLAY_AREA_PIXEL_SIZE.v)
  308.  
  309. # define the shape
  310. currentShape = shape(random.randint(0, 6))
  311.  
  312. # drop down one line timer
  313. TIMER_DURATION = 15
  314. timer = TIMER_DURATION
  315.  
  316. # prevent the shape spining like a catherine wheel if the up key if held down
  317. rotateKeyDown = False
  318.  
  319. # add blur lines if the player presses down key
  320. drawDropLines = False
  321.  
  322. while True:
  323.     events = pygame.event.get()
  324.     for e in events:
  325.         if e.type == pygame.QUIT: sys.exit()
  326.  
  327.     # clear the play area
  328.     PAS.fill(LOVELY_BLUE)
  329.  
  330.     # draw the current shape
  331.     currentShape.draw(drawDropLines)
  332.  
  333.     # translate the play area data into sprites IDs to blit of the play area surface
  334.     for y in range(PLAY_AREA_SIZE.y):
  335.         for x in range(PLAY_AREA_SIZE.x):
  336.  
  337.             # get data from the play area located at y,x            
  338.             id = PLAY_AREA[y][x]
  339.  
  340.             # if the data in the play area list is >0 then translate to sprite ID and blit
  341.             if id:
  342.                 PAS.blit(SPRITES.get(id - 1), (vect(x, y) * SPRITES.cellSize).v)
  343.  
  344.     # update the display
  345.     PDS.blit(pygame.transform.scale(PAS, SCALED_PLAY_AREA_PIXEL_SIZE.v), SCALED_PLAY_AREA_PLACEMENT.v)
  346.     pygame.display.update()
  347.     pygame.time.Clock().tick(FPS)
  348.  
  349.     # decrement the drop timer
  350.     timer -= 1
  351.  
  352.     # if the drop timer reaches zero set the y velocity to 1 (ie drops one line)
  353.     if timer <= 0:
  354.         timer = TIMER_DURATION
  355.         drawDropLines = False
  356.         collisionResults = currentShape.collision((0, 1))
  357.         if collisionResults == "FLOOR" or collisionResults == "STACK":
  358.             # add the shape to the play area's data
  359.             currentShape.solidify()
  360.             currentShape = shape(random.randint(0, 6))
  361.  
  362.             """ STACKOVER FLOW QUESTION START """
  363.             newPLAY_AREA = []
  364.             fullRowsCount = 0
  365.             for row in reversed(PLAY_AREA):
  366.                 rowBlockCount = sum(col > 0 for col in row)
  367.                 if rowBlockCount < PLAY_AREA_SIZE.x:
  368.                     newPLAY_AREA = [row] + newPLAY_AREA
  369.                 else:
  370.                     fullRowsCount += 1
  371.             if fullRowsCount:
  372.                 newPLAY_AREA = [[0] * PLAY_AREA_SIZE.x] * fullRowsCount + newPLAY_AREA
  373.                 PLAY_AREA = newPLAY_AREA
  374.                 del newPLAY_AREA
  375.             """ STACKOVER FLOW QUESTION END """
  376.            
  377.             continue
  378.  
  379.         # move the shape down if no collision with the stack
  380.         currentShape.pos += (0, 1)
  381.  
  382.     # get key presses and move/rotate the current shape
  383.     k = pygame.key.get_pressed()
  384.     if k[pygame.K_RIGHT]:
  385.         if currentShape.collision((1, 0)) == "OK":
  386.             currentShape.pos += (1, 0)
  387.     elif k[pygame.K_LEFT]:
  388.         if currentShape.collision((-1, 0)) == "OK":
  389.             currentShape.pos += (-1, 0)
  390.     if k[pygame.K_UP] and not rotateKeyDown:
  391.         rotateKeyDown = True
  392.         if currentShape.collision((0, 0), 1) == "OK":
  393.             currentShape.rotate()
  394.     elif not k[pygame.K_UP] and rotateKeyDown:
  395.         rotateKeyDown = False
  396.     if k[pygame.K_DOWN]:
  397.         timer = 0
  398.         drawDropLines = True
  399.     else:
  400.         drawDropLines = False
  401.  
  402.     if k[pygame.K_SPACE]:
  403.         pass
  404.  
  405.  
  406. pygame.quit()
  407. sys.exit()
Add Comment
Please, Sign In to add comment