@ -1,3 +1,4 @@
from __future__ import annotations
import sys
import sys
from time import sleep
from time import sleep
import pygame
import pygame
@ -9,12 +10,13 @@ from ship import Ship
from bullet import Bullet
from bullet import Bullet
from alien import Alien
from alien import Alien
from pygame . sprite import Group
from pygame . sprite import Group
from typing import Tuple
class AlienInvasion :
class AlienInvasion :
""" Overall class to manage game assets and behavior. """
""" Overall class to manage game assets and behavior. """
def __init__ ( self ) :
def __init__ ( self ) - > None :
pygame . init ( )
pygame . init ( )
self . clock = pygame . time . Clock ( )
self . clock = pygame . time . Clock ( )
self . settings = Settings ( )
self . settings = Settings ( )
@ -26,13 +28,13 @@ class AlienInvasion:
self . stats = GameStats ( self )
self . stats = GameStats ( self )
self . score_board = ScoreBoard ( self )
self . score_board = ScoreBoard ( self )
self . ship = Ship ( self )
self . ship = Ship ( self )
self . bullets = Group ( )
self . bullets : Group = Group ( ) # type: ignore
self . aliens = Group ( )
self . aliens : Group = Group ( ) # type: ignore
self . _create_fleet ( )
self . _create_fleet ( )
self . game_active = False
self . game_active = False
self . play_button = Button ( self , " Play " )
self . play_button = Button ( self , " Play " )
def run_game ( self ) :
def run_game ( self ) - > None :
""" Start the main loop for the game. """
""" Start the main loop for the game. """
while True :
while True :
# Check for events and update the game state.
# Check for events and update the game state.
@ -46,7 +48,7 @@ class AlienInvasion:
self . _update_screen ( )
self . _update_screen ( )
self . clock . tick ( 60 )
self . clock . tick ( 60 )
def _create_fleet ( self ) :
def _create_fleet ( self ) - > None :
""" Create a fleet of aliens. """
""" Create a fleet of aliens. """
alien = Alien ( self )
alien = Alien ( self )
alien_width , alien_height = alien . rect . size
alien_width , alien_height = alien . rect . size
@ -58,28 +60,28 @@ class AlienInvasion:
current_x = alien_width
current_x = alien_width
current_y + = 2 * alien_height
current_y + = 2 * alien_height
def _create_alien ( self , x_position , y_position ):
def _create_alien ( self , x_position : int , y_position : int ) - > None :
""" Create an alien and place it in the fleet. """
""" Create an alien and place it in the fleet. """
alien = Alien ( self )
alien = Alien ( self )
alien . x = x_position
alien . x = x_position
alien . rect . x = x_position
alien . rect . x = x_position
alien . rect . y = y_position
alien . rect . y = y_position
self . aliens . add ( alien )
self . aliens . add ( alien ) # type: ignore
def _check_fleet_edges ( self ) :
def _check_fleet_edges ( self ) - > None :
""" Respond appropriately if any aliens have reached an edge. """
""" Respond appropriately if any aliens have reached an edge. """
for alien in self . aliens . sprites ( ) :
for alien in self . aliens . sprites ( ) : # type: ignore
if alien . check_edges ( ) :
if alien . check_edges ( ) : # type: ignore
self . _change_fleet_direction ( )
self . _change_fleet_direction ( )
break
break
def _change_fleet_direction ( self ) :
def _change_fleet_direction ( self ) - > None :
""" Drop the entire fleet and change its direction. """
""" Drop the entire fleet and change its direction. """
for alien in self . aliens . sprites ( ) :
for alien in self . aliens . sprites ( ) : # type: ignore
alien . rect . y + = self . settings . fleet_drop_speed
alien . rect . y + = self . settings . fleet_drop_speed # type: ignore
self . settings . fleet_direction * = - 1
self . settings . fleet_direction * = - 1
def _check_events ( self ) :
def _check_events ( self ) - > None :
""" Respond to keypresses and mouse events. """
""" Respond to keypresses and mouse events. """
for event in pygame . event . get ( ) :
for event in pygame . event . get ( ) :
if event . type == pygame . QUIT :
if event . type == pygame . QUIT :
@ -92,7 +94,7 @@ class AlienInvasion:
mouse_pos = pygame . mouse . get_pos ( )
mouse_pos = pygame . mouse . get_pos ( )
self . _check_play_button ( mouse_pos )
self . _check_play_button ( mouse_pos )
def _check_keydown_events ( self , event ):
def _check_keydown_events ( self , event : pygame . event . Event ) - > None :
""" Respond to key presses. """
""" Respond to key presses. """
if event . key == pygame . K_RIGHT :
if event . key == pygame . K_RIGHT :
self . ship . moving_right = True
self . ship . moving_right = True
@ -103,14 +105,14 @@ class AlienInvasion:
elif event . key == pygame . K_SPACE :
elif event . key == pygame . K_SPACE :
self . _fire_bullet ( )
self . _fire_bullet ( )
def _check_keyup_events ( self , event ):
def _check_keyup_events ( self , event : pygame . event . Event ) - > None :
""" Respond to key releases. """
""" Respond to key releases. """
if event . key == pygame . K_RIGHT :
if event . key == pygame . K_RIGHT :
self . ship . moving_right = False
self . ship . moving_right = False
elif event . key == pygame . K_LEFT :
elif event . key == pygame . K_LEFT :
self . ship . moving_left = False
self . ship . moving_left = False
def _check_play_button ( self , mouse_pos ):
def _check_play_button ( self , mouse_pos : Tuple [ int , int ] ) - > None :
""" Start a new game when the player clicks Play. """
""" Start a new game when the player clicks Play. """
button_clicked = self . play_button . rect . collidepoint ( mouse_pos )
button_clicked = self . play_button . rect . collidepoint ( mouse_pos )
if button_clicked and not self . game_active :
if button_clicked and not self . game_active :
@ -120,62 +122,62 @@ class AlienInvasion:
self . score_board . prep_level ( )
self . score_board . prep_level ( )
self . score_board . prep_ships ( )
self . score_board . prep_ships ( )
self . game_active = True
self . game_active = True
self . bullets . empty ( )
self . bullets . empty ( ) # type: ignore
self . aliens . empty ( )
self . aliens . empty ( ) # type: ignore
self . _create_fleet ( )
self . _create_fleet ( )
self . ship . center_ship ( )
self . ship . center_ship ( )
pygame . mouse . set_visible ( False )
pygame . mouse . set_visible ( False )
def _fire_bullet ( self ) :
def _fire_bullet ( self ) - > None :
""" Fire a bullet if the limit is not reached. """
""" Fire a bullet if the limit is not reached. """
if len ( self . bullets ) < self . settings . bullets_allowed :
if len ( self . bullets ) < self . settings . bullets_allowed : # type: ignore
# Create a new bullet and add it to the bullets group.
# Create a new bullet and add it to the bullets group.
new_bullet = Bullet ( self )
new_bullet = Bullet ( self )
self . bullets . add ( new_bullet )
self . bullets . add ( new_bullet ) # type: ignore
def _update_bullets ( self ) :
def _update_bullets ( self ) - > None :
""" Update position of bullets and remove old bullets. """
""" Update position of bullets and remove old bullets. """
self . bullets . update ( )
self . bullets . update ( ) # type: ignore
for bullet in self . bullets . copy ( ) :
for bullet in self . bullets . copy ( ) : # type: ignore
if bullet . rect . bottom < = 0 :
if bullet . rect . bottom < = 0 : # type: ignore
self . bullets . remove ( bullet )
self . bullets . remove ( bullet ) # type: ignore
self . _check_bullet_alien_collisions ( )
self . _check_bullet_alien_collisions ( )
def _check_alien_bottom ( self ) :
def _check_alien_bottom ( self ) - > None :
""" Check if any aliens have reached the bottom of the screen. """
""" Check if any aliens have reached the bottom of the screen. """
for alien in self . aliens . sprites ( ) :
for alien in self . aliens . sprites ( ) : # type: ignore
if alien . rect . bottom > = self . settings . screen_height :
if alien . rect . bottom > = self . settings . screen_height : # type: ignore
self . _ship_hit ( )
self . _ship_hit ( )
break
break
def _check_bullet_alien_collisions ( self ) :
def _check_bullet_alien_collisions ( self ) - > None :
""" Respond to bullet-alien collisions. """
""" Respond to bullet-alien collisions. """
collisions = pygame . sprite . groupcollide ( self . bullets , self . aliens , True , True )
collisions = pygame . sprite . groupcollide ( self . bullets , self . aliens , True , True ) # type: ignore
if collisions :
if collisions :
for aliens in collisions . values ( ) :
for aliens in collisions . values ( ) : # type: ignore
self . stats . score + = self . settings . alien_points * len ( aliens )
self . stats . score + = self . settings . alien_points * len ( aliens ) # type: ignore
self . score_board . prep_score ( )
self . score_board . prep_score ( )
self . score_board . check_high_score ( )
self . score_board . check_high_score ( )
self . score_board . prep_level ( )
self . score_board . prep_level ( )
self . score_board . prep_ships ( )
self . score_board . prep_ships ( )
if not self . aliens :
if not self . aliens : # type: ignore
# If the fleet is empty, create a new fleet.
# If the fleet is empty, create a new fleet.
self . bullets . empty ( )
self . bullets . empty ( ) # type: ignore
self . _create_fleet ( )
self . _create_fleet ( )
self . settings . increase_speed ( )
self . settings . increase_speed ( )
self . stats . level + = 1
self . stats . level + = 1
self . score_board . prep_level ( )
self . score_board . prep_level ( )
def _ship_hit ( self ) :
def _ship_hit ( self ) - > None :
""" Respond to the ship being hit by an alien. """
""" Respond to the ship being hit by an alien. """
if self . stats . ships_left > 0 :
if self . stats . ships_left > 0 :
# Decrement ships_left and reset the game state.
# Decrement ships_left and reset the game state.
self . stats . ships_left - = 1
self . stats . ships_left - = 1
self . score_board . prep_ships ( )
self . score_board . prep_ships ( )
self . bullets . empty ( )
self . bullets . empty ( ) # type: ignore
self . aliens . empty ( )
self . aliens . empty ( ) # type: ignore
self . _create_fleet ( )
self . _create_fleet ( )
self . ship . center_ship ( )
self . ship . center_ship ( )
sleep ( 0.5 )
sleep ( 0.5 )
@ -183,22 +185,22 @@ class AlienInvasion:
self . game_active = False
self . game_active = False
pygame . mouse . set_visible ( True )
pygame . mouse . set_visible ( True )
def _update_aliens ( self ) :
def _update_aliens ( self ) - > None :
""" Update the positions of aliens. """
""" Update the positions of aliens. """
self . _check_fleet_edges ( )
self . _check_fleet_edges ( )
self . aliens . update ( )
self . aliens . update ( ) # type: ignore
if pygame . sprite . spritecollideany ( self . ship , self . aliens ) :
if pygame . sprite . spritecollideany ( self . ship , self . aliens ) : # type: ignore
self . _ship_hit ( )
self . _ship_hit ( )
self . _check_bullet_alien_collisions ( )
self . _check_bullet_alien_collisions ( )
self . _check_alien_bottom ( )
self . _check_alien_bottom ( )
def _update_screen ( self ) :
def _update_screen ( self ) - > None :
""" Update images on the screen and flip to the new screen. """
""" Update images on the screen and flip to the new screen. """
self . screen . fill ( self . bg_color )
self . screen . fill ( self . bg_color )
for bullet in self . bullets . sprites ( ) :
for bullet in self . bullets . sprites ( ) : # type: ignore
bullet . draw_bullet ( )
bullet . draw_bullet ( ) # type: ignore
self . ship . blitme ( )
self . ship . blitme ( )
self . aliens . draw ( self . screen )
self . aliens . draw ( self . screen ) # type: ignore
self . score_board . show_score ( )
self . score_board . show_score ( )
if not self . game_active :
if not self . game_active :
self . play_button . draw_button ( )
self . play_button . draw_button ( )