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
, 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.