From 37636974499803423dfad390a226f833d6389abd Mon Sep 17 00:00:00 2001 From: Leah Alpert Date: Fri, 26 Aug 2011 14:22:53 -0700 Subject: modified ddrinput to use asdw for payer 0 in debug mode. Changed spacing of rendered. Added new tetris file that uses pygame instead of tkinter. --- ddrinput.py | 27 +++- renderer.py | 8 +- tetris_pygame/tetris.py | 322 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 352 insertions(+), 5 deletions(-) create mode 100644 tetris_pygame/tetris.py diff --git a/ddrinput.py b/ddrinput.py index 71dbfd3..92bc123 100644 --- a/ddrinput.py +++ b/ddrinput.py @@ -7,7 +7,12 @@ Y = 1 KEY_LEFT = 276 KEY_UP = 273 KEY_DOWN = 274 -KEY_RIGHT = 275 +KEY_RIGHT = 275 +KEY_A = 97 +KEY_S = 115 +KEY_D = 100 +KEY_W = 119 + DIRECTIONS = {0:'LEFT', 1:'RIGHT', 2:'UP', 3:'DOWN'} class DdrInput(object): """ @@ -18,7 +23,7 @@ class DdrInput(object): DEBUG MODE: - Use the arrow keys. Hold down a modifier (alt, control, etc.) to get player 2 + Use the arrow keys for player 1, asdw for player 0. """ def __init__(self, debug_mode=True): """ @@ -69,14 +74,30 @@ class DdrInput(object): if self.debug_mode: if event.type == KEY_EVENT: if event.key == KEY_LEFT: + player_index = 1 player_move = LEFT elif event.key == KEY_RIGHT: + player_index = 1 player_move = RIGHT elif event.key == KEY_DOWN: + player_index = 1 player_move = DOWN elif event.key == KEY_UP: + player_index = 1 + player_move = UP + elif event.key == KEY_A: + player_index = 0 + player_move = LEFT + elif event.key == KEY_D: + player_index = 0 + player_move = RIGHT + elif event.key == KEY_S: + player_index = 0 + player_move = DOWN + elif event.key == KEY_W: + player_index = 0 player_move = UP - player_index = event.mod == 0 + if player_move != None: return (player_index, player_move) else: diff --git a/renderer.py b/renderer.py index 46d0fc5..fd452eb 100644 --- a/renderer.py +++ b/renderer.py @@ -21,7 +21,8 @@ class PygameRenderer(Renderer): DISPLAY_SIZE = (1000,1000) OFFSET = (100, 100) - SCALE = 10 + SCALE = 15 + RADIUS = 6 def __init__(self): pygame.init() @@ -33,8 +34,11 @@ class PygameRenderer(Renderer): def render_game(self, game_board): self.background.fill(Color(0,0,0)) for (x,y) in game_board: + disp_x = x + if x >= 10: + disp_x+=3 pygame.draw.circle(self.background, self.color_deref(game_board[(x,y)]), - (self.OFFSET[0] + x*self.SCALE, self.OFFSET[1] + y*self.SCALE), self.SCALE) + (self.OFFSET[0] + disp_x*self.SCALE, self.OFFSET[1] + y*self.SCALE), self.RADIUS) self.screen.blit(self.background, (0,0)) pygame.display.flip() diff --git a/tetris_pygame/tetris.py b/tetris_pygame/tetris.py new file mode 100644 index 0000000..e64b879 --- /dev/null +++ b/tetris_pygame/tetris.py @@ -0,0 +1,322 @@ +#!/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 + Up/w Rotate / add player + Down/s Move down / start game +""" + +from time import sleep, time +import random +import sys +from renderer import PygameRenderer +from tetris_shape import * +from ddrinput import DdrInput +from ddrinput import DIRECTIONS +import pygame + +MAXX = 10 +MAXY = 18 +NO_OF_LEVELS = 10 + +(LEFT, RIGHT, UP, DOWN) = range(4) + +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. + Stores blocks that have landed. + """ + 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 clear(self): + self.landed = {} + + 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 = (0,1) + self.landed[(x+dx, ay+dy)] = block_color + + # move the empty row index down too + empty_row +=1 + # y stays same as row above has moved down. + else: + y -= 1 + + # return the number of rows deleted. + return rows_deleted + + 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, other player's board, +#current shape, score, etc +class Player(): + def __init__(self, gs, myBoard, otherBoard, player_id = 99): + print "initialize player" + self.id = player_id + self.board = myBoard + self.other_board = otherBoard + self.score = 0 + self.gs = gs + self.shape = self.get_next_shape() + + def handle_move(self, direction): + #if you can't move then you've hit something + if self.shape: + if direction==UP: + self.shape.rotate(clockwise=False) + else: + if not self.shape.move( direction ): + # 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 self.gs.num_players == 2: + 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.gs.state = "ending" #loss! + self.gs.winner = self.other_board + + # 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 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.gs.shapes[ random.randint(0,len(self.gs.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.shapes = [square_shape, t_shape,l_shape, reverse_l_shape, + z_shape, s_shape,i_shape ] + self.num_players = 0 + self.level = 1 + self.delay = 800 + self.thresholds = range(10,100,10) + self.state = "waiting" #states: waiting (between games), playing, ending + self.winner = None #winning Board + + +#runs the overall game. initializes both player and any displays +class TwoPlayerGame(object): + + #one-time initialization for gui etc + def __init__(self): + print "initialize tetris" + self.gui = PygameRenderer() + self.input = DdrInput() + self.init_game() + + def handle_input(self): + drop_time = time() + while 1: + if self.gameState.state=="playing" and time()-drop_time > self.gameState.delay/1000.0: + self.gravity() + drop_time = time() + ev = self.input.poll() + if ev: + print "EVENT",ev + player,direction = ev + #print "Player",player,direction + if self.gameState.state=="playing": + if self.players[player]!=None: + self.players[player].handle_move(direction) + elif self.gameState.state == "waiting": + if direction==UP: + self.add_player(player) + elif direction==DOWN: + if self.players[player]!=None: + self.start_game() + self.update_gui() + + #initializes each game + def init_game(self): + print "init next game" + self.boards = [Board(MAXX,MAXY), Board(MAXX,MAXY)] + self.players = [None,None] + self.gameState = GameState(self.gui) + #display initial "animation" + self.handle_input() + #self.update_gui() + + def add_player(self,num): # 0=left, 1=right + print "adding player ",num + if self.players[num]==None: + p = Player(self.gameState, self.boards[num], self.boards[(num+1)%2]) + self.players[num] = p + self.update_gui() + self.gameState.num_players+=1 + + def start_game(self): + print "start game" + self.gameState.state = "playing" + self.update_gui() #maybe + self.gravity() + + #change to pygame + #handles gravity and checks for game over + def gravity(self): #probably shouldn't handle gravity and endgame...or rename + if self.gameState.state == "ending": + self.end_game() + return + else: + for p in self.players: + if p: + p.move_my_shape() + self.update_gui() + + def update_gui(self): + self.gui.render_game(self.to_dict()) + + def end_game(self): + winner_board = self.gameState.winner + self.animate_ending(winner_board) + self.init_game() + + def animate_ending(self,winner_board): + print "game over, display animation" + for i in range(100): + print i, + + def create_shapes(): #in progress..... + y = 4 + up_diags = [(1,y+4),(1,y+3),(2,y+3),(2,y+2),(3,y+2),(3,y+1), + (8,y+4),(8,y+3),(7,y+3),(7,y+2),(6,y+2),(6,y+1)] + down_diags = [(x0,10-y0+2*y) for (x0,y0) in up_diags] + line = [(i,j) for i in [4,5] for j in range(y,y+11)] + up_arrow = line[:] + for xy in up_diags: + up_arrow.append(xy) + down_arrow = line[:] + for xy in down_diags: + down_arrow.append(xy) + return down_arrow + + def to_dict(self): + d = {} + for n in range(2): + if self.players[n]!=None: + p = self.players[n] + offset = n*MAXX + + #blocks + for (x,y) in p.board.landed: + d[(x+offset,y)] = p.board.landed[(x,y)] + + #shapes + blocks = p.shape.blocks + for b in blocks: + d[(b.x+offset*n,b.y)] = b.color + + #score + score = p.score + for i in range(10): + bit = score%2 + score = score>>1 + coord = (MAXX-1-i + offset, MAXY+1) + if bit: + d[coord] = "yellow" + else: + d[coord] = "gray" + return d + + +if __name__ == "__main__": + tetrisGame = TwoPlayerGame() -- cgit v1.2.3