Archivo

Archivo para 10/06/2011

Versión preliminar de Tetris en Python

Con el tiempo me he dado cuenta que no soy nada bueno con los títulos, bueno tampoco soy bueno con los comentarios, y tampoco bueno con las entradas :P , pero bueno que vamos a hacer.

En esta ocasión les traigo mi versión ultra-hiper básica de un tetris implementado utilizando Python, Numpy y Pygame. Este es mi primer intento y puede que más adelante me de la rabia y lo vuelva a re-hacer de cero. Como les iba diciendo esta versión se encuentra aún en pañales pero hace lo básico.

Esta el tablero dibujado,  lanza piezas al azar que nosotros podemos ir rotando, permite acelerar la caída de la pieza, muestra siguiente pieza, reconoce otras piezas que están en el tablero, detecta fila completa y la procesa. Que faltaría, añadir un contador de tiempo, mostrar puntaje, permitir re-dimensionar el tablero, reiniciar juego y acelerar la caída de las piezas al cumplir n filas completadas; creo que eso es todo por el momento.

Espero tener pronto una versión más refinada, pero igual les dejo mi código para que lo prueben y utilicen con total libertad. Quedo dividido en 3 archivos: tetris.py(archivo principal), figure.py y board.py.

Para ejecutar: python tetris.py

tetris.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Filename: tetris.py
# Developer by: @itrogeno
# Date: 29-03-2011
# Version: 0.4a

'''Programando una version personalizada de Tetris
   para Python 2.6. Utilizando las librerias
   Numpy y Pygame.'''

# Cargamos las librerias
import pygame
import numpy

from board  import Board
from figure import Figure

# Constantes
TITLE = 'Tetris'
VERSION = '0.2a'
WIDTH, HEIGHT = SIZE = (600, 420) # definimos el tamano de la ventana
CENTER_X = WIDTH/2
FRAMERATE = 30

# Tablero
ROWS, COLS = SHAPE = (20, 10)
PIXEL = 20

# No se utilizaran los colores Negro(0, 0, 0), ni blanco(255, 255, 255)
# que se utilizan respectivamente como color de fondo y para dibujar
# el tablero. Utilice el comando len(COLORS) para obtener el numero
# de colores disponibles.
BG_COLOR = (  0,   0,   0) # Negro
FG_COLOR = (255, 255, 255) # Blanco

COLORS = {
     0 : BG_COLOR,
     1 : (  0,   0, 255), # Azul
     2 : (  0, 255,   0), # Verde
     3 : (  0, 255, 255), # Aqua
     4 : (255,   0,   0), # Rojo
     5 : (255,   0, 255), # Magenta
     6 : (255, 255,   0), # Amarillo
     7 : (200, 200, 200), # Gris
     8 : FG_COLOR,
     9 : BG_COLOR,
    10 : FG_COLOR
}

# Variables
screen, clock, running, board, figure, row = None, None, None, None, None, None
lock, speed = False, 1.0

def events(events):
    '''Procesa todos los eventos producidos en el
       juego.'''
    global running, figure, row, lock, speed

    for event in events:
        if event.type == pygame.QUIT:
            # Finalizamos la ejecucion del juego
            # cuando el usuario cierra la ventana.
            running = False

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                # Finalizamos la ejecución del juego
                # cuando el usuario presiona la tecla
                # escape.
                running = False
            if event.key == pygame.K_DOWN and not lock:
                #Bloquear teclas
                speed = 10.0
                lock = True
            if event.key == pygame.K_UP and not lock:
                # Rotar la figura
                figure.rotate()
            if event.key == pygame.K_LEFT and not lock:
                # Mover la figura a la izquierda
                figure.move(-1)
            if event.key == pygame.K_RIGHT and not lock:
                # Mover la figura a la derecha
                figure.move(1)
            if event.key == pygame.K_c:
                # Limpiar tablero
                board.clear()
                del figure
                row = 0
                figure = Figure(board.get_board())

# Desde donde empezamos a dibujar el tablero
X, Y = CENTER_X - (PIXEL*COLS/2), 10

def get_xy(col, row, x=None, y=None):
    x = X if x is None else x
    y = Y if y is None else y
    return x + (col * PIXEL), y + (row * PIXEL)

def draw_line(screen, col, row, X=0, Y=0, x=None, y=None):
    x, y = get_xy(col, row, x, y)
    pygame.draw.line(screen, FG_COLOR, (x, y), (x+X, y+Y))

def draw_rect(screen, color, col, row, x=None, y=None):
    x, y  = get_xy(col, row, x, y)
    pygame.draw.rect(screen, COLORS[color], (x, y, PIXEL, PIXEL))

def draw_board(board):
    rows, cols = board.shape
    '''Dibujamos el tablero'''
    for col in range(cols):
        for row in range(rows):
            draw_rect(screen, board[row, col], col, row)
            draw_line(screen, col, row, X=PIXEL)
            draw_line(screen, col, row, Y=PIXEL)
    draw_line(screen, 0, row+1, X=PIXEL*cols)
    draw_line(screen, col+1, 0, Y=PIXEL*rows)

def draw_figure(figure, color):
    x, y, = 450, 10
    rows, cols = figure.shape
    '''Dibujamos la figura'''
    for col in range(cols):
        for row in range(rows):
            if figure[row, col] != 0:
                draw_rect(screen, color, col, row, x=x, y=y)
            #draw_line(screen, col, row, X=PIXEL, x=x, y=y)
            #draw_line(screen, col, row, Y=PIXEL, x=x, y=y)
    #draw_line(screen, 0, row+1, X=PIXEL*cols, x=x, y=y)
    #Sdraw_line(screen, col+1, 0, Y=PIXEL*rows, x=x, y=y)

def refresh(board, framerate):
    draw_board(board.get_board())
    pygame.display.flip()
    clock.tick(framerate)

def init():
    '''Este metodo permite inicializar las librerias y
       variables del juegos.'''
    global screen, clock, running, board

    running = True
    board = Board(SHAPE)

    pygame.init()
    screen = pygame.display.set_mode(SIZE)
    clock = pygame.time.Clock()
    pygame.display.set_caption('%s v%s' % (TITLE, VERSION))

def main():
    global row, figure, lock, speed

    '''Metodo Principal'''
    init()
    row = 0
    completed = 0
    rows = 0
    figure = Figure(board.get_board())
    next   = Figure(board.get_board())
    while running:
        screen.fill(BG_COLOR)
        draw_figure(next.get_figure(), next.nro_figure)
        figure.clear(board.get_board())
        events(pygame.event.get())
        if not figure.forward(int(row)):
            figure.update(board.get_board())
            del figure
            figure = next
            row = 0
            while board.evaluate(8):
                completed += 1
                refresh(board, FRAMERATE/4)
                board.evaluate(9)
                refresh(board, FRAMERATE/4)
                board.evaluate(10)
                refresh(board, FRAMERATE/4)
                board.evaluate(0)
                refresh(board, FRAMERATE/4)
                board.update()
                refresh(board, FRAMERATE/4)
            next = Figure(board.get_board())
            figure.set_board(board.get_board())
            speed = 1.0
            lock = False
        figure.update(board.get_board())
        refresh(board, FRAMERATE)
        row += (speed/FRAMERATE) * 2.0
    print 'Fin del Juego.'

if __name__ == '__main__':
    main()

figure.py

# -*- coding: utf-8 -*-
# Filename: figure.py
# Developer by: @itrogeno
# Date: 29-03-2011
# Version: 0.4a
import random
import numpy

class Figure():
    # Definimos las diferentes figuras
    FIGURES = {
        1: ('0 0 0 0; 0 1 1 0; 0 1 1 0; 0 0 0 0',),

        2: ('0 0 0 0; 1 1 1 1; 0 0 0 0; 0 0 0 0',
            '0 0 1 0; 0 0 1 0; 0 0 1 0; 0 0 1 0'),

        3: ('0 0 0 0; 0 0 1 1; 0 1 1 0; 0 0 0 0',
            '0 0 1 0; 0 0 1 1; 0 0 0 1; 0 0 0 0'),

        4: ('0 0 0 0; 0 1 1 0; 0 0 1 1; 0 0 0 0',
            '0 0 0 1; 0 0 1 1; 0 0 1 0; 0 0 0 0'),

        5: ('0 0 0 0; 0 1 1 1; 0 1 0 0; 0 0 0 0',
            '0 0 1 0; 0 0 1 0; 0 0 1 1; 0 0 0 0',
            '0 0 0 1; 0 1 1 1; 0 0 0 0; 0 0 0 0',
            '0 1 1 0; 0 0 1 0; 0 0 1 0; 0 0 0 0'),

        6: ('0 0 0 0; 0 1 1 1; 0 0 0 1; 0 0 0 0',
            '0 0 1 1; 0 0 1 0; 0 0 1 0; 0 0 0 0',
            '0 1 0 0; 0 1 1 1; 0 0 0 0; 0 0 0 0',
            '0 0 1 0; 0 0 1 0; 0 1 1 0; 0 0 0 0'),

        7: ('0 0 0 0; 0 1 1 1; 0 0 1 0; 0 0 0 0',
            '0 0 1 0; 0 0 1 1; 0 0 1 0; 0 0 0 0',
            '0 0 1 0; 0 1 1 1; 0 0 0 0; 0 0 0 0',
            '0 0 1 0; 0 1 1 0; 0 0 1 0; 0 0 0 0')
    }

    ROWS_FIGURE = 4
    COLS_FIGURE = 4
    CLS_FIGURE  = 0

    angle = 0 # angulo inicial

    def __init__(self, board):
        self.set_board(board)
        self.rows, self.cols = self.shape = self.board.shape
        self.nro_row = -1
        self.nro_col = (self.cols / 2) -1
        self.nro_figure = random.randint(1, len(self.FIGURES))
        print 'Figura: %i' % self.nro_figure

    def set_board(self, board):
        self.board = board.copy()

    def get_figure(self, angle=None):
        angle = self.angle if angle is None else angle
        figure = numpy.matrix(self.FIGURES[self.nro_figure][angle])
        return figure

    def forward(self, mov):
        if self.nro_row == mov or\
           self.__evaluate(mov, self.nro_col, self.get_figure()):
            self.nro_row = mov
            return True
        else:
            return False

    def move(self, mov):
        if self.__evaluate(self.nro_row, self.nro_col+mov, self.get_figure()):
            self.nro_col += mov

    def rotate(self):
        nro_col = self.nro_col
        angle = self.__next_angle()

        i = 3
        while i >= 0:
            if self.__evaluate(self.nro_row, nro_col, self.get_figure(angle)):
                self.angle = angle
                self.nro_col = nro_col
                i = 0
            else:
                nro_col = self.nro_col -1 if i == 1 else nro_col + 1
            i -= 1

    def update(self, board, clear=False):
        figure = self.get_figure()
        nro_row, nro_col = self.nro_row, self.nro_col
        for x in range(self.ROWS_FIGURE):
            tmp_col = nro_col
            for y in range(self.COLS_FIGURE):
                if self.__is_valid(nro_row, tmp_col) and\
                   figure[(self.ROWS_FIGURE - x)-1, y] != 0:
                    if nro_row >= 0:
                        board[nro_row, tmp_col] = self.CLS_FIGURE if clear else self.nro_figure
                tmp_col += 1
            nro_row -= 1

    def clear(self, board):
        self.update(board, True)

    def __is_valid(self, nro_row, nro_col):
        return nro_row < self.rows and\
               nro_col >= 0 and nro_col < self.cols

    def __evaluate(self, nro_row, nro_col, figure):
        for x in range(self.ROWS_FIGURE):
            tmp_col = nro_col
            for y in range(self.COLS_FIGURE):
                if not self.__is_valid(nro_row, tmp_col) and\
                   figure[(self.ROWS_FIGURE - x)-1, y] != 0:
                    return False

                if self.__is_valid(nro_row, tmp_col) and nro_row >= 0 and\
                   figure[(self.ROWS_FIGURE - x)-1, y] != 0 and\
                   self.board[nro_row, tmp_col] != 0:
                    return False
                tmp_col += 1
            nro_row -= 1
        return True

    def __next_angle(self):
        angle = self.angle + 1
        return 0 if angle >= len(self.FIGURES[self.nro_figure]) else angle

board.py

# -*- coding: utf-8 -*-
# Filename: board.py
# Developer by: @itrogeno
# Date: 29-03-2011
# Version: 0.4a

import numpy

class Board():

    def __init__(self, shape):
        self.array = numpy.zeros(shape, numpy.int)

    def clear(self):
        self.array.fill(0)

    def get_board(self):
        return self.array

    def evaluate(self, color):
        rows, cols = self.array.shape
        for y in range(rows):
            row = self.array[(rows-y)-1]
            complete = True
            for x in range(len(row)):
                if row[x] == 0:
                    complete = False
                    break
            if complete:
                for x in range(len(row)):
                    row[x] = color
                return True
        return False

    def update(self):
        rows, cols = self.array.shape
        for y in range(rows):
            row = self.array[(rows-y)-1]
            if row.sum() == 0:
                if (rows-(y+1))-1 >= 0:
                    self.array[(rows-y)-1] = self.array[(rows-(y+1))-1]
                    self.array[(rows-(y+1))-1] = numpy.zeros(cols, numpy.int)

Bueno, esto es todo por hoy y no olviden si tienen cualquier duda o comentario no duden en Realizar.

Adiós.

Seguir

Get every new post delivered to your Inbox.