Valhalla Legends Forums Archive | General Programming | [Python] Enjoy

AuthorMessageTime
Noodlez
I've been trying to get into game development lately, and I just happened to write Tetris. I give you the first working version of my version of Tetris written in Python. This is my first ever application written in Python, I expect it to be ineffecient and have many flaws. The game play is nearly bug free. Even if you don't know Python, I suggest looking over the code as it gives great insight towards the development of games.

I'm trying to get into the habbit of commenting my code (from what I understand, it's a must in professional coding) so everything relevant should be commented.
[code]
# Tetris :: David Kachlon
#
# Project start : June 30, 2004
# Last edited : July 08, 2004
# Began Running : July 04, 2004
# Project finish: , 2004


# Thanks to $t0rm for CopyList class.

from Tkinter import *
import copy
import sys
import time
import random

p_shapes = {}
p_shapes['square'] = 0
p_shapes['straight'] = 1
p_shapes['middle finger'] = 2
p_shapes['L'] = 3
p_shapes['S'] = 4
p_shapes['Z'] = 5

b_color = {}
b_color[0] = 'blue'
b_color[1] = 'red'
b_color[2] = 'brown'
b_color[3] = 'pink'
b_color[4] = 'lightblue'
b_color[5] = 'green'



class CopyList(list):
def __init__(self, *args):
list.__init__(self, list(args))
def __pow__(self, n):
newlist = CopyList()
for i in range(0, n):
newlist.extend(copy.deepcopy(self))
return newlist

class tetris:
def __init__( self, level ):
self.level = level # Level determines how fast cubes drop.
def kill( self, event ):
self.canvas.delete(self.intro)
self.canvas.unbind(self.leftintro)
self.canvas.create_text(305,10,fill='black', text="Next Piece")
self.canvas.create_rectangle(255,150,350,25) # A block which displays the next piece
self.canvas.create_text(300,200, fill = 'black', text="Level: 1")
self.g_score = self.canvas.create_text(301,215, fill = 'black', text="Score: 0")
self.canvas.unbind("<Button-1>")
self.brick = bricks(random.randrange(0,5)) # Generate the first brick
self.next_brick = bricks(random.randrange(0,5)) #Randomly generate the second one
self.boar = board( self.canvas, self.g_score ) # Initialiaze the game board

#
# Assign the arrow keys to their respectful actions.
#
self.root.bind("<Left>", self.boar.move_left)
self.root.bind("<Right>", self.boar.move_right)
self.root.bind("<Up>", self.boar.rotate_piece)
self.root.bind("<Down>", self.boar.piece_speed)
self.lastpiece = self.boar.next_piece( self.next_brick, self.canvas ) # Draw the second coming brick in the box
self.boar.spawn_piece(self.brick, self.canvas) # Spawn the initial brick
while (self.boar.winning):

self.next_brick = bricks(random.randrange(0,5))
self.boar.next_piece( self.next_brick, self.canvas )
self.boar.spawn_piece(self.next_brick, self.canvas)
self.boar.clean_up()
self.canvas.create_text(150, 250,fill='red',text='YOU LOSE')

def quit( self ):
self.root.destroy()
def new_game ( self ):
self.quit()
self.init()
def init ( self, kill=0 ):
if (kill==0):
self.root = Tk()
self.canvas = Canvas( self.root, width=352, height=545, background='white')
self.canvas.pack()
self.canvas.create_rectangle(0,0,250,650,fill='black')
self.root.title('Tetris')
self.intro = self.canvas.create_text(150, 250,fill='red',text='Click mouse to begin')
self.menubar = Menu(self.root)
self.filemenu = Menu(self.menubar, tearoff=0)
self.filemenu.add_command(label="New Game", command=self.new_game)
self.filemenu.add_separator()
self.filemenu.add_command(label="Exit", command=self.quit)
self.menubar.add_cascade(label="File", menu=self.filemenu)
self.root.config(menu=self.menubar)
self.leftintro = self.canvas.bind("<Button-1>", self.kill)
class bricks:
def __init__( self, shape ):
self.shape = shape
self.coords = CopyList(CopyList(0) ** 1) ** 4
self.returns = CopyList(CopyList(0) ** 1) ** 4
self.done = 0
for x in range(4):
self.coords[x].append(1)
self.returns[x].append(1)

if (shape==0):
self.shapematrix = [[1]*2]*2
self.rotate=0
#square
elif (shape==1):
self.shapematrix = [[1]*4]
self.rotate=0
#straight
elif (shape==2):
self.shapematrix = [[1],[1]]
for x in range(2):
self.shapematrix[0].append(1)
self.shapematrix[1].append(1)
self.rotate=0
#middle finger
self.shapematrix[0][0] = 0
self.shapematrix[0][2] = 0
elif (shape==3):
self.shapematrix = [[1],[1]]
for x in range(2):
self.shapematrix[0].append(1)
self.shapematrix[1].append(1)
self.rotate=0
self.shapematrix[1][0] = 0
self.shapematrix[1][1] = 0
# L
elif (shape==4):
self.shapematrix = [[1],[1]]
for x in range(2):
self.shapematrix[0].append(1)
self.shapematrix[1].append(1)
self.rotate=0
self.shapematrix[0][2] = 0
self.shapematrix[1][0] = 0
# S
elif (shape==5):
self.shapematrix = [[1],[1]]
for x in range(2):
self.shapematrix[0].append(1)
self.shapematrix[1].append(1)
self.rotate=0
self.shapematrix[0][0] = 0
self.shapematrix[1][2] = 0
# Z

class board:

def __init__( self, canvas, xscore , width=10, height=22, ):
self.canvas = canvas
self.matrix = CopyList(CopyList(0) ** height) ** width
self.debug2 = 0
self.stillworking = 0
self.pause = .1
self.keypos = 0
self.move = 0
self.hit_bottom = 0
self.winning = 1
self.lastpiece = 0
self.currentpiece = bricks(0)
self.bricks = []
self.score = 0
self.t_score = xscore
def next_piece( self, piece, canvas ):
for x in range(len(piece.shapematrix)):
for y in range(len(piece.shapematrix[x])):
if (piece.shapematrix[x][y]==1):
return canvas.create_rectangle(280+x*25,y*25+50,280+x*25+25,y*25+25+50,fill=b_color[piece.shape])

def spawn_piece( self, piece, canvas ):
i = -1
self.currentpiece = piece
self.bricks.append(piece)
for x in range(len(piece.shapematrix)):
for y in range(len(piece.shapematrix[x])):
if (piece.shapematrix[x][y]==1): # Pass shape to other matrix
self.matrix[x][y] = piece.shapematrix[x][y]
i += 1
piece.coords[i] = [x,y]
piece.returns[i][1] = y
self.draw_matrix( piece, canvas )
canvas.update()
self.debug2 = 1
while(self.stillworking==0):
time.sleep(self.pause)
self.move_piece( piece )
self.draw_matrix( piece, canvas )
canvas.update()
self.check_loss()
self.stillworking = 0
## self.debug2 = 0
# self.board_comp('a',self.matrix)
#self.draw_matrix( piece, canvas )
return 0

def draw_matrix( self, piece, canvas ):
for x in range(len(piece.returns)):
if (piece.returns[x][0] == 0):
piece.returns[x][0] = canvas.create_rectangle(piece.coords[x][0]*25,piece.coords[x][1]*25,piece.coords[x][0]*25+25,piece.coords[x][1]*25+25,fill=b_color[piece.shape])
else:
if (piece.returns[x][1]!=0):
canvas.coords(piece.returns[x][0],piece.coords[x][0]*25,piece.coords[x][1]*25,piece.coords[x][0]*25+25,piece.coords[x][1]*25+25)

def delete_piece ( self, piece, canvas ):
for x in range(len(self.matrix)-1):
for y in range(len(self.matrix[x])-1):
if (self.matrix[x][y] == 0):
if (piece.coords[x][y]!=0):
canvas.delete(piece.coords[x][y])

def rotate_piece ( self, events ):
for x in range(len(self.currentpiece.coords)):
temp1 = self.currentpiece.coords[x][0]
temp = self.currentpiece.coords[x][1]
self.currentpiece.coords[x][0] = temp
self.currentpiece.coords[x][1] = temp1
temp1, temp = 0,0
## def move_piece( self, p ):
## for x in range(len(self.matrix)-1,-1, -1):
## for y in range(len(self.matrix[x])-1,-1, -1):
## if (self.matrix[x][y]==1): # It's a 1, then it's a piece that needs to be moved
## if (y!=len(self.matrix[x])-1): # If y=len(self.matrix[x]) then the piece is on the floor
##
## if (self.matrix[x][y+1]==1): # Check if there is a piece below
## self.matrix[x][y] = 0 # Erase the block from the original matrix
## self.new_matrix[x+self.move][y+1] = 1 # Put the new position on the new matrix
## p.coords[x+self.move][y+1] = p.coords[x][y]
## p.coords[x][y] = 0
## self.stillworking = 0
## else:
## self.new_matrix[x][y]=2
## self.stillworking = 1
## self.hit_bottom = 1
## break
## else:
## self.new_matrix[x][y] = 2 # 2 will remain stationary
## self.hit_bottom = 1
## self.stillworking = 1
## break
def clean_up( self ):
for x in range(len(self.matrix)):
for y in range(len(self.matrix[x])):
self.matrix[x][y] = 0
for x in range(len(self.bricks)):
for y in range(len(self.bricks[x].returns)):
self.canvas.delete(self.bricks[x].returns[y][0])
print 'all clean'

def move_piece( self, piece):

for x in range(len(piece.coords)):
x1 = piece.coords[x][0]
y1 = piece.coords[x][1]
if (piece.coords[x][1]==21): # Piece is on the floor.
piece.done = 1
self.stillworking = 1
elif (self.matrix[x1][y1+1]==2): # Theres a piece under, stop moving
piece.done = 1
self.stillworking = 1
if (self.move == 1):
if (y1<21) and (x1<9):
if (self.matrix[x1+1][y1]==2) or (self.matrix[x1+1][y1+1]==2):
self.move = 0
if (self.move == -1):
if (y1<21) and (x1<9):
if (self.matrix[x1-1][y1]==2) or (self.matrix[x1-1][y1+1]==2):
self.move = 0
if (piece.coords[x][0]==9) and (self.move == 1): # Move right, make sure it stops at the wall
self.move = 0
if (piece.coords[x][0]==0) and (self.move == -1): # Move left, make sure it stops at the wall
self.move = 0
if (piece.done!=1):
for x in range(len(piece.coords)):
if (piece.coords[x][1]!=21):
self.matrix[piece.coords[x][0]][piece.coords[x][1]] = 0
piece.coords[x][1] = int(piece.coords[x][1]) + 1
piece.coords[x][0] = int(piece.coords[x][0]) + self.move
piece.returns[x][1] = piece.coords[x][1]
else:
for x in range(len(piece.coords)):
self.matrix[piece.coords[x][0]][piece.coords[x][1]] = 2

self.move = 0
self.pause = .1
self.check_line( )

def check_line ( self ): # Check the bottom row for lines.
lines = [0] * 22
for x in range(len(self.matrix)):
for y in range(len(self.matrix[x])):
lines[y] += self.matrix[x][y]
for x in range(len(lines)):
if (lines[x]==20):
self.kill_line( x )

def check_loss ( self ):
loss = 0
for x in range(10):
if (self.matrix[0][x] == 2):
self.winning = 0

def kill_line ( self, line ): # Kill the last line, to make sure that no other lines exist
self.score += 100
canvas = self.canvas
canvas.itemconfigure(self.t_score, text="Score: " + str(self.score))
for x in range(10): # Mark deleted line as gone
self.matrix[x][line] = 0
for y in range( line, 0, -1 ):
for x in range(10):
self.matrix[x][y] = self.matrix[x][y-1]
for x in range(10):
self.matrix[x][0] = 0
for x in range(len(self.bricks)):
for y in range(len(self.bricks[x].coords)):
if (self.bricks[x].returns[y][1] == line):
canvas.delete(self.bricks[x].returns[y][0])
self.bricks[x].returns[y][1] = 0
for x in range(len(self.bricks)):
for y in range(len(self.bricks[x].coords)):
if (self.bricks[x].returns[y][1] < line):
self.bricks[x].coords[y][1] = int(self.bricks[x].coords[y][1])+1
self.draw_matrix( self.bricks[x], canvas )

###################################################
### Keyboard controls ###
###################################################

def move_left( self, event ):
self.move = -1

def move_right( self, event ):
self.move = 1

def piece_speed( self, event ):
self.score += 20
print self.score
self.pause = .0

def board_comp( self, label, m ):
one_count = 0
for x in range(len(m)):
for y in range(len(m[x])):
if (m[x][y] ==2 ):
one_count += 1

print label, one_count

t=tetris(1)
t.init(0)
[/code]

Corrections/Optimizations are more then welcome.
July 8, 2004, 2:10 PM
St0rm.iD
I couldn't get it to run initially; you need a call to mainloop() somewhere (I put it at the end).

If you want startup code to run, you should do it like this at the end of your module:
[code]
if __name__ == "__main__":
# call your main method or something
[/code]

#1 optimization you can do: Install http://psyco.sf.net, and then add these little lines at the top to give your program a massive speed boost
[code]
try:
import psyco; psyco.full()
except:
pass
[/code]

Oh yeah, I can't rotate the bricks. BTW, people generally capitalize their class names and use underscores in method names, but that's just a preference.
July 8, 2004, 6:16 PM
Noodlez
Oh yea, I forgot to put mainloop() back, I took it off because it was making debugging a problem. Brick rotation in the version I pasted is pretty flawed. I became aware of the naming conventions of classes and methods mid-program and decided not to worry about it. Thanks for psyco though.
July 8, 2004, 6:54 PM

Search