• Welcome to Valhalla Legends Archive.
 

[Python] Enjoy

Started by Noodlez, July 08, 2004, 09:10 AM

Previous topic - Next topic

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.

# 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)


Corrections/Optimizations are more then welcome.

Banana fanna fo fanna

#1
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:

if __name__ == "__main__":
   # call your main method or something


#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

try:
    import psyco; psyco.full()
except:
    pass


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.

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.