Diff of /game.py [000000] .. [6c45ee] Maximize Restore

  Switch to side-by-side view

--- a
+++ b/game.py
@@ -0,0 +1,595 @@
+# -*- coding: utf-8 -*-
+'''
+Created on 14 July 2012
+
+@author: Marco Baxemyr
+'''
+
+import sys
+import datetime
+import operator
+import random
+from collections import defaultdict
+
+import pygame
+from pygame.locals import *
+
+from block import Block
+from paddle import Paddle
+from ball import Ball
+from vector import Vector
+from helper_functions import *
+import widgets
+
+TITLE = "Block Fortress"
+SCREEN_WIDTH, SCREEN_HEIGHT = 960, 720
+
+class Game(object):
+    MUSIC = 'coherence.ogg'
+    WAIT_NEW_LEVEL = USEREVENT+5
+    ORANGE = (130, 40, 0)
+    
+    def __init__(self, screen):
+        random.seed()
+        self.screen = screen
+        self.clock = pygame.time.Clock()
+        self.background = load_image('background.png')
+        self.heart = load_image('heart.png')
+        self.paddle_image = load_image('paddle.png')
+        self.ball_image = load_image('ball.png')
+        self.paddles = pygame.sprite.Group()
+        self.paddle = Paddle(self.paddle_image, (390, 651))
+        self.paddles.add(self.paddle)
+        self.balls = pygame.sprite.RenderClear()
+        self.blocks = pygame.sprite.Group()
+        self.invulnerable_blocks = pygame.sprite.Group()
+        
+        self.lives = 3
+        self.livesfont = load_font('audiowide.ttf', 28)
+        self.lives_surface = self.livesfont.render(str(self.lives), True, self.ORANGE)
+        self.game_over = False
+        
+        self.score = 0
+        self.scorefont = load_font('audiowide.ttf', 17)
+        self.score_surface = self.scorefont.render(str(self.score), True, self.ORANGE)
+        self.scorelabelfont = load_font('audiowide.ttf', 17)
+        self.score_label_surface = self.scorelabelfont.render("Score", True, self.ORANGE)
+        self.combo = 0
+        self.combo_hits = 0
+        self.combo_surface = self.scorefont.render(str(self.combo), True, self.ORANGE)
+        self.combo_hits_surface = self.scorefont.render(str(self.combo_hits), True, self.ORANGE)
+        self.score_combo_plus_sign_font = load_font('audiowide.ttf', 40)
+        self.score_combo_plus_sign_surface = self.score_combo_plus_sign_font.render("+", True, self.ORANGE)
+        
+        self.text_messages = []
+        
+        self.detect_levels()
+        self.load_level(1)
+        self.waiting_for_level = False
+        
+        pygame.mouse.set_visible(False)
+        pygame.event.set_grab(True) #required to capture mouse input outside of the window
+        self.background.blit(self.heart, (30,0))
+        self.background.blit(self.score_label_surface, (29, 63))
+        self.background.blit(self.score_combo_plus_sign_surface, (47, 109))
+        self.screen.blit(self.background, (0,0))
+        pygame.display.update()
+    
+    def detect_levels(self):
+        self.available_levels = 0
+        while True:
+            if os.path.isfile(os.path.join('levels', str(self.available_levels+1) + ".txt")):
+                self.available_levels += 1
+            else:
+                break
+        
+    def load_level(self, level):
+        self.balls.empty()
+        self.blocks.empty()
+        self.invulnerable_blocks.empty()
+        self.balls.add(Ball(self.ball_image, (230+82, 625), Vector(-3,-5), attached=True))
+        self.text_messages.append(widgets.TextMessage(self.screen, "Press SPACE to begin level " + str(level), Vector(self.screen.get_width() / 2, self.screen.get_height() / 2), duration=3800, size=24, initialdelay=1200))
+        if level <= self.available_levels:
+            self.level = level
+            level_data = open(os.path.join('levels', str(level)+".txt"), 'r')
+            i = 0
+            for row in level_data:
+                row = row.split()
+                for j in range(12):
+                    if row[j].isdigit():
+                        if int(row[j]) > 0:
+                            self.blocks.add(Block((230+41*j,0+31*i),int(row[j]), self.create_powerup))
+                    elif row[j] == "X": #Roman numeral for ten
+                        self.blocks.add(Block((230+41*j,0+31*i),10, self.create_powerup))
+                    elif row[j] == "*": #invulnerable block!
+                        self.invulnerable_blocks.add(Block((230+41*j,0+31*i),1, self.create_powerup, invulnerable=True))
+                i += 1
+            level_data.close()
+        else:
+            self.load_level(level-1)
+
+    def run(self):
+        self.play_music()
+        self.total_time_passed = 0
+        while True: #main game loop
+            time_passed = self.clock.tick() / 1000.0
+            self.total_time_passed += time_passed
+            # If too long has passed between two frames, don't update 
+            # (the game must have suspended for some reason, and we don't want it to "jump forward" suddenly)
+            if time_passed > 150:
+                continue
+            mouse_x_movement = self.process_input()
+            
+            self.update(time_passed, mouse_x_movement)
+            
+            self.draw()
+
+    def process_input(self):
+        for event in pygame.event.get():
+                if event.type == pygame.QUIT:
+                    self.quit()
+                elif event.type == pygame.KEYDOWN:
+                    if event.key == pygame.K_ESCAPE:
+                        self.quit()
+                    elif event.key == pygame.K_SPACE:
+                        for ball in self.balls:
+                            ball.release_from_paddle()
+                    elif event.key == pygame.K_s:
+                        for ball in self.balls:
+                            ball.speed = 100
+                    elif event.key == pygame.K_a:
+                        self.load_level(self.level+1)
+                    elif event.key == pygame.K_d:
+                        for block in self.blocks:
+                            block.set_invulnerable()
+                    elif event.key == pygame.K_y and self.game_over:
+                        self.restart()
+                    elif event.key == pygame.K_n and self.game_over:
+                        self.force_quit()
+                elif event.type == pygame.KEYUP:
+                    if event.key == pygame.K_s:
+                        for ball in self.balls:
+                            ball.speed = 650
+                    elif event.key == pygame.K_d:
+                        for block in self.blocks:
+                            block.undo_invulnerable()
+                elif event.type == pygame.MOUSEBUTTONDOWN:
+                    for ball in self.balls:
+                        ball.release_from_paddle()
+                elif event.type == self.WAIT_NEW_LEVEL:
+                    self.waiting_for_level = False
+                    self.balls.empty()
+                    self.load_level(self.level+1)
+                    pygame.time.set_timer(self.WAIT_NEW_LEVEL, 0)
+        return pygame.mouse.get_rel()[0]
+    
+    def update(self, time_passed, mouse_x_movement):
+        self.balls.update(time_passed, self.paddle, self.blocks, self.invulnerable_blocks)
+        self.paddles.update(mouse_x_movement)
+        
+        for text_message in self.text_messages:
+            text_message.update(time_passed * 1000)
+        
+        if len(self.blocks) == 0 and not self.waiting_for_level and not self.game_over:
+            self.level_complete()
+        if len(self.balls) == 0 and not self.game_over and not self.waiting_for_level:
+            self.dropped_balls()
+        
+        self.update_score()
+        pygame.display.set_caption(TITLE + "   %.0f fps" % self.clock.get_fps())
+        
+    def play_music(self):
+        music_file_name = os.path.join('audio', self.MUSIC)
+        if not pygame.mixer or not pygame.mixer.get_init():
+            print "pygame.mixer not enabled! Skipping music."
+            return
+        elif not os.path.exists(music_file_name):
+            print "Music file not found: ", music_file_name
+            return
+        
+        pygame.mixer.music.load(music_file_name)
+        pygame.mixer.music.set_volume(0.8)
+        pygame.mixer.music.play(-1)
+
+    def stop_music(self):
+        pygame.mixer.music.stop()
+
+    def erase_rect(self, surface, rect):
+        surface.blit(self.background, rect, rect)
+
+    def draw(self, update=True):
+        self.screen.blit(self.background, (0,0))
+        self.balls.draw(self.screen)
+        if self.game_over:
+            self.screen.blit(self.lives_surface, (16, 10))
+        else:
+            self.screen.blit(self.lives_surface, (66, 10))
+        self.screen.blit(self.score_surface, (58 - self.score_surface.get_width() // 2, 92))
+        self.screen.blit(self.combo_surface, (58 - self.combo_surface.get_width() // 2, 153))
+        self.screen.blit(self.combo_hits_surface, (58 - self.combo_hits_surface.get_width() // 2, 182))
+        self.blocks.draw(self.screen)
+        self.invulnerable_blocks.draw(self.screen)
+        self.paddles.draw(self.screen)
+        
+        for text_message in self.text_messages:
+            if not text_message.timealive > text_message.duration or text_message.duration == 0:
+                text_message.draw()
+            else:
+                self.text_messages.remove(text_message)
+        if update:
+            pygame.display.update()
+    
+    def update_score(self):
+        highest_combo = 0
+        combo_length = 0 
+        for ball in self.balls:
+            if ball.score != 0:
+                self.erase_rect(self.screen, Rect((730, 83, self.scorefont.size(str(self.score))[0], self.scorefont.size(str(self.score))[1])))
+                self.score += ball.score
+                ball.score = 0
+            if ball.combo_length > 0:
+                if ball.combo > highest_combo:
+                    highest_combo = ball.combo
+                    combo_length = ball.combo_length
+        self.erase_rect(self.screen, Rect((730, 120, self.scorefont.size(str(self.combo))[0], self.scorefont.size(str(self.combo))[1])))
+        self.combo = highest_combo
+        self.combo_hits = combo_length
+        
+        self.score_surface = self.scorefont.render(str(self.score), True, self.ORANGE)
+        self.combo_surface = self.scorefont.render(str(self.combo), True, self.ORANGE)
+        self.combo_hits_surface = self.scorefont.render("(" + str(self.combo_hits) + " hits)", True, self.ORANGE)
+    
+    def collect_all_score(self):
+        for ball in self.balls:
+                self.score += ball.combo
+    
+    def create_powerup(self):
+        print "powerup!"
+    
+    def level_complete(self):
+        self.waiting_for_level = True
+        self.collect_all_score()
+        if self.level < self.available_levels:
+            pygame.time.set_timer(self.WAIT_NEW_LEVEL, 1800)
+            self.text_messages.append(widgets.TextMessage(self.screen, "Level Complete!", Vector(self.screen.get_width() / 2, self.screen.get_height() / 2), duration=1800, size=32, initialdelay=800))
+        else:
+            self.text_messages.append(widgets.TextMessage(self.screen, "Congratulations! You beat all " + str(self.available_levels) + " levels. Play again? Y/n", Vector(self.screen.get_width() / 2, self.screen.get_height() / 2), duration=9999999, size=24, initialdelay=9999990))
+            self.add_to_highscore()
+            self.game_over = True
+    
+    def dropped_balls(self):
+        self.erase_rect(self.screen, Rect((168, 88, self.livesfont.size(str(self.lives))[0], self.livesfont.size(str(self.lives))[1])))
+        self.lives -= 1
+        if self.lives > 0:
+            self.balls.add(Ball(self.ball_image, (230+82, 625), Vector(-3,-5), attached=True))
+            self.text_messages.append(widgets.TextMessage(self.screen, "Press SPACE to begin", Vector(self.screen.get_width() / 2, self.screen.get_height() / 2), duration=1800, size=32, initialdelay=800))
+        else:
+            self.lost()
+        self.lives_surface = self.livesfont.render(str(self.lives), True, self.ORANGE)
+    
+    def lost(self):
+        self.game_over = True
+        self.lives = "DEAD"
+        self.text_messages.append(widgets.TextMessage(self.screen, "You died. Play again? Y/n", Vector(self.screen.get_width() / 2, self.screen.get_height() / 2), duration=9999999, size=32, initialdelay=9999990))
+        self.background = load_image('background.png')
+        self.add_to_highscore()    
+
+    def add_to_highscore(self):
+        if not os.path.exists('playername.txt'):
+            playername_file = open('playername.txt', 'w')
+            playername_file.write("Player")
+            playername_file.close()
+        playername_file = open('playername.txt', 'r')
+        playername = playername_file.readline()
+        playername.rstrip()
+        playername_file.close()
+        
+        now = datetime.datetime.now()
+        
+        highscore = open('highscores.txt', 'a')
+        highscore.write(str(now.year)+"-" + str(now.month) + "-" + str(now.day) + " " + str(now.hour).zfill(2) + ":" +  str(now.minute) + " " + str(playername) + " scored " + str(self.score) + "\n")
+        highscore.close()
+    
+    def pause(self, resume=False):
+        if not resume:
+            pygame.event.set_grab(False)
+            pygame.mouse.set_visible(True)
+            
+        else:
+            pygame.event.set_grab(True)
+            pygame.mouse.set_visible(False)
+            self.clock.tick() #Tick the clock to discard the passed time
+
+    def restart(self):
+        screen = self.screen
+        del self
+        game = Game(screen)
+        game.run()
+
+    def quit(self):
+        self.pause()
+        if widgets.UserConfirm(self.screen, message="Return to Main Menu and lose progress?", backgroundclass=self):
+            self.add_to_highscore()
+            self.force_quit()
+        self.pause(resume=True)
+    
+    def force_quit(self):
+        self.pause() #Restores mouse state
+        self.stop_music()
+        screen = self.screen
+        del self
+        menu = Menu(screen)
+
+class Menu(object):
+    """Adapted from PyTowerDefense (http://sourceforge.net/projects/pytowerdefense/), an earlier project of mine"""
+    BG_IMG = 'background.png'
+    NEW_GAME_CLICK = pygame.locals.USEREVENT + 1
+    HELP_CLICK = pygame.locals.USEREVENT + 2
+    HIGHSCORES_CLICK = pygame.locals.USEREVENT + 3
+    EXIT_CLICK = pygame.locals.USEREVENT + 4
+    BACK_CLICK = pygame.locals.USEREVENT + 5
+    ORANGE = (130, 40, 0)
+    WHITE = (192, 192, 192) #(210, 210, 210)
+    def __init__(self, screen, game=None):
+        self.bg_img = load_image(self.BG_IMG)
+        self.screen = screen
+        self.game = game
+        self.titlefont = load_font('audiowide.ttf', 56)
+        self.title = self.titlefont.render(TITLE, True, self.WHITE)
+        # Text Widget lists
+        self.main_text_widgets = []
+        self.help_text_widgets = []
+        self.highscore_text_widgets = []
+        self.main()
+
+    def draw_bg(self, rect=None):
+        if not rect:
+            self.screen.blit(self.bg_img, (0,0))
+            self.screen.blit(self.title, (self.screen.get_rect().centerx - self.titlefont.size(TITLE)[0] / 2, self.screen.get_rect().centery - (self.titlefont.size(TITLE)[1] / 2) - 170))
+        else:
+            self.screen.blit(self.bg_img, rect, rect)
+    def main(self):
+        self.state = "Main"
+        self.draw_bg()
+        
+        self.new_game_text = widgets.TextWidget("Start Game", colour=self.WHITE, size=44, highlight_increase=3, event=self.NEW_GAME_CLICK, font_filename='audiowide.ttf', bold=False)
+        self.new_game_text.rect.center = self.screen.get_rect().center
+        self.new_game_text.rect.top -= 25
+        self.main_text_widgets.append(self.new_game_text)
+        
+        self.help_text = widgets.TextWidget("Help", colour=self.WHITE, size=44, highlight_increase=3, event=self.HELP_CLICK, font_filename='audiowide.ttf', bold=False)
+        self.help_text.rect.center = self.screen.get_rect().center
+        self.help_text.rect.top += 50
+        self.main_text_widgets.append(self.help_text)
+
+        self.help_text = widgets.TextWidget("Highscores", colour=self.WHITE, size=44, highlight_increase=3, event=self.HIGHSCORES_CLICK, font_filename='audiowide.ttf', bold=False)
+        self.help_text.rect.center = self.screen.get_rect().center
+        self.help_text.rect.top += 125
+        self.main_text_widgets.append(self.help_text)
+
+        self.exit_text = widgets.TextWidget("Exit Game", colour=self.WHITE, size=44, highlight_increase=3, event=self.EXIT_CLICK, font_filename='audiowide.ttf', bold=False)
+        self.exit_text.rect.center = self.screen.get_rect().center
+        self.exit_text.rect.top += 200
+        self.main_text_widgets.append(self.exit_text)
+        self.loop()
+
+    def help(self):
+        self.state = "Help"
+        helpstring = "Press SPACE (or left click) to put the ball into motion.\n\nMove the paddle with your mouse to keep the ball in play!\n\nThe ball will go left if it hits the left side of the paddle, and so on.\n\nEliminate all blocks to advance to the next level.\n\nEarn more score through combo-streaks, that is, hit as many bricks as possible without touching the paddle.\n\nWhite blocks are indestructible\n\nYou can pause and resume with ESC"
+        menufont = load_font('audiowide.ttf', 21)
+        menurect = Rect(270,35,460,630)
+        self.draw_bg(self.screen.get_rect())
+        textsurface = widgets.render_textrect(helpstring, menufont, menurect, self.WHITE, justification=0, background=self.bg_img)
+        self.screen.blit(textsurface, (270,35))#((self.screen.get_width() / 2) - menutext.get_width() / 2, (self.screen.get_height() / 2) - menutext.get_height()))
+        
+        self.back_text = widgets.TextWidget("Back", colour=self.WHITE, size=44, highlight_increase=3, event=self.BACK_CLICK, font_filename='audiowide.ttf', bold=False)
+        self.back_text.rect.center = self.screen.get_rect().center
+        self.back_text.rect.top += 250
+        self.help_text_widgets.append(self.back_text)
+        
+        self.loop()
+        
+    def highscore(self):
+        self.state = "Highscore"
+        highscores = "Top 20:\n\n\n"
+        rank = 1
+        highscores_list = self.read_highscores()
+        for highscore in highscores_list:
+            highscores += str(rank) + ". " + highscore
+            rank += 1
+        
+        if len(highscores_list) == 20:
+            highscores += "\n\nFor a full list, see highscores.txt"
+        
+        self.draw_bg()
+        menufont = load_font('audiowide.ttf', 16)
+        menurect = Rect(120,35,760,620)
+        textsurface = widgets.render_textrect(highscores, menufont, menurect, self.WHITE, justification=1, background=self.bg_img)
+        self.screen.blit(textsurface, (120,35))#((self.screen.get_width() / 2) - menutext.get_width() / 2, (self.screen.get_height() / 2) - menutext.get_height()))
+        
+        self.back_text = widgets.TextWidget("Back", colour=self.WHITE, size=44, highlight_increase=3, event=self.BACK_CLICK, font_filename='audiowide.ttf', bold=False)
+        self.back_text.rect.center = self.screen.get_rect().center
+        self.back_text.rect.top += 250
+        self.highscore_text_widgets.append(self.back_text)
+        
+        self.loop()
+    
+    def read_highscores(self):
+        """Loads up to the 20 highest highscores and returns them in a sorted list"""
+        highscores_list = []
+        highscores_dict = defaultdict(list)
+        highscores_file = open('highscores.txt', 'r')
+        for highscore_line in highscores_file:
+            if highscore_line != "\n":
+                score = [int(s) for s in highscore_line.split() if s.isdigit()][-1] #Extract the rightmost integer in the string (safeguard against integer player names)
+                highscores_dict[score].append(highscore_line)
+        highscores_file.close()
+        
+        sorted_scores = sorted(highscores_dict.keys(), reverse=True)
+        
+        for score in sorted_scores:
+            all_with_score_as_score = highscores_dict[score]
+            all_with_score_as_score.reverse() #Makes newest entries in highscores.txt appear at the top if equal to an older score
+            for highscore_line in all_with_score_as_score:
+                highscores_list.append(highscore_line)
+         
+        highscores_list = highscores_list[:20]
+        return  highscores_list
+
+    def loop(self):
+        while True:
+            for event in pygame.event.get():
+                if event.type == pygame.QUIT:
+                    self.quit()
+                elif event.type == pygame.KEYDOWN:
+                    if event.key == pygame.K_ESCAPE:
+                        if self.state == "Main":
+                            self.quit()
+                        else:
+                            if self.state == "Help":
+                                self.main()
+                            elif self.state == "Highscore":
+                                self.main()
+                    elif event.key == pygame.K_SPACE:
+                        if self.state == "Main":
+                            self.run_game()
+                        elif self.state == "Help":
+                            self.main()
+                        elif self.state == "Highscore":
+                                self.main()
+                    elif event.key== pygame.K_h and self.state == "Main":
+                        self.help()
+                #TextWidget stuff:
+                if self.state == "Main":
+                    if (event.type == pygame.ACTIVEEVENT):
+                        if (event.gain == 1):
+                            for text in self.main_text_widgets:
+                                text.dirty = True
+                            self.draw()
+                        elif (event.state == 2):
+                            #We are hidden so wait for the next event
+                            pygame.event.post(pygame.event.wait())
+                    elif (event.type == pygame.MOUSEMOTION):
+                        for text in self.main_text_widgets:
+                            orig = text.highlight
+                            orig_rect = text.rect
+                            text.highlight = text.rect.collidepoint(event.pos)
+                            if orig != text.highlight:
+                                for t in self.main_text_widgets:
+                                    t.dirty = True
+                                self.draw_bg(rect=orig_rect) #Redraw background if highlight state changes
+                                
+                    elif (event.type == pygame.MOUSEBUTTONDOWN):
+                        for text in self.main_text_widgets:
+                            text.on_mouse_button_down(event)
+                    elif (event.type == pygame.MOUSEBUTTONUP):
+                        for text in self.main_text_widgets:
+                            text.on_mouse_button_up(event)
+                    elif (event.type == self.NEW_GAME_CLICK):
+                        pygame.mouse.set_cursor(*pygame.cursors.arrow)
+                        self.run_game()
+                    elif (event.type == self.HELP_CLICK):
+                        pygame.mouse.set_cursor(*pygame.cursors.arrow)
+                        self.help()
+                    elif (event.type == self.HIGHSCORES_CLICK):
+                        pygame.mouse.set_cursor(*pygame.cursors.arrow)
+                        self.highscore()
+                    elif (event.type == self.EXIT_CLICK):
+                        self.quit()
+                elif self.state == "Help":
+                    if (event.type == pygame.ACTIVEEVENT):
+                        if (event.gain == 1):
+                            for text in self.help_text_widgets:
+                                text.dirty = True
+                            self.draw()
+                        elif (event.state == 2):
+                            #We are hidden so wait for the next event
+                            pygame.event.post(pygame.event.wait())
+                    elif (event.type == pygame.MOUSEMOTION):
+                        for text in self.help_text_widgets:
+                            orig = text.highlight
+                            orig_rect = text.rect
+                            text.highlight = text.rect.collidepoint(event.pos)
+                            if orig != text.highlight:
+                                for t in self.help_text_widgets:
+                                    t.dirty = True
+                                self.draw_bg(rect=orig_rect) #Redraw background if highlight state changes
+                    elif (event.type == pygame.MOUSEBUTTONDOWN):
+                        for text in self.help_text_widgets:
+                            text.on_mouse_button_down(event)
+                    elif (event.type == pygame.MOUSEBUTTONUP):
+                        for text in self.help_text_widgets:
+                            text.on_mouse_button_up(event)
+                    elif (event.type == self.BACK_CLICK):
+                        self.main()
+                elif self.state == "Highscore":
+                    if (event.type == pygame.ACTIVEEVENT):
+                        if (event.gain == 1):
+                            for text in self.highscore_text_widgets:
+                                text.dirty = True
+                            self.draw()
+                        elif (event.state == 2):
+                            #We are hidden so wait for the next event
+                            pygame.event.post(pygame.event.wait())
+                    elif (event.type == pygame.MOUSEMOTION):
+                        for text in self.highscore_text_widgets:
+                            orig = text.highlight
+                            orig_rect = text.rect
+                            text.highlight = text.rect.collidepoint(event.pos)
+                            if orig != text.highlight:
+                                for t in self.highscore_text_widgets:
+                                    t.dirty = True
+                                self.draw_bg(rect=orig_rect) #Redraw background if highlight state changes
+                    elif (event.type == pygame.MOUSEBUTTONDOWN):
+                        for text in self.highscore_text_widgets:
+                            text.on_mouse_button_down(event)
+                    elif (event.type == pygame.MOUSEBUTTONUP):
+                        for text in self.highscore_text_widgets:
+                            text.on_mouse_button_up(event)
+                    elif (event.type == self.BACK_CLICK):
+                        self.main()
+            self.draw()
+
+    def draw(self):
+        """Draw everything"""
+        for text in self.main_text_widgets:
+            text.draw(self.screen)
+        for text in self.help_text_widgets:
+            text.draw(self.screen)
+        for text in self.highscore_text_widgets:
+            text.draw(self.screen)
+        pygame.display.update()
+
+    def run_game(self):
+        self.game = Game(self.screen)
+        self.game.run()
+        del self
+
+    def quit(self):
+        pygame.quit()
+        sys.exit()
+
+		
+def we_are_frozen():
+    """Returns whether we are frozen via py2exe.
+    This will affect how we find out where we are located."""
+
+    return hasattr(sys, "frozen")
+
+
+def module_path():
+    """ This will get us the program's directory,
+    even if we are frozen using py2exe"""
+    if we_are_frozen():
+        return os.path.dirname(unicode(sys.executable, sys.getfilesystemencoding( )))
+
+    return os.path.dirname(unicode(__file__, sys.getfilesystemencoding( )))
+		
+if __name__ == '__main__':
+    pygame.mixer.pre_init(44100, -16, 2, 1024) #sound effects are delayed on my windows machine without this, I think the buffer is initialized too large by default
+    pygame.init()
+    while True:
+        screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
+        pygame.display.set_caption(TITLE)
+        pygame.display.set_icon(load_image(os.path.join('blocks', 'lightgreen.png')))
+        menu = Menu(screen)