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/tetris.py | 278 +++++++++++++++ originaltetris/tetris2.py | 790 +++++++++++++++++++++++++++++++++++++++++ originaltetris/tetrisGUI.py | 120 +++++++ originaltetris/tetris_shape.py | 171 +++++++++ 4 files changed, 1359 insertions(+) create mode 100644 originaltetris/tetris.py create mode 100644 originaltetris/tetris2.py create mode 100644 originaltetris/tetrisGUI.py create mode 100644 originaltetris/tetris_shape.py (limited to 'originaltetris') diff --git a/originaltetris/tetris.py b/originaltetris/tetris.py new file mode 100644 index 0000000..b6b31f4 --- /dev/null +++ b/originaltetris/tetris.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python +""" +Tetris Tk - A tetris clone written in Python using the Tkinter GUI library. + +Controls: + Left/a Move left + Right/d Move right + Down/s Move down + Up/w Rotate anti-clockwise (to the left) +""" +from Tkinter import * +from time import sleep +import random +import sys +from tetrisGUI import GUI +from tetris_shape import * + +MAXX = 10 +MAXY = 18 +NO_OF_LEVELS = 10 + +LEFT = "left" +RIGHT = "right" +DOWN = "down" +direction_d = { "left": (-1, 0), "right": (1, 0), "down": (0, 1) } + +COLORS = ["orange", "red", "green", "blue", "purple", "yellow", "magenta"] +#COLORS = ["gray"] + +class Board(): + """ + The board represents the tetris playing area. A grid of x by y blocks. + """ + def __init__(self, max_x=10, max_y=20): + # blocks are stored in dict of (x,y)->"color" + self.landed = {} + self.max_x = max_x + self.max_y = max_y + + def receive_lines(self, num_lines): + #shift lines up + for y in range(self.max_y-num_lines): + for x in xrange(self.max_x): + block_color = self.landed.pop((x,y+num_lines),None) + if block_color: + self.landed[(x,y)] = block_color + #put in new lines + for j in range(num_lines): + for i in random.sample(xrange(self.max_x), random.choice([6,7])): + self.landed[(i,self.max_y-1-j)] = random.choice(COLORS) + + def check_for_complete_row( self, blocks ): + """ + Look for a complete row of blocks, from the bottom up until the top row + or until an empty row is reached. + """ + rows_deleted = 0 + + # Add the blocks to those in the grid that have already 'landed' + for block in blocks: + self.landed[ block.coord() ] = block.color + + empty_row = 0 + # find the first empty row + for y in xrange(self.max_y -1, -1, -1): + row_is_empty = True + for x in xrange(self.max_x): + if self.landed.get((x,y), None): + row_is_empty = False + break; + if row_is_empty: + empty_row = y + break + + # Now scan up and until a complete row is found. + y = self.max_y - 1 + while y > empty_row: + + complete_row = True + for x in xrange(self.max_x): + if self.landed.get((x,y), None) is None: + complete_row = False + break; + + if complete_row: + rows_deleted += 1 + + #delete the completed row + for x in xrange(self.max_x): + self.landed.pop((x,y)) + + # move all the rows above it down + for ay in xrange(y-1, empty_row, -1): + for x in xrange(self.max_x): + block_color = self.landed.pop((x,ay), None) + if block_color: + dx,dy = direction_d[DOWN] + self.landed[(x+dx, ay+dy)] = block_color + + # move the empty row down index down too + empty_row +=1 + # y stays same as row above has moved down. + else: + y -= 1 + + # return the score, calculated by the number of rows deleted. + return rows_deleted + + def output( self ): + for y in xrange(self.max_y): + line = [] + for x in xrange(self.max_x): + if self.landed.get((x,y), None): line.append("X") + else: line.append(".") + print "".join(line) + + def check_block( self, (x, y) ): + """ + Check if the x, y coordinate can have a block placed there. + That is; if there is a 'landed' block there or it is outside the + board boundary, then return False, otherwise return true. + """ + if x < 0 or x >= self.max_x or y < 0 or y >= self.max_y: + return False + elif self.landed.has_key( (x, y) ): + return False + else: + return True + +#represents a player. each player has a board and can get new shapes... +# +class Player(): + def __init__(self,parent, gs, myBoard, otherBoard): + print "initialize player" + self.parent = parent + self.board = myBoard + self.other_board = otherBoard + self.score = 0 + self.shapes = [square_shape, + t_shape, + l_shape, + reverse_l_shape, + z_shape, + s_shape, + i_shape ] + self.gs = gs + self.shape = self.get_next_shape() + + def handle_move(self, direction): + #if you can't move then you've hit something + success = self.shape.move( direction ) + if not success: + + # if you're heading down then the shape has 'landed' + if direction == DOWN: + points = self.board.check_for_complete_row( + self.shape.blocks) + del self.shape + self.shape = self.get_next_shape() + + self.score += points + if points > 1: + self.other_board.receive_lines(points-1) + + # If the shape returned is None, then this indicates that + # that the check before creating it failed and the + # game is over! + if self.shape is None: + self.end_game() #loss! + + # do we go up a level? + if (self.gs.level < NO_OF_LEVELS and + self.score >= self.gs.thresholds[self.gs.level]): + self.gs.level+=1 + self.gs.delay-=100 + + # Signal that the shape has 'landed' + return False + return True + + def left(self): + if self.shape: + self.handle_move( LEFT ) + + def right(self): + if self.shape: + self.handle_move( RIGHT ) + + def down(self): + if self.shape: + self.handle_move( DOWN ) + + def up(self): + if self.shape: + self.shape.rotate(clockwise=False) + + def move_my_shape( self ): + if self.shape: + self.handle_move( DOWN ) + + def get_next_shape( self ): + #Randomly select which tetrominoe will be used next. + the_shape = self.shapes[ random.randint(0,len(self.shapes)-1) ] + return the_shape.check_and_create(self.board) + +#contains variables that are shared between the players: +#levels, delay time, etc? +class GameState(): + def __init__(self, gui): + self.level = 1 + self.delay = 1000 + self.thresholds = range(10,100,10) + + +#runs the overall game. initializes both player and any displays +class TwoPlayerGame(object): + """ + Main game loop and receives GUI callback events for keypresses etc... + """ + def __init__(self, parent): + print "initialize game" + self.parent = parent + self.gui = GUI(parent,20,MAXX,MAXY) + self.gui.pack(side=BOTTOM) + self.gameState = GameState(self.gui) + + board0 = Board(MAXX,MAXY) + board1 = Board(MAXX,MAXY) + player0 = Player(parent, self.gameState, board0, board1) + player1 = Player(parent, self.gameState, board1, board0) + self.players = [player0, player1] + + self.parent.bind("", self.handle_key_press) + self.parent.after( self.gameState.delay, self.gravity) + + def gravity(self): + for p in self.players: + p.move_my_shape() + self.update_gui() + self.parent.after( self.gameState.delay, self.gravity) + + def handle_key_press(self,event): + k = event.keysym + if k=="Left": + self.players[1].left() + elif k=="Right": + self.players[1].right() + elif k=="Down": + self.players[1].down() + elif k=="Up": + self.players[1].up() + elif k=="a": + self.players[0].left() + elif k=="d": + self.players[0].right() + elif k=="s": + self.players[0].down() + elif k=="w": + self.players[0].up() + self.update_gui() + + def update_gui(self): + self.gui.draw_board(self.players) + + def end_game(self): + #fix this + #show_end_seq() + Toplevel().destroy() + self.parent.destroy() + sys.exit(0) + + + +if __name__ == "__main__": + root = Tk() + root.title("Tetris Tk") + theGame = TwoPlayerGame( root ) + root.mainloop() 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() diff --git a/originaltetris/tetrisGUI.py b/originaltetris/tetrisGUI.py new file mode 100644 index 0000000..8085980 --- /dev/null +++ b/originaltetris/tetrisGUI.py @@ -0,0 +1,120 @@ +""" +This is the class you run to run tetris. +Handles input and output +""" + +from Tkinter import * + +#GUI +class GUI( Frame ): + def __init__(self, parent, scale=20, max_x=10, max_y=20, offset=3): + print "initialize gui" + """ + Init and config the tetris computer display + """ + Frame.__init__(self, parent) + self.parent = parent + self.scale = scale + self.max_x = max_x + self.max_y = max_y + self.offset = offset + #height = rows + one blank + score row + #width = left board + 4 blank + right board + self.canvas = Canvas(parent, + height=((max_y+2) * scale)+offset, + width= 2*((max_x+2) * scale)+offset) + #size of one board plus buffer + self.boardsize = ((max_x+4) * scale)+offset + self.canvas.pack() + + + def add_block(self, (x, y), color): + """ + Draw a block on the canvas + """ + shrink = 4 + rx = (x * self.scale) + self.offset + ry = (y * self.scale) + self.offset + #self.canvas.create_oval(rx+shrink, ry+shrink, rx+self.scale-shrink, + #ry+self.scale-shrink, width=0, fill=color) + self.canvas.create_rectangle( + rx, ry, rx+self.scale, ry+self.scale, fill=color + ) + """ + def draw_board(self, color_dict): + self.canvas.delete(ALL) + #ARRAY or DICT... + for b in color_dict: + self.add_block(b) + """ + def draw_board(self, players): + self.canvas.delete(ALL) + x_width = self.max_x*self.scale+3 + y_width = self.max_y*self.scale+3 + gap = 4*self.scale + self.canvas.create_rectangle(3,2,x_width, y_width) + self.canvas.create_rectangle(x_width+gap,2,2*x_width+gap-3, y_width) + offset = 0 + for p in players: + landed = p.board.landed + for b in landed: + self.add_block((b[0]+offset, b[1]), landed[b]) + for b in p.shape.blocks: + self.add_block((b.x+offset, b.y), b.color) + offset += (self.max_x + 4) + + self.display_score(players[0].score,0) + self.display_score(players[1].score,1) + + def display_score(self, score, player_num): + offset = player_num * (self.max_x + 4) + for i in range(10): + bit = score%2 + score = score>>1 + coord = (self.max_x-1-i + offset, self.max_y+1) + if bit: + self.add_block(coord, "yellow") + else: + self.add_block(coord, "gray") + +""" +def update_display(player1,player2) + nah = process(meh) + #debug mode: + display_image(nah) + #real mode: + #update_lights(nah) + + +#waiting +#press +start = false +while (!start) +if (get_press(p0s)and p0_is_in) or (p1s and p1_is_in): + start = true +if get_press(p0in): + game.add_player(0) +if p1in: + game.add_player(1) + +animate countdown + +#assume 1p +handle controls... +p0.left() +p0.right() +etc + +get state... +if p0.update()... + update_display(p0.board, p0.score) + +if p0.died: + end + +animate end_seq(score) + +goto waiting + + +""" diff --git a/originaltetris/tetris_shape.py b/originaltetris/tetris_shape.py new file mode 100644 index 0000000..a0ec59e --- /dev/null +++ b/originaltetris/tetris_shape.py @@ -0,0 +1,171 @@ +LEFT = "left" +RIGHT = "right" +DOWN = "down" +direction_d = { "left": (-1, 0), "right": (1, 0), "down": (0, 1) } + +class Block(object): + def __init__( self, (x, y), color): + self.color = color + self.x = x + self.y = y + + def coord( self ): + return (self.x, self.y) + +class shape(object): + """ + Shape is the Base class for the game pieces e.g. square, T, S, Z, L, + reverse L and I. Shapes are constructed of blocks. + """ + @classmethod + def check_and_create(cls, board, coords, color ): + """ + Check if the blocks that make the shape can be placed in empty coords + before creating and returning the shape instance. Otherwise, return + None. + """ + for coord in coords: + if not board.check_block( coord ): + return None + + return cls( board, coords, color) + + def __init__(self, board, coords, color ): + """ + Initialise the shape base. + """ + self.board = board + self.blocks = [] + + for coord in coords: + self.blocks.append( Block(coord,color) ) + + def move( self, direction ): + """ + Move the blocks in the direction indicated by adding (dx, dy) to the + current block coordinates + """ + d_x, d_y = direction_d[direction] + + for block in self.blocks: + x = block.x + d_x + y = block.y + d_y + if not self.board.check_block( (x, y) ): + return False + + for block in self.blocks: + block.x += d_x + block.y += d_y + + return True + + def rotate(self, clockwise = True): + """ + Rotate the blocks around the 'middle' block, 90-degrees. The + middle block is always the index 0 block in the list of blocks + that make up a shape. + """ + # TO DO: Refactor for DRY + middle = self.blocks[0] + rel = [] + for block in self.blocks: + rel.append( (block.x-middle.x, block.y-middle.y ) ) + + # to rotate 90-degrees (x,y) = (-y, x) + # First check that the there are no collisions or out of bounds moves. + for idx in xrange(len(self.blocks)): + rel_x, rel_y = rel[idx] + if clockwise: + x = middle.x+rel_y + y = middle.y-rel_x + else: + x = middle.x-rel_y + y = middle.y+rel_x + + if not self.board.check_block( (x, y) ): + return False + + for idx in xrange(len(self.blocks)): + rel_x, rel_y = rel[idx] + if clockwise: + x = middle.x+rel_y + y = middle.y-rel_x + else: + x = middle.x-rel_y + y = middle.y+rel_x + + self.blocks[idx].x = x + self.blocks[idx].y = y + + return True + +class shape_limited_rotate( shape ): + """ + This is a base class for the shapes like the S, Z and I that don't fully + rotate (which would result in the shape moving *up* one block on a 180). + Instead they toggle between 90 degrees clockwise and then back 90 degrees + anti-clockwise. + """ + def __init__( self, board, coords, color ): + self.clockwise = True + super(shape_limited_rotate, self).__init__(board, coords, color) + + def rotate(self, clockwise=True): + """ + Clockwise, is used to indicate if the shape should rotate clockwise + or back again anti-clockwise. It is toggled. + """ + super(shape_limited_rotate, self).rotate(clockwise=self.clockwise) + if self.clockwise: + self.clockwise=False + else: + self.clockwise=True + +class square_shape( shape ): + @classmethod + def check_and_create( cls, board ): + coords = [(4,0),(5,0),(4,1),(5,1)] + return super(square_shape, cls).check_and_create(board, coords, "red") + + def rotate(self, clockwise=True): + """ + Override the rotate method for the square shape to do exactly nothing! + """ + pass + +class t_shape( shape ): + @classmethod + def check_and_create( cls, board ): + coords = [(4,0),(3,0),(5,0),(4,1)] + return super(t_shape, cls).check_and_create(board, coords, "yellow" ) + +class l_shape( shape ): + @classmethod + def check_and_create( cls, board ): + coords = [(4,0),(3,0),(5,0),(3,1)] + return super(l_shape, cls).check_and_create(board, coords, "orange") + +class reverse_l_shape( shape ): + @classmethod + def check_and_create( cls, board ): + coords = [(5,0),(4,0),(6,0),(6,1)] + return super(reverse_l_shape, cls).check_and_create( + board, coords, "green") + +class z_shape( shape_limited_rotate ): + @classmethod + def check_and_create( cls, board ): + coords =[(5,0),(4,0),(5,1),(6,1)] + return super(z_shape, cls).check_and_create(board, coords, "purple") + +class s_shape( shape_limited_rotate ): + @classmethod + def check_and_create( cls, board ): + coords =[(5,1),(4,1),(5,0),(6,0)] + return super(s_shape, cls).check_and_create(board, coords, "magenta") + +class i_shape( shape_limited_rotate ): + @classmethod + def check_and_create( cls, board ): + coords =[(4,0),(3,0),(5,0),(6,0)] + return super(i_shape, cls).check_and_create(board, coords, "blue") -- cgit v1.2.3