0% found this document useful (0 votes)
75 views4 pages

Codigo Tetris

This document defines classes for blocks in a Tetris-like game built with PyGame. It includes classes for different block shapes (SquareBlock, TBlock, etc.) that inherit from a Block class. This Block class handles movement, collision detection and drawing of each block. A BlocksGroup class is also defined to manage the full set of blocks and game logic.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
75 views4 pages

Codigo Tetris

This document defines classes for blocks in a Tetris-like game built with PyGame. It includes classes for different block shapes (SquareBlock, TBlock, etc.) that inherit from a Block class. This Block class handles movement, collision detection and drawing of each block. A BlocksGroup class is also defined to manage the full set of blocks and game logic.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 4

1

#!/usr/bin/env python self.color = random.choice((


# -*- coding: utf-8 -*- (200, 200, 200),
(215, 133, 133),
""" (30, 145, 255),
The classic Tetris developed using PyGame. (0, 170, 0),
Copyright (C) 2018 Recursos Python - recursospython.com. (180, 0, 140),
""" (200, 200, 0)
))
from collections import OrderedDict self.current = True
import random self.struct = np.array(self.struct)
# Initial random rotation and flip.
from pygame import Rect if random.randint(0, 1):
import pygame self.struct = np.rot90(self.struct)
import numpy as np if random.randint(0, 1):
# Flip in the X axis.
self.struct = np.flip(self.struct, 0)
WINDOW_WIDTH, WINDOW_HEIGHT = 500, 601 self._draw()
GRID_WIDTH, GRID_HEIGHT = 300, 600
TILE_SIZE = 30 def _draw(self, x=4, y=0):
width = len(self.struct[0]) * TILE_SIZE
height = len(self.struct) * TILE_SIZE
def remove_empty_columns(arr, _x_offset=0, _keep_counting=True): self.image = pygame.surface.Surface([width, height])
""" self.image.set_colorkey((0, 0, 0))
Remove empty columns from arr (i.e. those filled with zeros). # Position and size
The return value is (new_arr, x_offset), where x_offset is how self.rect = Rect(0, 0, width, height)
much the x coordinate needs to be increased in order to maintain self.x = x
the block's original position. self.y = y
""" for y, row in enumerate(self.struct):
for colid, col in enumerate(arr.T): for x, col in enumerate(row):
if col.max() == 0: if col:
if _keep_counting: pygame.draw.rect(
_x_offset += 1 self.image,
# Remove the current column and try again. self.color,
arr, _x_offset = remove_empty_columns( Rect(x*TILE_SIZE + 1, y*TILE_SIZE + 1,
np.delete(arr, colid, 1), _x_offset, _keep_counting) TILE_SIZE - 2, TILE_SIZE - 2)
break )
else: self._create_mask()
_keep_counting = False
return arr, _x_offset def redraw(self):
self._draw(self.x, self.y)

class BottomReached(Exception): def _create_mask(self):


pass """
Create the mask attribute from the main surface.
The mask is required to check collisions. This should be called
class TopReached(Exception): after the surface is created or update.
pass """
self.mask = pygame.mask.from_surface(self.image)

class Block(pygame.sprite.Sprite): def initial_draw(self):


raise NotImplementedError
@staticmethod
def collide(block, group): @property
""" def group(self):
Check if the specified block collides with some other block return self.groups()[0]
in the group.
""" @property
for other_block in group: def x(self):
# Ignore the current block which will always collide with itself. return self._x
if block == other_block:
continue @x.setter
if pygame.sprite.collide_mask(block, other_block) is not None: def x(self, value):
return True self._x = value
return False self.rect.left = value*TILE_SIZE

def __init__(self): @property


super().__init__() def y(self):
# Get a random color. return self._y
2

@y.setter class LineBlock(Block):


def y(self, value): struct = (
self._y = value (1,),
self.rect.top = value*TILE_SIZE (1,),
(1,),
def move_left(self, group): (1,)
self.x -= 1 )
# Check if we reached the left margin.
if self.x < 0 or Block.collide(self, group):
self.x += 1 class LBlock(Block):
struct = (
def move_right(self, group): (1, 1),
self.x += 1 (1, 0),
# Check if we reached the right margin or collided with another (1, 0),
# block. )
if self.rect.right > GRID_WIDTH or Block.collide(self, group):
# Rollback.
self.x -= 1 class ZBlock(Block):
struct = (
def move_down(self, group): (0, 1),
self.y += 1 (1, 1),
# Check if the block reached the bottom or collided with (1, 0),
# another one. )
if self.rect.bottom > GRID_HEIGHT or Block.collide(self, group):
# Rollback to the previous position.
self.y -= 1 class BlocksGroup(pygame.sprite.OrderedUpdates):
self.current = False
raise BottomReached @staticmethod
def get_random_block():
def rotate(self, group): return random.choice(
self.image = pygame.transform.rotate(self.image, 90) (SquareBlock, TBlock, LineBlock, LBlock, ZBlock))()
# Once rotated we need to update the size and position.
self.rect.width = self.image.get_width() def __init__(self, *args, **kwargs):
self.rect.height = self.image.get_height() super().__init__(self, *args, **kwargs)
self._create_mask() self._reset_grid()
# Check the new position doesn't exceed the limits or collide self._ignore_next_stop = False
# with other blocks and adjust it if necessary. self.score = 0
while self.rect.right > GRID_WIDTH: self.next_block = None
self.x -= 1 # Not really moving, just to initialize the attribute.
while self.rect.left < 0: self.stop_moving_current_block()
self.x += 1 # The first block.
while self.rect.bottom > GRID_HEIGHT: self._create_new_block()
self.y -= 1
while True: def _check_line_completion(self):
if not Block.collide(self, group): """
break Check each line of the grid and remove the ones that
self.y -= 1 are complete.
self.struct = np.rot90(self.struct) """
# Start checking from the bottom.
def update(self): for i, row in enumerate(self.grid[::-1]):
if self.current: if all(row):
self.move_down() self.score += 5
# Get the blocks affected by the line deletion and
# remove duplicates.
class SquareBlock(Block): affected_blocks = list(
struct = ( OrderedDict.fromkeys(self.grid[-1 - i]))
(1, 1),
(1, 1) for block, y_offset in affected_blocks:
) # Remove the block tiles which belong to the
# completed line.
block.struct = np.delete(block.struct, y_offset, 0)
class TBlock(Block): if block.struct.any():
struct = ( # Once removed, check if we have empty columns
(1, 1, 1), # since they need to be dropped.
(0, 1, 0) block.struct, x_offset = \
) remove_empty_columns(block.struct)
# Compensate the space gone with the columns to
3

# keep the block's original position. def move_current_block(self):


block.x += x_offset # First check if there's something to move.
# Force update. if self._current_block_movement_heading is None:
block.redraw() return
else: action = {
# If the struct is empty then the block is gone. pygame.K_DOWN: self.current_block.move_down,
self.remove(block) pygame.K_LEFT: self.current_block.move_left,
pygame.K_RIGHT: self.current_block.move_right
# Instead of checking which blocks need to be moved }
# once a line was completed, just try to move all of try:
# them. # Each function requires the group as the first argument
for block in self: # to check any possible collision.
# Except the current block. action[self._current_block_movement_heading](self)
if block.current: except BottomReached:
continue self.stop_moving_current_block()
# Pull down each block until it reaches the self._create_new_block()
# bottom or collides with another block. else:
while True: self.update_grid()
try:
block.move_down(self) def start_moving_current_block(self, key):
except BottomReached: if self._current_block_movement_heading is not None:
break self._ignore_next_stop = True
self._current_block_movement_heading = key
self.update_grid()
# Since we've updated the grid, now the i counter def stop_moving_current_block(self):
# is no longer valid, so call the function again if self._ignore_next_stop:
# to check if there're other completed lines in the self._ignore_next_stop = False
# new grid. else:
self._check_line_completion() self._current_block_movement_heading = None
break
def rotate_current_block(self):
def _reset_grid(self): # Prevent SquareBlocks rotation.
self.grid = [[0 for _ in range(10)] for _ in range(20)] if not isinstance(self.current_block, SquareBlock):
self.current_block.rotate(self)
def _create_new_block(self): self.update_grid()
new_block = self.next_block or BlocksGroup.get_random_block()
if Block.collide(new_block, self):
raise TopReached def draw_grid(background):
self.add(new_block) """Draw the background grid."""
self.next_block = BlocksGroup.get_random_block() grid_color = 50, 50, 50
self.update_grid() # Vertical lines.
self._check_line_completion() for i in range(11):
x = TILE_SIZE * i
def update_grid(self): pygame.draw.line(
self._reset_grid() background, grid_color, (x, 0), (x, GRID_HEIGHT)
for block in self: )
for y_offset, row in enumerate(block.struct): # Horizontal liens.
for x_offset, digit in enumerate(row): for i in range(21):
# Prevent replacing previous blocks. y = TILE_SIZE * i
if digit == 0: pygame.draw.line(
continue background, grid_color, (0, y), (GRID_WIDTH, y)
rowid = block.y + y_offset )
colid = block.x + x_offset
self.grid[rowid][colid] = (block, y_offset)
def draw_centered_surface(screen, surface, y):
@property screen.blit(surface, (400 - surface.get_width()/2, y))
def current_block(self):
return self.sprites()[-1]
def main():
def update_current_block(self): pygame.init()
try: pygame.display.set_caption("Tetris con PyGame")
self.current_block.move_down(self) screen = pygame.display.set_mode((WINDOW_WIDTH,
except BottomReached: WINDOW_HEIGHT))
self.stop_moving_current_block() run = True
self._create_new_block() paused = False
else: game_over = False
self.update_grid() # Create background.
background = pygame.Surface(screen.get_size())
4

bgcolor = (0, 0, 0) draw_centered_surface(screen, score_text, 270)


background.fill(bgcolor) if game_over:
# Draw the grid on top of the background. draw_centered_surface(screen, game_over_text, 360)
draw_grid(background) # Update.
# This makes blitting faster. pygame.display.flip()
background = background.convert()
pygame.quit()
try:
font = pygame.font.Font("Roboto-Regular.ttf", 20)
except OSError: if __name__ == "__main__":
# If the font file is not available, the default will be used. main()
pass
next_block_text = font.render(
"Siguiente figura:", True, (255, 255, 255), bgcolor)
score_msg_text = font.render(
"Puntaje:", True, (255, 255, 255), bgcolor)
game_over_text = font.render(
"¡Juego terminado!", True, (255, 220, 0), bgcolor)

# Event constants.
MOVEMENT_KEYS = pygame.K_LEFT, pygame.K_RIGHT, pygame.K_DOWN
EVENT_UPDATE_CURRENT_BLOCK = pygame.USEREVENT + 1
CODIGO DE
TETRIS EN
EVENT_MOVE_CURRENT_BLOCK = pygame.USEREVENT + 2
pygame.time.set_timer(EVENT_UPDATE_CURRENT_BLOCK, 1000)
pygame.time.set_timer(EVENT_MOVE_CURRENT_BLOCK, 100)

blocks = BlocksGroup()

while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
PYTHON
run = False
break
elif event.type == pygame.KEYUP:
if not paused and not game_over:
if event.key in MOVEMENT_KEYS:
blocks.stop_moving_current_block()
elif event.key == pygame.K_UP:
blocks.rotate_current_block()
if event.key == pygame.K_p:
paused = not paused

# Stop moving blocks if the game is over or paused.


if game_over or paused:
continue

if event.type == pygame.KEYDOWN:
if event.key in MOVEMENT_KEYS:
blocks.start_moving_current_block(event.key)

try:
if event.type == EVENT_UPDATE_CURRENT_BLOCK:
blocks.update_current_block()
elif event.type == EVENT_MOVE_CURRENT_BLOCK:
blocks.move_current_block()
except TopReached:
game_over = True

# Draw background and grid.


screen.blit(background, (0, 0))
# Blocks.
blocks.draw(screen)
# Sidebar with misc. information.
draw_centered_surface(screen, next_block_text, 50)
draw_centered_surface(screen, blocks.next_block.image, 100)
draw_centered_surface(screen, score_msg_text, 240)
score_text = font.render(
str(blocks.score), True, (255, 255, 255), bgcolor)

You might also like