From a18c2e350f7ae7018900dce9878cc969340d2663 Mon Sep 17 00:00:00 2001 From: rcoh Date: Sun, 7 Aug 2011 20:14:17 -0700 Subject: These are the base tetris files. They are Leah's. --- originaltetris/tetris2.py | 790 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 790 insertions(+) create mode 100644 originaltetris/tetris2.py (limited to 'originaltetris/tetris2.py') diff --git a/originaltetris/tetris2.py b/originaltetris/tetris2.py new file mode 100644 index 0000000..f2d54d3 --- /dev/null +++ b/originaltetris/tetris2.py @@ -0,0 +1,790 @@ + + + + +from graphics import * +import random + +############################################################ +# BLOCK CLASS +############################################################ + +class Block(Rectangle): + ''' Block class: + Implement a block for a tetris piece + Attributes: x - type: int + y - type: int + specify the position on the tetris board + in terms of the square grid + ''' + # BLOCK SIZE defined in pixels. So from now on, each coordinate is multiplied by 30 + # (see below) + BLOCK_SIZE = 30 + OUTLINE_WIDTH = 3 + + def __init__(self, pos, color): + self.x = pos.x + self.y = pos.y + + p1 = Point(pos.x*Block.BLOCK_SIZE + Block.OUTLINE_WIDTH, + pos.y*Block.BLOCK_SIZE + Block.OUTLINE_WIDTH) + p2 = Point(p1.x + Block.BLOCK_SIZE, p1.y + Block.BLOCK_SIZE) + + Rectangle.__init__(self, p1, p2) + self.setWidth(Block.OUTLINE_WIDTH) + self.setFill(color) + self.color = color + + def can_move(self, board, dx, dy): + ''' Parameters: dx - type: int + dy - type: int + + Return value: type: bool + + checks if the block can move dx squares in the x direction + and dy squares in the y direction + Returns True if it can, and False otherwise + HINT: use the can_move method on the Board object + ''' + + if board.can_move(self.x + dx, self.y + dy) == True: + return True + return False + + def move(self, dx, dy): + ''' Parameters: dx - type: int + dy - type: int + + moves the block dx squares in the x direction + and dy squares in the y direction + ''' + + self.x += dx + self.y += dy + + Rectangle.move(self, dx*Block.BLOCK_SIZE, dy*Block.BLOCK_SIZE) + + +############################################################ +# SHAPE CLASS +############################################################ + +class Shape(): + ''' Shape class: + Base class for all the tetris shapes + Attributes: blocks - type: list - the list of blocks making up the shape + rotation_dir - type: int - the current rotation direction of the shape + shift_rotation_dir - type: Boolean - whether or not the shape rotates + ''' + + def __init__(self, coords, color): + # Define empty list + self.blocks = [] + self.rotation_dir = 1 + ### A boolean to indicate if a shape shifts rotation direction or not. + ### Defaults to false since only 3 shapes shift rotation directions (I, S and Z) + self.shift_rotation_dir = False + + # for each coordinate, make a block object out of it and append to list + for pos in coords: + self.blocks.append(Block(pos, color)) + + + + def get_blocks(self): + '''returns the list of blocks + ''' + #return list of blocks + return self.blocks + pass + + def draw(self, win): + ''' Parameter: win - type: CanvasFrame + + Draws the shape: + i.e. draws each block + ''' + # For each block in list, draw block + for block in self.blocks: + block.draw(win) + + def move(self, dx, dy): + ''' Parameters: dx - type: int + dy - type: int + + moves the shape dx squares in the x direction + and dy squares in the y direction, i.e. + moves each of the blocks + ''' + for block in self.blocks: + block.move(dx, dy) + + def can_move(self, board, dx, dy): + ''' Parameters: dx - type: int + dy - type: int + + Return value: type: bool + + checks if the shape can move dx squares in the x direction + and dy squares in the y direction, i.e. + check if each of the blocks can move + Returns True if all of them can, and False otherwise + + ''' + + #Checks each block to see if it can move dx and dy + for blocks in self.blocks: + if blocks.can_move(board, dx, dy) == False: + return False + return True + # default implementation (MUST CHANGE) + #return True + + def get_rotation_dir(self): + ''' Return value: type: int + + returns the current rotation direction + ''' + return self.rotation_dir + + def can_rotate(self, board): + ''' Parameters: board - type: Board object + Return value: type : bool + + Checks if the shape can be rotated. + + 1. Get the rotation direction using the get_rotation_dir method + 2. Compute the position of each block after rotation and check if + the new position is valid + 3. If any of the blocks cannot be moved to their new position, + return False + + otherwise all is good, return True + ''' + #Get rotation direction from other method, and center + rotation_direction = self.get_rotation_dir() + center = self.center_block + + #For each block in list: + for block in self.blocks: + # Use these formulas to determine new coordinates (provided for us) + x = center.x - rotation_direction*center.y + rotation_direction*block.y + y = center.y + rotation_direction*center.x - rotation_direction*block.x + # If these posistions are not valid on the board(other piece there + #or out of bounds), return False + if board.can_move(x, y) == False: + return False + return True + + + + + def rotate(self, board): + ''' Parameters: board - type: Board object + + rotates the shape: + 1. Get the rotation direction using the get_rotation_dir method + 2. Compute the position of each block after rotation + 3. Move the block to the new position + + ''' + + + # For each block defined in shape + for block in self.blocks: + #Grab rotation direction and center block + rotation_direction = self.get_rotation_dir() + center = self.center_block + #Compute new coordinates and call move method + x = center.x - rotation_direction*center.y + rotation_direction*block.y + y = center.y + rotation_direction*center.x - rotation_direction*block.x + # Use x - block.x for dx, y-block.y for dy + block.move(x - block.x, y - block.y) + + ### This should be at the END of your rotate code. + ### DO NOT touch it. Default behavior is that a piece will only shift + ### rotation direciton after a successful rotation. This ensures that + ### pieces which switch rotations definitely remain within their + ### accepted rotation positions. + if self.shift_rotation_dir: + self.rotation_dir *= -1 + + + +############################################################ +# ALL SHAPE CLASSES +############################################################ + +# For each shape in the game, we gave the coordinates for each block in the +# Tetronimo, using the center block as reference. We then initialized a shape +# object with the list of coords and a color. Self.center_block is used +# above for rotating; it tells us what the center block is + +class I_shape(Shape): + def __init__(self, center): + coords = [Point(center.x - 2, center.y), + Point(center.x - 1, center.y), + Point(center.x , center.y), + Point(center.x + 1, center.y)] + Shape.__init__(self, coords, 'blue') + self.shift_rotation_dir = True + self.center_block = self.blocks[2] + + +class J_shape(Shape): + def __init__(self, center): + coords = [Point(center.x - 1, center.y), + Point(center.x , center.y), + Point(center.x + 1, center.y), + Point(center.x + 1, center.y + 1)] + Shape.__init__(self, coords, 'orange') + self.center_block = self.blocks[1] + + +class L_shape(Shape): + def __init__(self, center): + coords = [Point(center.x - 1, center.y), + Point(center.x , center.y), + Point(center.x + 1, center.y), + Point(center.x - 1, center.y + 1)] + Shape.__init__(self, coords, 'cyan') + self.center_block = self.blocks[1] + + +class O_shape(Shape): + def __init__(self, center): + coords = [Point(center.x , center.y), + Point(center.x - 1, center.y), + Point(center.x , center.y + 1), + Point(center.x - 1, center.y + 1)] + Shape.__init__(self, coords, 'red') + self.center_block = self.blocks[0] + + def rotate(self, board): + # Override Shape's rotate method since O_Shape does not rotate + return + + + +class S_shape(Shape): + def __init__(self, center): + coords = [Point(center.x , center.y), + Point(center.x , center.y + 1), + Point(center.x + 1, center.y), + Point(center.x - 1, center.y + 1)] + Shape.__init__(self, coords, 'green') + self.center_block = self.blocks[0] + self.shift_rotation_dir = True + self.rotation_dir = -1 + + +class T_shape(Shape): + def __init__(self, center): + coords = [Point(center.x - 1, center.y), + Point(center.x , center.y), + Point(center.x + 1, center.y), + Point(center.x , center.y + 1)] + Shape.__init__(self, coords, 'yellow') + self.center_block = self.blocks[1] + + +class Z_shape(Shape): + def __init__(self, center): + coords = [Point(center.x - 1, center.y), + Point(center.x , center.y), + Point(center.x , center.y + 1), + Point(center.x + 1, center.y + 1)] + Shape.__init__(self, coords, 'magenta') + self.shift_rotation_dir = True + self.rotation_dir = -1 + self.center_block = self.blocks[1] + + + +############################################################ +# BOARD CLASS +############################################################ + +class Board(): + ''' Board class: it represents the Tetris board + + Attributes: width - type:int - width of the board in squares + height - type:int - height of the board in squares + canvas - type:CanvasFrame - where the pieces will be drawn + grid - type:Dictionary - keeps track of the current state of + the board; stores the blocks for a given position + ''' + + def __init__(self, win, width, height): + self.width = width + self.height = height + + # create a canvas to draw the tetris shapes on + self.canvas = CanvasFrame(win, self.width * Block.BLOCK_SIZE, + self.height * Block.BLOCK_SIZE) + self.canvas.setBackground('black') + # Create Scoreboard with same width, but a height of 100 pixels + self.scoreboard = Scoreboard(win, width, 100) + + # create an empty dictionary + # currently we have no shapes on the board + self.grid = {} + + def draw_shape(self, shape): + ''' Parameters: shape - type: Shape + Return value: type: bool + + draws the shape on the board if there is space for it + and returns True, otherwise it returns False + ''' + # If the shape can move function returns True, draw new shape + if shape.can_move(self, 0, 0): + shape.draw(self.canvas) + return True + # If not, no shape can be drawn (game over) + else: + self.game_over() + + def can_move(self, x, y): + ''' Parameters: x - type:int + y - type:int + Return value: type: bool + + 1. check if it is ok to move to square x,y + if the position is outside of the board boundaries, can't move there + return False + + 2. if there is already a block at that postion, can't move there + return False + + 3. otherwise return True + + ''' + # if this x, y coordinates are within the board and there are no pieces there (aka no keys + # with the coordinates in the dictionary), return True + if (x >= 0 and x < self.width) and (y >= 0 and y < self.height) and (x, y) not in self.grid.keys(): + return True + return False + + pass + + def add_shape(self, shape): + ''' Parameter: shape - type:Shape + + add a shape to the grid, i.e. + add each block to the grid using its + (x, y) coordinates as a dictionary key + + Hint: use the get_blocks method on Shape to + get the list of blocks + + ''' + + #For each block in a given shape, add it to the self.grid dictionary + for block in shape.get_blocks(): + self.grid[(block.x, block.y)] = block + + + def delete_row(self, y): + ''' Parameters: y - type:int + + remove all the blocks in row y + to remove a block you must remove it from the grid + and erase it from the screen. + If you dont remember how to erase a graphics object + from the screen, take a look at the Graphics Library + handout + + ''' + # If the row is complete (use is_row_complete method) + if self.is_row_complete(y) == True: + #For every key in the block dictionary, + for item in self.grid.keys(): + # If the y coordinate of that key matches the row that must be removed + if item[1] == y: + #Undraw the item and delete it from the dictionary + self.grid[item].undraw() + del self.grid[item] + + + def is_row_complete(self, y): + ''' Parameter: y - type: int + Return value: type: bool + + for each block in row y + check if there is a block in the grid (use the in operator) + if there is one square that is not occupied, return False + otherwise return True + + ''' + # Define empty dictionary to keep track of blocks in row + y_list = [] + # for every block on the board, if it is in this row, append to list + for block in self.grid.keys(): + if block[1] == y: + y_list.append(block) + + + # If the row is full (i.e. length of the list is the same as the width of the board) + # empty the list and return True + if len(y_list) == self.width: + y_list = [] + return True + return False + + + def move_down_rows(self, y_start): + ''' Parameters: y_start - type:int + + for each row from y_start to the top + for each column + check if there is a block in the grid + if there is, remove it from the grid + and move the block object down on the screen + and then place it back in the grid in the new position + + ''' + # For each row from y_start to the top + for row in range(y_start, 0, -1): + # And each column on the board + for column in range(0, self.width): + # If there is a block there, move it down 1, delete from grid + # and add new coordinates to the grid + if (column, row) in self.grid.keys(): + #print "WE MADE IT" + self.grid[(column, row)].move(0, 1) + block = self.grid[(column, row)] + del self.grid[(column, row)] + self.grid[(column, row + 1)] = block + + def remove_complete_rows(self): + ''' removes all the complete rows + 1. for each row, y, + 2. check if the row is complete + if it is, + delete the row + move all rows down starting at row y - 1 + + ''' + # Define empty list for keeping track of how many lines deleted(scoring purposes) + lines_completed = [] + + #For each row in the board, if it is complete, delete, move down rows, + #Append the list + for row in range(0, self.height): + if self.is_row_complete(row) == True: + #print "YADDA YADDA" + self.delete_row(row) + self.move_down_rows(row - 1) + lines_completed.append('a') + # Call the add_score method in the scoreboard class with the # of completed lines + self.scoreboard.add_score(len(lines_completed)) + + def game_over(self): + ''' display "Game Over !!!" message in the center of the board + HINT: use the Text class from the graphics library + ''' + # Displays game_over message on screen + game_over_message = Text(Point(150, 250), "GAME OVER \n BEETCHES") + game_over_message.setStyle('bold') + game_over_message.setSize(25) + game_over_message.setTextColor('white') + game_over_message.setFace('helvetica') + game_over_message.draw(self.canvas) + + +############################################################ +# SCOREBOARD CLASS +############################################################ + +class Scoreboard(): + + + def __init__(self, win, width, height): + self.width = width + self.height = height + + # create a canvas to draw the tetris shapes on + self.canvas = CanvasFrame(win, self.width * Block.BLOCK_SIZE, + 100) + self.canvas.setBackground('black') + + # Start the score off at 0, level off at 1 + self.current_score = 0 + self.current_level = 1 + + # Text object displaying the Score + self.score = Text(Point(75, 25), "Score: " + str(self.current_score)) + self.score.setStyle('bold') + self.score.setSize(18) + self.score.setTextColor('white') + self.score.setFace('helvetica') + + # Text object displaying the Level + self.levels = Text(Point(75, 50), "Level: " + str(self.current_level)) + self.levels.setStyle('bold') + self.levels.setSize(18) + self.levels.setTextColor('white') + self.levels.setFace('helvetica') + + # Draw the Text objects on the screen + self.score.draw(self.canvas) + self.levels.draw(self.canvas) + + # Set the current_level = the method defined below + self.current_level = self.increase_level() + + # This method is called from remove_complete_rows method, using len(lines_completed) as parameter + def add_score(self, lines): + # Depending on # of lines completed, add score accordingly + if lines == 1: + self.current_score += 100 + elif lines == 2: + self.current_score += 300 + elif lines == 3: + self.current_score += 450 + elif lines == 4: + self.current_score += 700 + #Change Text Object + self.score.setText("Score: "+ str(self.current_score)) + + def increase_level(self): + # Depending on Score, change level and text object + while self.current_score <= 1000: + self.current_level = 1 + self.levels.setText("Level: "+ str(self.current_level)) + return self.current_level + while self.current_score > 1000 and self.current_score <= 5000: + self.current_level = 2 + self.levels.setText("Level: "+ str(self.current_level)) + return self.current_level + while self.current_score > 5000 and self.current_score <= 10000: + self.current_level = 3 + self.levels.setText("Level: "+ str(self.current_level)) + return self.current_level + while self.current_score > 10000 and self.current_score <= 15000: + self.current_level = 4 + self.levels.setText("Level: "+ str(self.current_level)) + return self.current_level + while self.current_score > 15000 and self.current_score <= 19000: + self.current_level = 5 + self.levels.setText("Level: "+ str(self.current_level)) + return self.current_level + while self.current_score > 19000 and self.current_score <= 23000: + self.current_level = 6 + self.levels.setText("Level: "+ str(self.current_level)) + return self.current_level + while self.current_score > 23000 and self.current_score <= 27000: + self.current_level = 7 + self.levels.setText("Level: "+ str(self.current_level)) + return self.current_level + while self.current_score > 27000 and self.current_score <= 30000: + self.current_level = 8 + self.levels.setText("Level: "+ str(self.current_level)) + return self.current_level + while self.current_score > 30000 and self.current_score <= 33000: + self.current_level = 9 + self.levels.setText("Level: "+ str(self.current_level)) + return self.current_level + while self.current_score > 33000: + self.current_level = 10 + self.levels.setText("Level: "+ str(self.current_level)) + return self.current_level + + +############################################################ +# TETRIS CLASS +############################################################ + +class Tetris(): + ''' Tetris class: Controls the game play + Attributes: + SHAPES - type: list (list of Shape classes) + DIRECTION - type: dictionary - converts string direction to (dx, dy) + BOARD_WIDTH - type:int - the width of the board + BOARD_HEIGHT - type:int - the height of the board + board - type:Board - the tetris board + win - type:Window - the window for the tetris game + delay - type:int - the speed in milliseconds for moving the shapes + current_shapes - type: Shape - the current moving shape on the board + ''' + + SHAPES = [I_shape, J_shape, L_shape, O_shape, S_shape, T_shape, Z_shape] + DIRECTION = {'Left':(-1, 0), 'Right':(1, 0), 'Down':(0, 1)} + BOARD_WIDTH = 10 + BOARD_HEIGHT = 20 + + def __init__(self, win): + self.board = Board(win, self.BOARD_WIDTH, self.BOARD_HEIGHT) + self.win = win + self.delay = 1000 #ms + + # sets up the keyboard events + # when a key is called the method key_pressed will be called + self.win.bind_all('', self.key_pressed) + + # set the current shape to a random new shape + self.current_shape = self.create_new_shape() + + # Draw the current_shape on the board (take a look at the + # draw_shape method in the Board class) + self.board.draw_shape(self.current_shape) + + # For Step 9: animate the shape! + self.animate_shape() + + + + def create_new_shape(self): + ''' Return value: type: Shape + + Create a random new shape that is centered + at y = 0 and x = int(self.BOARD_WIDTH/2) + return the shape + ''' + # Pick a random number from 0 to 6 + rand_shape = random.randint(0, 6) + # That number corresponds to a shape in SHAPES list. Use coordinates to tell where to draw + new_shape = self.SHAPES[rand_shape](Point(int(self.BOARD_WIDTH/2), 0)) + return new_shape + + pass + + def animate_shape(self): + ''' animate the shape - move down at equal intervals + specified by the delay attribute + ''' + self.board.scoreboard.increase_level() + #Depending onlevel, change the delay for animation to get shorter and shorter + # move the shape down + if self.board.scoreboard.current_level == 1: + self.delay = 1000 + self.do_move('Down') + self.win.after(self.delay, self.animate_shape) + elif self.board.scoreboard.current_level == 2: + self.delay = 900 + self.do_move('Down') + self.win.after(self.delay, self.animate_shape) + elif self.board.scoreboard.current_level == 3: + self.delay = 800 + self.do_move('Down') + self.win.after(self.delay, self.animate_shape) + elif self.board.scoreboard.current_level == 4: + self.delay = 650 + self.do_move('Down') + self.win.after(self.delay, self.animate_shape) + elif self.board.scoreboard.current_level == 5: + self.delay = 500 + self.do_move('Down') + self.win.after(self.delay, self.animate_shape) + elif self.board.scoreboard.current_level == 6: + self.delay = 400 + self.do_move('Down') + self.win.after(self.delay, self.animate_shape) + elif self.board.scoreboard.current_level == 7: + self.delay = 300 + self.do_move('Down') + self.win.after(self.delay, self.animate_shape) + elif self.board.scoreboard.current_level == 8: + self.delay = 200 + self.do_move('Down') + self.win.after(self.delay, self.animate_shape) + elif self.board.scoreboard.current_level == 9: + self.delay = 100 + self.do_move('Down') + self.win.after(self.delay, self.animate_shape) + elif self.board.scoreboard.current_level == 10: + self.delay = 50 + self.do_move('Down') + self.win.after(self.delay, self.animate_shape) + + + def do_move(self, direction): + ''' Parameters: direction - type: string + Return value: type: bool + + Move the current shape in the direction specified by the parameter: + First check if the shape can move. If it can, move it and return True + Otherwise if the direction we tried to move was 'Down', + 1. add the current shape to the board + 2. remove the completed rows if any + 3. create a new random shape and set current_shape attribute + 4. If the shape cannot be drawn on the board, display a + game over message + + return False + + ''' + # space bar is hit, move shape down until it can't move anymore + if direction == 'space': + while self.current_shape.can_move(self.board, self.DIRECTION['Down'][0], self.DIRECTION['Down'][1]) == True: + self.current_shape.move(self.DIRECTION['Down'][0], self.DIRECTION['Down'][1]) + # add new shape to grid, create new shape to draw, and check for completed rows + self.board.add_shape(self.current_shape) + self.current_shape = self.create_new_shape() + self.board.draw_shape(self.current_shape) + self.board.remove_complete_rows() + + #if the shape can move in the direction given by DIRECTION list, move it there + elif self.current_shape.can_move(self.board, self.DIRECTION[direction][0], self.DIRECTION[direction][1]) == True: + self.current_shape.move(self.DIRECTION[direction][0], self.DIRECTION[direction][1]) + return True + + else: + # if the shape can't move any moore + if direction == 'Down': + # add new shape to grid, create new shape to draw, and check for completed rows + self.board.add_shape(self.current_shape) + self.current_shape = self.create_new_shape() + self.board.draw_shape(self.current_shape) + self.board.remove_complete_rows() + return False + + + def do_rotate(self): + ''' Checks if the current_shape can be rotated and + rotates if it can + ''' + + + #If shape can rotate, rotate it + if self.current_shape.can_rotate(self.board) == True: + self.current_shape.rotate(self.board) + + + + def key_pressed(self, event): + ''' this function is called when a key is pressed on the keyboard + it currenly just prints the value of the key + + Modify the function so that if the user presses the arrow keys + 'Left', 'Right' or 'Down', the current_shape will move in + the appropriate direction + + if the user presses the space bar 'space', the shape will move + down until it can no longer move and is added to the board + + if the user presses the 'Up' arrow key , + the shape should rotate. + + ''' + + + key = event.keysym + # If key is up, call do_rotate function + if key == 'Up': + self.do_rotate() + + # Else, call do_move function + else: + self.do_move(key) + + + +################################################################ +# Start the game +################################################################ + +win = Window("Tetris") +game = Tetris(win) +win.mainloop() -- cgit v1.2.3