Author | Message | Time |
---|---|---|
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 |