class InvadersWindow(pyglet.window.Window):
    """
    This class does all managing: it draws to the screen, and
    updates all the bits and pieces flying around the screen!

    Extends pyglet.window.Window, overwriting the on_draw method.
    """

    def __init__(self):
        """
        This sets everything up. Factoid: Init is short for 'initialise'.

        We call up to pyglets Window init to do the heavy lifting,
        specifying a width, height and caption (title).
        """
        # Create pyglet window - the caption is the window title
        pyglet.window.Window.__init__(
            self,
            caption="Invaders From Space!",
            width=640,
            height=480)
        from objects import Player
        self.player = Player()
        self.push_handlers(self.player.key_handler)

    def on_draw(self):
        """
        Overrides Window.on_draw.
        """
        # First off we wipe the slate clean.
        self.clear()
        self.player.draw()

    def update(self, elapsed_time):
        """
        Perform frame-rate indepent updates of game objects.
        """
        self.player.update(elapsed_time=elapsed_time)
        pass
class Game:
    def __init__(self):
        self.tk = Tk()
        self.tk.title('Chrome')
        self.tk.resizable(0, 0)
        self.canvas = Canvas(self.tk,
                             width=800,
                             height=200,
                             bd=0,
                             highlightthickness=0)
        self.canvas.config(bg='black')
        self.canvas.pack()

        self.player = Player(self.canvas)
        self.floor = Floor(self.canvas)
        self.obstacles = []
        self.current_speed = 5
        self.frame_number = 0

    def next_frame(self, action):
        done = False
        # Do we have to remove the first obstacle from obstacles
        elim = False

        # Creating image and drawing with PIL
        img = Image.new('L', (800, 200), 0)
        drawing = ImageDraw.Draw(img)

        # Creating new random obstacle and change speed of the game
        if self.frame_number % 40 == 0:
            if self.current_speed < 25:
                self.current_speed += 0.5
            else:
                print('Game beat!')
                done = True
            self.obstacles.append(
                Obstacle(self.canvas, random.choice([1, 2, 3, 4]),
                         self.current_speed))

        # Checking collision
        for i in range(len(self.obstacles)):
            obs_pos = self.obstacles[i].get_coords()
            player_pos = self.player.get_coords()

            # Getting coords of corners of player and obstacles
            obs_corners = [(obs_pos[0], obs_pos[1]), (obs_pos[2], obs_pos[1]),
                           (obs_pos[0], obs_pos[3]), (obs_pos[2], obs_pos[3])]
            player_corners = [(player_pos[0], player_pos[1]),
                              (player_pos[2], player_pos[1]),
                              (player_pos[0], player_pos[3]),
                              (player_pos[2], player_pos[3])]
            for corner in obs_corners:
                if player_pos[0] <= corner[0] <= player_pos[2] and player_pos[
                        1] <= corner[1] <= player_pos[3]:
                    done = True
            for corner in player_corners:
                if obs_pos[0] <= corner[0] <= obs_pos[2] and obs_pos[
                        1] <= corner[1] <= obs_pos[3]:
                    done = True

            # Removing obstacles or drawing them
            if obs_pos[2] < -1:
                self.obstacles[i].remove()
                elim = True
            else:
                self.obstacles[i].draw(drawing, self.current_speed)

        # Drawing a player
        self.player.draw(action, drawing)
        # Updating screen
        self.tk.update_idletasks()
        self.tk.update()

        # Remove obstacle if needed
        if elim:
            self.obstacles = self.obstacles[1:]

        return np.array(img)[:175, :], done

    def reset(self, mode='easy'):
        # Reseting player and params
        self.player.reset()
        self.frame_number = 0
        self.current_speed = 13

        # Removing obstacles
        for i in range(len(self.obstacles)):
            self.obstacles[i].remove()
        self.obstacles = []

        # Getting start frame (first action is jump)
        start_frame, _, _ = self.step('n', mode)
        return start_frame

    def step(self, action, mode='easy'):
        # Get params of the next frame
        cur_x, done = self.next_frame(action)

        # Calculating reward
        if done:
            reward = -500 / self.frame_number
        else:
            reward = 0.01 * self.frame_number

        # Increment frame_number (score)
        self.frame_number += 1

        if mode == 'hard':
            return cur_x, done, reward

        # Players current position
        pos_player = self.player.get_coords()

        player_y = (pos_player[1] - 85) / 90
        # Ensure that there are no errors
        obstacle_distance = -1
        obstacle_x = -1
        obstacle_y = -1
        min_y = -1
        try:
            # Get params and normalize them
            if self.obstacles[0].get_coords()[2] - pos_player[0] > 0:
                pos_obstacle = self.obstacles[0].get_coords()

                obstacle_distance = (pos_player[2] - pos_obstacle[0]) / 765
                obstacle_x = (pos_obstacle[2] - pos_obstacle[0]) / 40
                obstacle_y = (pos_obstacle[3] - pos_obstacle[1]) / 40
                min_y = (pos_obstacle[1] - 80) / 65
            elif len(self.obstacles) > 1:
                pos_obstacle = self.obstacles[1].get_coords()

                obstacle_distance = (pos_player[2] - pos_obstacle[0]) / 765
                obstacle_x = (pos_obstacle[2] - pos_obstacle()[0]) / 40
                obstacle_y = (pos_obstacle[3] - pos_obstacle[1]) / 40
                min_y = (pos_obstacle[1] - 80) / 65
        except:
            pass

        speed = (self.current_speed - 5) / 20

        return np.array([
            player_y, obstacle_distance, obstacle_x, obstacle_y, min_y, speed
        ]), done, reward
Exemple #3
0
            x = random.randint(10, WIDTH - 100)
            e = Enemy(x, -150, type)
            enemy_group.add(e)
            start_time = current_time

        if plane_destroy_count:
            if plane_destroy_count % 5 == 0 and level < 5:
                level += 1
                plane_destroy_count = 0

        p.fuel -= 0.05
        bg.update(1)
        win.blit(clouds_img, (0, 70))

        p.update(moving_left, moving_right, explosion_group)
        p.draw(win)

        player_bullet_group.update()
        player_bullet_group.draw(win)
        enemy_bullet_group.update()
        enemy_bullet_group.draw(win)
        explosion_group.update()
        explosion_group.draw(win)
        fuel_group.update()
        fuel_group.draw(win)
        powerup_group.update()
        powerup_group.draw(win)

        enemy_group.update(enemy_bullet_group, explosion_group)
        enemy_group.draw(win)
Exemple #4
0
class Game:
    def __init__(self, seed, size, volume):
        # Константы генерации карты
        self.SEED = 15
        self.SIZE = int(1024 * size)
        self.H_MAX = 20
        self.SHARP = 2
        self.DEPTH = 100
        self.PROBABILITY = 1/2
        self.H_0 = 12
        self.LAKES_NUMBER = int(10*size)
        
        # Инициализируем пайгейм
        pygame.init()
        self.screen = pygame.display.set_mode((0,0),pygame.FULLSCREEN)
        pygame.display.update()
        self.clock = pygame.time.Clock()
        
        # Константы игры
        self.FPS = 20    
        self.finished = False
        self.BLOCK_SIZE = 20
        self.DO_RADIUS = 140
        self.inventory_bool = False
        self.volume = volume
        
        # Размеры окна
        self.WINDOW_X, self.WINDOW_Y = pygame.display.get_surface().get_size()
        self.ORIGINAL_WINDOW_X = 1920
        self.ORIGINAL_WINDOW_Y = 1080
        self.RATIO_X = self.WINDOW_X/self.ORIGINAL_WINDOW_X
        self.RATIO_Y = self.WINDOW_Y/self.ORIGINAL_WINDOW_Y        
        
        # Подгружаем текстуры карты
        self.dirt_grass = pygame.image.load('resources/map/dirt_grass.png')

        self.blocks = [[pygame.image.load('resources/map/dirt.png'), 
                        pygame.image.load('resources/map/stone.png')],
                       [pygame.image.load('resources/map/snow.png'), 
                        pygame.image.load('resources/map/ice.png')]]

        self.water = [pygame.image.load('resources/map/water.png'), 
                      pygame.image.load('resources/map/ice_water.png')]
        
        # Подгружаем текстуры персонажа
        self.player_animation = [pygame.image.load('resources/player_animation/player_right.png'), 
                    pygame.image.load('resources/player_animation/move_right_1.png'),
                    pygame.image.load('resources/player_animation/move_right_2.png'),
                    pygame.image.load('resources/player_animation/player_left.png'), 
                    pygame.image.load('resources/player_animation/move_left_1.png'),
                    pygame.image.load('resources/player_animation/move_left_2.png')]
        
        # Подгружаем бэкграунд
        self.bg = pygame.image.load('resources/bg.png')
        self.bg = pygame.transform.scale(self.bg, (self.WINDOW_X, self.WINDOW_Y))
        
        # Подгружаем текстуры инвентаря
        self.inventory_image = pygame.image.load('resources/inventory/inventory.png')
        self.INVENTORY_IMAGE_SIZE_X = int(1000*self.RATIO_X)
        self.INVENTORY_IMAGE_SIZE_Y = int(100*self.RATIO_Y)
        self.inventory_image = pygame.transform.scale(self.inventory_image, (self.INVENTORY_IMAGE_SIZE_X, self.INVENTORY_IMAGE_SIZE_Y))
        
        self.tools_invent = [pygame.image.load('resources/inventory/inventory_image/dirt.png'),
                             pygame.image.load('resources/inventory/inventory_image/stone.png')]
        self.TOOLS_INVENT_SIZE_X = int(100*self.RATIO_X)
        self.TOOLS_INVENT_SIZE_Y = int(100*self.RATIO_Y)
        for i in range(0, len(self.tools_invent)):
            self.tools_invent[i] = pygame.transform.scale(self.tools_invent[i], (self.TOOLS_INVENT_SIZE_X, self.TOOLS_INVENT_SIZE_Y))                           

        self.cell = pygame.image.load('resources/inventory/cell.png')
        self.CELL_SIZE_X = int(100*self.RATIO_X)
        self.CELL_SIZE_Y = int(120*self.RATIO_Y)
        self.cell = pygame.transform.scale(self.cell, (self.CELL_SIZE_X, self.CELL_SIZE_Y))
        
        # Подгружаем текстуры инструментов
        self.tools_images = [[pygame.image.load('resources/tools/shovel.png'),
                         pygame.image.load('resources/tools/pick.png')],
                        [pygame.image.load('resources/tools/shovel_right.png'),
                         pygame.image.load('resources/tools/pick_right.png')],     
                        [pygame.image.load('resources/tools/shovel_right_1.png'), 
                         pygame.image.load('resources/tools/pick_right_1.png')],
                        [pygame.image.load('resources/tools/shovel_right_2.png'), 
                         pygame.image.load('resources/tools/pick_right_2.png')],
                        [pygame.image.load('resources/tools/shovel_left.png'),
                         pygame.image.load('resources/tools/pick_left.png')],
                        [pygame.image.load('resources/tools/shovel_left_1.png'),
                         pygame.image.load('resources/tools/pick_left_1.png')],
                        [pygame.image.load('resources/tools/shovel_left_2.png'),
                         pygame.image.load('resources/tools/pick_left_2.png')]]
        for i in range(0, 2):
            self.tools_images[0][i] = pygame.transform.scale(self.tools_images[0][i], (self.TOOLS_INVENT_SIZE_X, self.TOOLS_INVENT_SIZE_Y))  
            
        self.check = pygame.image.load('resources/tools/check.png')
        self.check = pygame.transform.scale(self.check, (self.TOOLS_INVENT_SIZE_X, self.TOOLS_INVENT_SIZE_Y))
        
        # Создаем карту
        self.map = generate_map(self.SEED, self.SIZE, self.H_MAX, self.SHARP, self.DEPTH, self.PROBABILITY) 
        self.biome_map, map = biome_generator(self.SEED, self.DEPTH, self.SIZE, self.map, self.LAKES_NUMBER)
        
        # Создаем персонажа и его рюкзак
        self.player = Player(self.SIZE/2, self.H_0, self.DEPTH, self.SIZE, self.WINDOW_X, self.BLOCK_SIZE)
        self.player.y_calculation(self.map, self.H_MAX)
        self.backpack = Backpack()
        
        # Загружаем музыку
        pygame.mixer.music.load('sounds/background_music.wav')
        
        #Загружаем шрифт
        pygame.font.init()
        self.font_1 = pygame.font.Font(None, 36)
        
        
    def draw_map(self, x, y, H):
        self.screen.blit(self.bg, (0, 0))
        
        for i in range(0, int(self.WINDOW_X/self.BLOCK_SIZE)):
            h = -1
            for j in range(1, 1 + y + self.H_0 - H):
                if self.map[j][x - int(self.WINDOW_X/self.BLOCK_SIZE/2) + i] != 0:
                    h = y - j + self.H_0 - H
                    break
                
            if h >= 0:
                # Рисуем верхние блоки
                if self.biome_map[y + self.H_0 - H - h][x - int(self.WINDOW_X/self.BLOCK_SIZE/2) + i] == 0.5: 
                    self.screen.blit(self.dirt_grass,(20*i, self.WINDOW_Y - 20*(h)))
                elif self.biome_map[y + self.H_0 - H - h][x - int(self.WINDOW_X/self.BLOCK_SIZE/2) + i]  > 2:
                    self.screen.blit(self.blocks[1][int(self.map[y + self.H_0 - H - h][x - int(self.WINDOW_X/self.BLOCK_SIZE/2) + i] - 1)], (20*i, self.WINDOW_Y - 20*(h)))
                elif self.map[y + self.H_0 - H - h][x - int(self.WINDOW_X/self.BLOCK_SIZE/2) + i] > 0.5:
                    self.screen.blit(self.blocks[0][int(self.map[y + self.H_0 - H - h][x - int(self.WINDOW_X/self.BLOCK_SIZE/2) + i] - 1)], (20*i, self.WINDOW_Y - 20*h))
                    
                #Рисуем твердые блоки
                for j in range(y + self.H_0 - H - h + 1, 1 + y + self.H_0 - H): 
                    if self.biome_map[j][x - int(self.WINDOW_X/self.BLOCK_SIZE/2) + i]  > 2:
                        self.screen.blit(self.blocks[1][int(self.map[j][x - int(self.WINDOW_X/self.BLOCK_SIZE/2) + i] - 1)], (20*i, self.WINDOW_Y - 20*(y - j + self.H_0 - H)))
                    elif self.map[j][x - int(self.WINDOW_X/self.BLOCK_SIZE/2) + i] > 0:
                        self.screen.blit(self.blocks[0][int(self.map[j][x - int(self.WINDOW_X/self.BLOCK_SIZE/2) + i] - 1)], (20*i, self.WINDOW_Y - 20*(y - j + self.H_0 - H)))
        
            #Рисуем воду
            for j in range(1 + y + self.H_0 - H - int(self.WINDOW_Y/self.BLOCK_SIZE), 1 + y + self.H_0 - H):  
                if self.biome_map[j][x - int(self.WINDOW_X/self.BLOCK_SIZE/2) + i] < 0:
                    self.screen.blit(self.water[0], (20*i, self.WINDOW_Y - 20*(y - j + self.H_0 - H)))
                    
                    
    def draw_player(self, number, height):
        self.screen.blit(self.player_animation[number], (self.WINDOW_X/2, self.WINDOW_Y - self.H_0*self.BLOCK_SIZE - height))
    
    
    def draw_inventory(self, inventory, hand, hand_image, height):
        self.screen.blit(self.inventory_image, (0, 0))
        self.screen.blit(self.check, ((hand - 1)*self.TOOLS_INVENT_SIZE_X, 0))
        for i in range(0, len(inventory)):
            if inventory[i] != 0:
                self.screen.blit(self.tools_images[0][inventory[i] - 1], (i*self.TOOLS_INVENT_SIZE_X, 0))
                if hand_image == 4:
                    self.screen.blit(self.tools_images[4][hand - 1], (self.WINDOW_X/2 - 30, self.WINDOW_Y - self.H_0*self.BLOCK_SIZE - 150))
                elif hand_image == 1:
                    self.screen.blit(self.tools_images[1][hand - 1], (self.WINDOW_X/2 - 30, self.WINDOW_Y - self.H_0*self.BLOCK_SIZE - 150))
                else:
                    self.screen.blit(self.tools_images[hand_image][hand - 1], (self.WINDOW_X/2, self.WINDOW_Y - self.H_0*self.BLOCK_SIZE - height))
            
        if self.inventory_bool:
            slots, count, size = self.backpack.show()
            for i in range(0, size):
                self.screen.blit(self.cell, (300 + 100*(i - 4*(i//4)), 100 + 120*(i//4)))
                if slots[i] != 0:
                    self.screen.blit(self.tools_invent[int(slots[i] - 1)], (310 + 100*(i - 4*(i//4)), 110 + 120*(i//4)))
                    self.screen.blit(self.font_1.render(str(count[i]), True, (0, 0, 0)), (380 + 100*(i - 4*(i//4)), 195 + 120*(i//4)))

    # Проверка на воду
    def water_check(self, i, j):
        if self.biome_map[i-1][j] == -1 or self.biome_map[i][j-1] == -1 or self.biome_map[i][j+1] == -1:
            self.biome_map[i][j] = -1
            if self.map[i][j-1] == 0 and self.biome_map[i][j-1] >= 0:
                self.water_check(i, j-1)
            if self.map[i][j+1] == 0 and self.biome_map[i][j+1] >= 0:
                self.water_check(i, j+1)
            if self.map[i+1][j] == 0 and self.biome_map[i+1][j] >= 0:
                self.water_check(i+1, j)

    def doing(self, x, y, player_x, player_y, type, height):
        if (x > self.WINDOW_X/2 + self.BLOCK_SIZE - self.DO_RADIUS 
            and x < self.WINDOW_X/2 + self.BLOCK_SIZE + self.DO_RADIUS):
            if (y > self.WINDOW_Y - self.H_0*self.BLOCK_SIZE - height/2 - self.DO_RADIUS 
                and y < self.WINDOW_Y - self.H_0*self.BLOCK_SIZE - height/2 + self.DO_RADIUS):
                x = x - self.WINDOW_X/2
                y = y - self.WINDOW_Y + self.H_0*self.BLOCK_SIZE
                new_y = int(player_y + y//self.BLOCK_SIZE)
                new_x = int(player_x + x//self.BLOCK_SIZE)
                if self.map[new_y][new_x] == type:
                    self.map[new_y][new_x] = 0
                    self.backpack.add(int(self.biome_map[new_y][new_x]))
                    self.water_check(new_y, new_x)
        
    def main(self):
        pygame.mixer.music.play(-1)
        pygame.mixer.music.set_volume(self.volume/5)
        
        while not self.finished:
            self.player.checking(self.map)
            start_time = time.time()
            self.clock.tick(self.FPS)
            x, y, H, bool = self.player.recalculation(self.map)
            if x >= self.SIZE - self.WINDOW_X/self.BLOCK_SIZE/2:
                x = self.SIZE - self.WINDOW_X/self.BLOCK_SIZE/2 - 1
            if x <= self.WINDOW_X/self.BLOCK_SIZE/2:
                x = self.WINDOW_X/self.BLOCK_SIZE/2 + 1
            self.draw_map(int(x), int(y), int(H))
            
            number, height = self.player.draw()
            self.draw_player(number, height)
            
            inventory, hand, hand_image, height = self.player.player_inventory()
            self.draw_inventory(inventory, hand, hand_image, height)
            
            
            
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.finished = True
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    coord = event.pos
                    if self.player.doing() == 0:
                        pass
                    else:
                        self.doing(coord[0], coord[1], x, y, self.player.doing(), height)
                        pass
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_i:
                        if self.inventory_bool:
                            self.inventory_bool = False
                        else:
                            self.inventory_bool = True
                        
            keys = pygame.key.get_pressed()
            if keys[pygame.K_SPACE]:
                if keys[pygame.K_d]:
                    self.player.jumping_right(True, self.map)
                if keys[pygame.K_a]:
                    self.player.jumping_left(True, self.map)
                self.player.jumping()
            elif keys[pygame.K_d]:
                self.player.moving_right(True)
            elif keys[pygame.K_a]:
                self.player.moving_left(True)
            else:
                self.player.moving_right(False)
                self.player.moving_left(False)
                
            if keys[pygame.K_1]:
                self.player.change_hand(1)
            
            if keys[pygame.K_2]:
                self.player.change_hand(2)
            
            if keys[pygame.K_3]:
                self.player.change_hand(3)
            
            if keys[pygame.K_4]:
                self.player.change_hand(4)
                    
            if keys[pygame.K_5]:
                self.player.change_hand(5)
            
            if keys[pygame.K_6]:
                self.player.change_hand(6)
            
            if keys[pygame.K_7]:
                self.player.change_hand(7)
            
            if keys[pygame.K_8]:
                self.player.change_hand(8)
            
            if keys[pygame.K_9]:
                self.player.change_hand(9)
            
            if keys[pygame.K_0]:
                self.player.change_hand(10)

            if keys[pygame.K_s]:
                self.save()
                    
            if keys[pygame.K_ESCAPE]:
                pygame.quit()
        
            pygame.display.update()
            print("FPS: ", 1.0 / (time.time() - start_time))

        pygame.quit()

    def load(self):
        with open('saves/data.json', 'r') as file:
            data = json.load(file)
            self.map = data['map']
            self.biome_map = data['biome_map']
            self.SIZE = data['size']
            self.player.load()
            self.backpack.load()

    def save(self):
        with open('saves/data.json', 'w') as file:
            print(dict({'map': self.map.tolist(), 'biome_map': self.biome_map.tolist(), 'size': self.SIZE}))
            json.dump(dict({'map': self.map.tolist(), 'biome_map': self.biome_map.tolist(), 'size': self.SIZE}), file)
            self.player.save()
            self.backpack.save()
class InvadersWindow(object):
    """This class does all managing: it draws to the screen, and
    runs the main game loop.

    Class variables:
    seconds_till_lurch -- the seconds between aliens lurching (default 5)
    aliens_per_row -- how many new aliens per row created (default 5)

    Instance Variables:
    game_over_label -- Initially None, set to a pyglet label by game_over
    aliens -- List of all Alien objects in the game.
    lasers -- List of all laser blasts in the game.
    player -- The Player object.
    bullets -- The list of bullets in the game.
    window -- The pyglet window

    Methods:
    on_draw -- Assigned to the window as a draw function
    update -- Calls the update functions for all game objects.
    change_alien_direction -- Makes all aliens swap strafe direction.
    lurch_aliens_forward -- Makes all aliens jump forward.
    spawn_alien_row -- Spawns a new row of aliens at the top of the screen.
    game_over -- Sets the game over text based on a boolean argument.
    """

    seconds_till_lurch = 5
    aliens_per_row = 5

    def __init__(self):
        """
        This sets everything up. Factoid: Init is short for 'initialise'.

        We call up to pyglets Window init to do the heavy lifting, and we
        give it a caption for the window title.
        """
        # Create pyglet window - the caption is the window title
        self.window = pyglet.window.Window(
            caption="Invaders From Space!",
            width=640,
            height=480)

        # Game over label. We also use it as a flag for when
        # the game is finished.
        self.game_over_label = None

        #  Add the alien and laser lists
        self.aliens = []
        self.lasers = []

        # Start the game with three loads of aliens
        self.lurch_aliens_forward()
        self.lurch_aliens_forward()
        self.lurch_aliens_forward()

        # We add two timed functions here to control the flow of aliens
        pyglet.clock.schedule_interval(
            self.change_alien_direction,
            self.seconds_till_lurch)
        pyglet.clock.schedule_interval(
            self.lurch_aliens_forward,
            self.seconds_till_lurch)

        # Add the player and bullet tracker
        from objects import Player
        self.player = Player(window=self)
        self.bullets = []
        # And let the window know to send keyboard events to the Player's
        # key_handler object.
        self.window.push_handlers(self.player.key_handler)

    def on_draw(self):
        """
        Overrides Window.on_draw, and draws all our sprites to the screen.

        Draw order is:
        1. Player
        2. Bullets
        3. Aliens
        4. Lasers

        Things drawn later go on top of things drawn earlier.
        """
        # First off we wipe the slate clean.
        self.window.clear()

        # Then we draw our tank
        self.player.draw()

        # Now we go through the bullets, aliens and lasers and draw them
        for drawable in chain(self.bullets, self.aliens, self.lasers):
            drawable.draw()

        # Lastly we draw the game over text on the screen if it has been set
        if self.game_over_label is not None:
            self.game_over_label.draw()

    def update(self, elapsed_time):
        """
        Perform frame-rate independent updates of game objects.

        This method just tells each game object to update themselves, Then it
        checks for collions, removes destroyed objects and tests for Player
        victory.

        Arguments:
        elapsed_time -- Time in seconds since the last update.
        """

        # First off we make sure the player gets updated.
        self.player.update(elapsed_time=elapsed_time)

        # Update all the bullets...
        for bullet in self.bullets:
            bullet.update(elapsed_time=elapsed_time)
            # .. and now check for collisions
            for alien in self.aliens:
                if bullet.has_hit(alien):
                    bullet.destroy()
                    alien.explode()

        # Update all the lasers...
        for laser in self.lasers:
            laser.update(elapsed_time=elapsed_time)
            # and check for collisions there too!
            if laser.has_hit(self.player):
                laser.destroy()
                self.player.explode()
                self.game_over(you_won=False)

        # Remove bullets that have gone off the screen or have
        # been marked as 'destroyed'. This kind of line here is
        # a 'list comprehension'. They are really nifty.
        self.bullets = [
            b for b in self.bullets if b.sprite.y < self.window.height
            and not b.destroyed]

        # Remove the aliens that are destroyed, like above, with
        # another list comprehension
        self.aliens = [a for a in self.aliens if not a.destroyed]

        # Remove lasers that have gone off the screen.
        self.lasers = [
            l for l in self.lasers if l.sprite.y > 0
            and not l.destroyed]

        # Make the aliens fire! Maybe. It's a bit random.
        for alien in self.aliens:
            alien.fire()

        # Do the end game victory check
        if len(self.aliens) == 0:
            self.game_over(you_won=True)

    def change_alien_direction(self, elapsed_time=None):
        """
        Make aliens strafe in a different direction.

        Simply sets each aliens head_right variable to the opposite
        value.

        Arguments:
        elapsed_time -- Ignored. Required by pyglet clock.
        """
        for alien in self.aliens:
            alien.head_right = not alien.head_right

    def lurch_aliens_forward(self, elapsed_time=None):
        """
        Make aliens lurch forward.

        Simply calls each aliens lurch function, checking for the
        return value of false that means the Alien has won. If it
        finds it, it calls game_over.

        After each lurch, it spawns a new row of aliens.

        Arguments:
        elapsed_time -- Ignored, required by pyglet's clock.
        """
        if self.game_over_label is None:
            for alien in self.aliens:
                if not alien.lurch():
                    # lurch() returns false if the alien has reached you!
                    # This is a nice way of checking that.
                    self.game_over(you_won=False)

            # After all the aliens have moved forward, we add a new row in
            self.spawn_alien_row()

    def spawn_alien_row(
            self,
            elapsed_time=None,
            number_of_aliens=None):
        """
        Make a row of aliens at the top of the screen.

        Does some rather hacky spacing calculations to determine
        Alien x coordinates.

        Arguments:
        elapsed_time -- Ignored, required by pyglet's clock.
        number_of_aliens -- How many aliens do we want?
        """
        from objects import Alien
        # Check if we should use the default number of aliens
        if not number_of_aliens:
            number_of_aliens = self.aliens_per_row

        # This maths figures out how much space we need to leave for
        # the aliens to strafe across the whole screen.
        number_of_strafes = self.seconds_till_lurch / Alien.strafe_delay
        strafe_distance = number_of_strafes * Alien.strafe_step
        rightmost_start = self.window.width - strafe_distance

        # Now we figure out if we can fit the number of aliens requested
        # into that space. If we can't, we try with one less, then two less...
        spacing = None

        while not spacing:
            space_per_alien = rightmost_start / number_of_aliens
            if space_per_alien < Alien.image.width:
                # Won't fit! Try one less!
                number_of_aliens -= 1
            else:
                # Great! Let's make these aliens!
                spacing = space_per_alien

        # Add some new aliens to the list.
        self.aliens += [
            Alien(window=self, x_pos=(spacing*number + Alien.strafe_step))
            for number in range(number_of_aliens)]

    def game_over(self, you_won=False):
        """
        Game over! Set the game_over_label.

        The text is determined by the boolean you_won argument.

        Arguments:
        you_won -- True for a player win, false for an Alien victory.
        """
        if you_won:
            text = "You Win!"
        else:
            text = "Game Over"

        self.game_over_label = pyglet.text.Label(
            text,
            font_size=30,
            anchor_x="center",
            x=self.window.width / 2,
            y=self.window.height / 2)

        pyglet.clock.unschedule(self.update)
        pyglet.clock.unschedule(self.change_alien_direction)
        pyglet.clock.unschedule(self.lurch_aliens_forward)
        for alien in self.aliens:
            pyglet.clock.unschedule(alien.strafe)
class InvadersWindow(pyglet.window.Window):
    """
    This class does all managing: it draws to the screen, and
    updates all the bits and pieces flying around the screen!

    Extends pyglet.window.Window, overwriting the on_draw method.
    """

    def __init__(self):
        """
        This sets everything up. Factoid: Init is short for 'initialise'.

        We call up to pyglets Window init to do the heavy lifting,
        specifying a width, height and caption (title).
        """
        # Create pyglet window - the caption is the window title
        pyglet.window.Window.__init__(
            self,
            caption="Invaders From Space!",
            width=640,
            height=480)

        # A list of bullets!
        self.bullets = []

        from objects import Player
        # Our fearless tank, with a reference to ourselves being passed in.
        self.player = Player(window=self)
        # We make sure that the keyboard events are sent to the key handler.
        self.push_handlers(self.player.key_handler)

    def on_draw(self):
        """
        Overrides Window.on_draw.
        """
        # First off we wipe the slate clean.
        self.clear()

        # We draw our player
        self.player.draw()

        # We draw our bullets
        for bullet in self.bullets:
            bullet.draw()

    def update(self, elapsed_time):
        """
        Perform frame-rate indepent updates of game objects.
        """
        # Update the player
        self.player.update(elapsed_time=elapsed_time)

        # We update all our bullets!
        for bullet in self.bullets:
            bullet.update(elapsed_time=elapsed_time)
            # Check if it has gone off the screen
            if bullet.sprite.y > self.height:
                # It has, so destroy it.
                bullet.destroy()

        # Keep only the bullets that aren't 'destroyed'
        # This is a super cool and useful Python feature
        # called a 'list comprehension'. Make a list from a loop!
        self.bullets = [b for b in self.bullets if not b.destroyed]