Python Project Practice - Alien Invasion
Objective: Develop a game called 'Alien Invasion' using Pygame.
GitHub repository: https://github.com/Angelia-Wang/alien_invasion
Chapter 12: Arm the Spaceship
First, install the Pygame library:
pip install pygame
If that fails, update pip:
conda update pip
Create a folder named alien_invasion for your development work.
You can use Git for project management.
Git and GitHub usage guide How to use Git and GitHub with Pycharm for pull request process
12.1 Start the Game Project
We first need to create a Pygame window and respond to user input.
12.1.1 Functions Involved
The modules imported are Pygame and sys.
pygame.init()initializes the background settings so that Pygame can work properly.pygame.display.set_mode((x,y))creates a display window. The parameter is a tuple specifying the size of the game window in pixels. The return value is a surface representing the entire game window.pygame.display.set_caption(string)sets the title of the game window.fill((255,255,255),[rect])fills the specified surface's rect area with a certain color. The first parameter is the color, such as red (255,0,0); green (0,255,0); blue (0,0,255); same numbers represent gray, and the number determines the shade. The second parameter is a rect object, which can be omitted, omitting it means filling the entire surface with the specified color.pygame.display.flip()tells Pygame to make the most recently drawn screen visible.pygame.event.get()gets a list of keyboard and mouse events (all events that occurred after the last call).
Event is an action performed by the user while playing the game, such as pressing a key or moving the mouse.
type()method of the event gets the type of the event.pygame.QUITdetects this event when the player clicks the close button of the game window.sys.exit()exits the game.
12.1.2 Analysis
After understanding the functions that can be called, what we need to do in this step is:
- Create a game window (set basic data such as screen color, screen size)
- For game settings information, you can create a settings class
- Get user input at all times and respond to it
- Since input needs to be detected at all times, there must be a loop, and within the loop, input is obtained and responded to
- Getting input - listen for events
- Responding - update the screen after processing the event
12.1.3 Code
settings.py
class Settings:
"""Class to store all settings for the game 'Alien Invasion'"""
def __init__(self):
"""Initialize game settings"""
# Screen settings
self.screen_width = 1200 # Screen width in pixels
self.screen_height = 800 # Screen height in pixels
self.bg_color = (230, 230, 230) # Background color
alien_invasion.py
import pygame
import sys
from settings import Settings
class AlienInvasion:
"""Class to initialize game resources and behavior"""
def __init__(self):
"""Initialize the game and create game resources"""
pygame.init() # Initialize Pygame background settings
self.settings = Settings()
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))
pygame.display.set_caption("Alien Invasion")
def run_game(self):
"""Start the main game loop"""
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
self.screen.fill(self.settings.bg_color)
pygame.display.flip()
if __name__ == "__main__":
ai = AlienInvasion()
ai.run_game()
12.2 Add Ship Image
We have already created a Pygame window and can respond to user input. Now we need to give the user an object to manipulate for the game - the ship.
12.2.1 Functions Involved
pygame.image.load(string)loads the ship image. The parameter is the image address, returning the position of the image's surface.
Pygame defaults to loading bitmaps (.bmp), most images are .jpg, .png, .gif formats, which can be converted to bitmaps using tools like Photoshop.
get_rect()gets the rect attribute of the corresponding surface.
Pygame is efficient because it allows you to handle all game elements as rectangles (rect objects), even if their shape is not rectangular. When handling rect objects, you can set the x and y coordinates of the four corners and center to specify the position of the game element.
- To center the game element: set the attributes center, centerx, centery of the corresponding rect object
- To align the game element with the edges of the screen: use the attributes top, bottom, left, right or midtop, midbottom, midleft, midright
- To adjust the horizontal or vertical position of the game element, use the attributes x and y, which are the x and y coordinates of the upper-left corner of the rectangle.
The x and y attributes of rect can only store integer values. In Pygame, the origin (0, 0) is at the top-left corner of the screen, and the coordinate values increase as you move to the bottom-right.
blit(surface object, rect object)draws the surface object from the caller onto the specified location of the rect object.
self.screen.blit(self.image, self.rect) # Example code
12.2.2 Analysis
There are two steps involved:
- Create a ship object (involving the ship image and its position on the screen)
- Add the ship to the screen
12.2.3 Code
We first create an images folder in the current alien_invasion folder and place the ship image ship.bmp inside it.
ship.py
import pygame
class Ship:
"""Class to manage the ship"""
def __init__(self, ai_game):
"""Initialize the ship and set its initial position"""
self.screen = ai_game.screen
self.screen_rect = ai_game.screen.get_rect()
# Load the ship image and get its outer rectangle
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
# Place each new ship at the bottom center of the screen
self.rect.midbottom = self.screen_rect.midbottom
def blitme(self):
"""Draw the ship at the specified location"""
self.screen.blit(self.image, self.rect)
alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
class AlienInvasion:
"""Class to initialize game resources and behavior"""
def __init__(self):
"""Initialize the game and create game resources"""
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))
self.ship = Ship(self)
pygame.display.set_caption("Alien Invasion")
def run_game(self):
"""Start the main game loop"""
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
pygame.display.flip()
if __name__ == '__main__':
ai = AlienInvasion()
ai.run_game()
12.3 Refactor run_game() Method
Refactoring is common in large projects to simplify existing code structure for easier expansion.
Refactoring a method involves splitting it into multiple helper methods. Helper methods perform tasks within the class, are not called through instances, and are prefixed with a single underscore.
12.3.1 Analysis
Currently, the code we have written has a run_game() method where, as the response to user key presses and mouse events becomes more detailed, the code will become longer. Therefore, we need to refactor this method.
At present, the work in the run_game() method can be divided into two parts: listening to user events and redrawing the screen. So we can split the run_game() method into two helper methods:
- _check_events() for event management
- _update_screen() for updating the screen
12.3.2 Code
alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
class AlienInvasion:
"""Class to initialize game resources and behavior"""
def __init__(self):
"""Initialize the game and create game resources"""
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))
self.ship = Ship(self)
pygame.display.set_caption("Alien Invasion")
def run_game(self):
"""Start the main game loop"""
while True:
self._check_events()
self._update_screen()
def _check_events(self):
"""Respond to key and mouse events"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
def _update_screen(self):
"""Update the images on the screen and switch to the new screen"""
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
pygame.display.flip()
if __name__ == '__main__':
ai = AlienInvasion()
ai.run_game()
12.4 Drive the Ship
We now have a ship, and the next step is to make the ship respond to user operations (left and right movement, but not leaving the screen).
We also want to: increase the ship's speed; press q to exit the game; display the game in full screen.
12.4.1 Functions Involved
pygame.KEYDOWNeach key press is registered as a KEYDOWN event.pygame.FULLSCREEparameter, full screen parameter.
pygame.display.set_mode((0, 0), pygame.FULLSCREE) generates a screen that covers the entire monitor
12.4.2 Analysis
- Pressing left and right keys to control the ship's movement, so we need to listen for keyboard events, and when detected, change the x coordinate of the ship's rect object
- To make the ship move continuously, just listening for the KEYDOWN event is not enough, we need to record the state between the key being pressed and released. So we define two properties in the Ship class, moving_right and moving_left, to determine whether to move left or right. Based on these two True or False values, update the ship's position.
- Before updating the position, we need to check whether the current position will exceed the screen, and only update the position if it won't.
- To increase the ship's speed, we need to give the ship a speed variable for easy modification. If the ship's movement unit distance (speed) is a decimal, and the rect object's x etc. attributes can only store integers, then we should use float calculations for accuracy, and finally assign the value to the rect object.
- Pressing q to exit the game; full screen display functions are relatively simple, so they are not described here.
- It can be anticipated that the _check_events() method will become increasingly complex due to responding to different key presses, so we will decompose it into two helper methods:
- _check_keydown_events() handles key press events
- _check_keyup_events() handles key release events
12.4.3 Code
ship.py
import pygame
from settings import Settings
class Ship:
"""Class to manage the ship"""
def __init__(self, ai_game):
"""Initialize the ship and set its initial position"""
self.screen = ai_game.screen
self.settings = ai_game.settings
self.screen_rect = ai_game.screen.get_rect()
# Load the ship image and get its outer rectangle
self.image = pygame.image.load('images/ship.bmp')
self.rect = self.image.get_rect()
# Place each new ship at the bottom center of the screen
self.rect.midbottom = self.screen_rect.midbottom
self.x = float(self.rect.x)
# Movement flag
self.moving_right = False
self.moving_left = False
def blitme(self):
"""Draw the ship at the specified location"""
self.screen.blit(self.image, self.rect)
def update(self):
if self.moving_right and self.rect.right < self.screen_rect.right:
self.x += self.settings.ship_speed
elif self.moving_left and self.rect.left > 0:
self.x -= self.settings.ship_speed
self.rect.x = self.x
setting.py
class Settings:
"""Class to store all settings for the game 'Alien Invasion'"""
def __init__(self):
"""Initialize game settings"""
# Screen settings
self.screen_width = 1200 # Screen width in pixels
self.screen_height = 800 # Screen height in pixels
self.bg_color = (230, 230, 230) # Background color
# Ship settings
self.ship_speed = 1.5
alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
class AlienInvasion:
"""Class to initialize game resources and behavior"""
def __init__(self):
"""Initialize the game and create game resources"""
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
self.settings.screen_width = self.screen.get_rect().width
self.settings.screen_height = self.screen.get_rect().height
self.ship = Ship(self)
pygame.display.set_caption("Alien Invasion")
def run_game(self):
"""Start the main game loop"""
while True:
self._check_events()
self.ship.update()
self._update_screen()
def _check_events(self):
"""Respond to key and mouse events"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
def _check_keydown_events(self, event):
"""Respond to key presses"""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_q:
sys.exit()
def _check_keyup_events(self, event):
"""Respond to key releases"""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _update_screen(self):
"""Update the images on the screen and switch to the new screen"""
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
pygame.display.flip()
if __name__ == '__main__':
ai = AlienInvasion()
ai.run_game()
12.5 Shooting
The user can now control the ship to move left and right, and the next step is to design the ship's ability to fire bullets.
12.5.1 Functions Involved
pygame.Rect(x,y,width,height)creates a rectangle, with parameters 1-4 representing the x and y coordinates of the upper-left corner of the rectangle and its width and height.pygame.draw.rect(surface, color, rect)fills the surface part occupied by the rect with the color.
# Two ways have the same effect, both fill the color in the rect area of the screen
self.screen.fill(self.button_color, self.rect)
pygame.draw.rect(self.screen, self.button_color, self.rect)
pygame.sprite.Group()creates an instance of the pygame.sprite.Group class. It is a group, similar to a list, but provides additional features helpful for developing games.- When calling the
update()method on a group, the group automatically calls theupdate()method for each sprite in the group.
We can write a subclass of the Sprite class, override the update() method, so that we can group related elements in the game and operate on all elements in the group simultaneously through update. Introducing the Sprite class: from pygame.sprite import Sprite
- Call the
add(sprite)method on the group to add a sprite to the group. This method is similar to append(), but is specifically written for Pygame groups. - Call the
remove(sprite)method on the group to remove a sprite from the group. - Call the
sprites()method on the group to return a list containing all the sprites in the group.
12.5.2 Analysis
We want the user to be able to fire bullets by pressing spacebar, and to limit the maximum number of bullets that can be fired.
Key technical points are as follows:
- Create a bullet class - since the user can fire multiple bullets and the bullets need to keep moving (i.e., the position of the bullet changes every moment), we can consider writing a Bullet class that inherits from the Sprite class, so that by instantiating the pygame.sprite.Group class, we can get a bullet group, which can be added or removed using the add() and remove() methods. We can also use the update() method to manage the changes of all bullets in the group.
- When the user presses spacebar, if the number of bullets in the group is less than the maximum number of bullets that can be fired, then add()
- Once the bullet reaches the top of the screen, it disappears, and we need to remove these invalid bullets from the group, otherwise they will continue to consume memory and CPU processing power. This requires traversing all bullets in the group and checking if the rect.buttom position of the bullet is <= 0, and then removing it.
- Bullets need to keep moving. So we can rewrite the update() method in the Bullet class to change the position of the bullet. Before redrawing the screen each time, call the update() method on the bullet group, and the group will automatically call the update() method for each bullet in the group.
- The bullet is not an image, it is a rectangle created with pygame.Rect(), and to draw it on the screen, we need to call pygame.draw.rect().
So the general process is:
- Set various settings for bullets in the Settings class, including bullet color, width, height, and maximum number of bullets that can be fired
- Write a bullet class, which should create a rectangle attribute for the bullet, inherit from the Sprite class, and override the update() method. Write a method to draw the bullet on the screen.
- In the init() method of the AlienInvasion class, create a bullet group, and in the run_game() method, listen for the user pressing the spacebar event to create bullets, continuously update the bullet position, and finally draw the screen.
12.5.3 Code
setting.py
class Settings:
"""Class to store all settings for the game 'Alien Invasion'"""
def __init__(self):
"""Initialize game settings"""
# Screen settings
self.screen_width = 1200 # Screen width in pixels
self.screen_height = 800 # Screen height in pixels
self.bg_color = (230, 230, 230) # Background color
# Ship settings
self.ship_speed = 1.5
# Bullet settings
self.bullet_speed = 1.0
self.bullet_with = 3
self.bullet_height = 15
self.bullet_color = (60, 60, 60)
self.bullet_allowed = 3 # Maximum number of bullets
bullet.py
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
"""Class to manage the bullets fired by the ship"""
def __init__(self, ai_game):
"""Create a bullet object at the ship's current position"""
super().__init__()
self.screen = ai_game.screen
self.settings = ai_game.settings
self.color = self.settings.bullet_color
# Create a rectangle representing the bullet at (0,0), then set the correct position
self.rect = pygame.Rect(0, 0, self.settings.bullet_with, self.settings.bullet_height)
self.rect.midtop = ai_game.ship.rect.midtop
# Store the bullet's position as a float
self.y = float(self.rect.y)
def update(self):
"""Move the bullet upward"""
# Update the small value representing the bullet's position
self.y -= self.settings.bullet_speed
self.rect.y = self.y
def draw_bullet(self):
"""Draw the bullet on the screen"""
pygame.draw.rect(self.screen, self.color, self.rect)
alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
from bullet import Bullet
class AlienInvasion:
"""Class to initialize game resources and behavior"""
def __init__(self):
"""Initialize the game and create game resources"""
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))
self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
pygame.display.set_caption("Alien Invasion")
def run_game(self):
"""Start the main game loop"""
while True:
self._check_events()
self.ship.update()
self._update_bullets()
self._update_screen()
def _check_events(self):
"""Respond to key and mouse events"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
def _check_keydown_events(self, event):
"""Respond to key presses"""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_SPACE:
self._fire_bullet()
elif event.key == pygame.K_q:
sys.exit()
def _fire_bullet(self):
"""Create a bullet and add it to the bullets group"""
if len(self.bullets) < self.settings.bullet_allowed:
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def _update_bullets(self):
"""Update the bullet positions and remove the ones that have disappeared"""
self.bullets.update()
# Remove the bullets that have disappeared
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
def _check_keyup_events(self, event):
"""Respond to key releases"""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
elif event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _update_screen(self):
"""Update the images on the screen and switch to the new screen"""
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
pygame.display.flip()
if __name__ == '__main__':
ai = AlienInvasion()
ai.run_game()
When looping through a list (or a Python group), Python requires the length of the list to remain unchanged throughout the loop. So you cannott delete elements from the list/group during the loop. If you need to delete, loop through a copy of the list/group. As shown in the example: for bullet in self.bullets.copy():
Chapter 13: Aliens Are Coming
When developing large projects, it is important to review the development plan before entering each development phase to clarify what tasks need to be completed by writing code.
This chapter covers:
- Review existing code to determine if refactoring is needed before implementing new features.
- Add an alien to the top-left corner of the screen with appropriate margins.
- Calculate how many aliens can fit on the screen based on the first alien's margin and screen size. Write a loop to create a series of aliens to fill the top half of the screen.
- Make the alien group move left and right and down until all aliens are shot, or an alien collides with the ship or reaches the bottom of the screen.
- If all alien are shot, create a new group of aliens.
- If an alien collides with the ship or reaches the bottom of the screen, destroy the ship and create a new group of aliens.
- Limit the number of ships available to the player. The game ends when the player runs out of ships.
We will improve this plan while implementing the features.
Before adding new features to the project, it is also important to review existing code. Clean up any messy or inefficient code, and refactor if necessary.
13.1 Create a Group of Aliens
Add an alien bitmap file alien.bmp to the images folder. The final goal is shown in the figure:
12.1.1 Functions Involved
- Calling the
draw(surface)method on a group draws each element of the group to the surface, with the specific position determined by the element's rect attribute.
13.1.2 Analysis
- Write an alien class
- Calculate the position of the alien on the screen, we need to reserve the distance of the first row of aliens from the screen, and the distance of the last row of aliens from the ship, as well as the distance of the left and right columns of aliens from the screen.
13.1.3 Code
alien.py
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):
"""Class to represent a single alien"""
def __init__(self, ai_game):
"""Initialize the alien and set its initial position"""
super().__init__()
self.screen = ai_game.screen
# Load the alien image and set its rect attribute
self.image = pygame.image.load("images/alien.bmp")
self.rect = self.image.get_rect()
# Each alien initially appears near the top-left corner of the screen
self.rect.x = self.rect.width
self.rect.y = self.rect.height
# Store the exact position of the alien
self.x = float(self.rect.x)
self.y = float(self.rect.y)
alien_invasion.py
import sys
import pygame
from settings import Settings
from ship import Ship
from bullet import Bullet
from alien import Alien
class AlienInvasion:
"""Class to initialize game resources and behavior"""
def __init__(self):
"""Initialize the game and create game resources"""
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))
self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
self.aliens = pygame.sprite.Group()
self._create_fleet()
pygame.display.set_caption("Alien Invasion")
def run_game(self):
"""Start the main game loop"""
while True:
self._check_events()
self.ship.update()
self._update_bullets()
self._update_screen()
def _check_events(self):
"""Respond to key and mouse events"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
def _check_keydown_events(self, event):
"""Respond to key presses"""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
elif event.key == pygame.K_LEFT:
self.ship.moving_left = True
elif event.key == pygame.K_SPACE:
self._fire_bullet()
elif event.key == pygame.K_q:
sys.exit()
def _create_fleet(self):
"""Create a fleet of aliens"""
# Create the first alien and calculate how many aliens can fit in a row
alien = Alien(self)
alien_width = alien.rect.width
alien_height = alien.rect.height
available_space_x = self.settings.screen_width - alien_width
available_space_y = self.settings.screen_height - 3 * alien_height - self.ship.rect.height
num_aliens_x = available_space_x // (2 * alien_width)
num_aliens_rows = available_space_y // (2 * alien_height)
# Create the fleet of aliens
for row_number in range(num_aliens_rows):
for alien_number in range(num_aliens_x):
self._create_alien(alien_number, row_number, alien_width, alien_height)
def _create_alien(self, alien_number, row_number, alien_width, alien_height):
"""Create a group of aliens"""
alien = Alien(self)
alien.x = alien_width + alien_number * alien_width * 2
alien.y = alien_height + row_number * alien_height * 2
alien.rect.x = alien.x
alien.rect.y = alien.y
self.aliens.add(alien)
def _fire_bullet(self):
"""Create a bullet and add it to the bullets group"""
if len(self.bullets) < self.settings.bullet_allowed:
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def _update_bullets(self):
"""Update the bullet positions and remove the ones that have disappeared"""
self.bullets.update()
# Remove the bullets that have
- #
ndsystem
# # # # n
n
. n
# # n
- n
n
n
n
n
n
n
n
n. n
n
n
n
n = n
n
n
n n
n
n
n
n
n
n n
n
n
n l
n
n
n
n
n
n
n
n
n
n s
n
n = n
n. n n
n
n
n
n
n
n
n
n
n: n
n
n
n
n. n
n \n
n
n
n
n
n n
n.n. n
n
. n
n
. b
n
.n
n
n
n.n
f
. n
- I
n n
n
k
n
n
i
n
n. n
n
n
n
n
n
n e
n. n
n
n
n
n
n,n. n n. n
n. n
n. n. n.n
n. n. n
n. n. n
n
n
n
n
n\n. n: n.n
n
n
. n n
n
n
n
n n n n
v
n
n
n. n
n n
n
n
n
n
n n
n
n
n
n. n n. n
``` n n
n n
n
n
. n. n g n n
n m
n
n
. n
n. n
e n ``n. n
n n w
n
n
. n
n :n
n c n n.
n
n
n. n n
n -n j
n
n. n. n l n n n. n
n:n. n.n n.n.g
n n. n
n.n n n n
r n
n
n
n
n
n
n
n
n n
n
n
n
n n
n
n
n
n
n n n
)n
n
n
n
n
.n. n
a
n
n a
. n
u
. n
n n
n
n n n n
n n
n
n
n
n
n
n
n
. n n
n
. n
n
. n t
n
n
n
n n n n n n
n
n n n n a n
n
n
n
p
n
a
n
n
n
n
n
n n
n
n
n
n e
n= n n
n
n
n
n
n
. n
n
n
n n
. n k
n n
n n: n
x
n
a n a
n
= n
n
n n n
n
n
n
n: n
n l n
n
n e
n
e
n
n
l
n
n
n n n
n
n
n
n
n
n
n
b
. n
n
e
n
n
n
n
n:
n
sample
n.n n
n
n
n. n
n n
n
function
n
n
n n
n
n
n n
n n
n n
n
n: n n
n
d n) n
n n
n
n
n
n
n k
n n
n
n
n
n k
n
n
n
n
n
n l
n
. n
n n
n
i
n n n n
l
l l
e
n
n e
n{ n
n
n
a n
n
n
n
n
n n n
. n n l
n
n
n
n a
n n: n
e n n n n
n
n
n "n n n n l n n n n
ncode
n n
n n n n n: n n n n n
l n n n n n n n
n
def @ h
l
n n n
code n
n
n
n n a n n n$n n
n
n
code
n
n n n. n
n n n n
string:
n
n
n n n
n
n n n n v
n
n
fffff n self
n:
n n
n
n n n o
n
n n n
l
n
n k
k
n
n n
n
e
n
n n a
n
n n
n => n
e n
n
l n
n n l e
v
n n
n
n
n k
n
f
n l
k n
. n
n
. code
l e l e k n self n a k
n n n
n l n n
n n k: k
this n n
n a
l n
n n l n e n
k n a
n n n k
n
ile
e n
n n n k
n
n
l k
n
l
n n
n
n
n
n
n
n k n
:
l
s
:n n
l
k
n k
n
n e n
n
n
n
code
n n a
n
l
n
a
l k
a n
name n
l l n n
n k
n
n n
n
n
i
n
n
l l
n n
code n
n
n k n e
l l l n
n
n
n
k n n n
n
e
s
: l
s " n n
n
n n
="n
n n
l
a n n
n
n l
l l
n
n s k
n n e
s n
l k
n n n
n
n
n k
n
n
n n: n text
n: code:
n
n) n n
k) n)n:
n.
.n n
code n n n
n n
k k
n
n n
: n n
n
n l
n
n
n
n
l n
: code
n
)
n
code
n
n
n n
n n n n
n l a n
f
n
n
s
n n n
e
n "n:\n n a l n n file n
n l e
n e n n k
text: n content
e
l l
, n a
n code n
n n, n
:
n
n n n n n n
code code n n+code
n code def n
n
n:
code n
e
e
n n
n:
k
n
n
n
l
code
n
code code code
: n
code n e code
n code
c
k e
code n
code
code code
code code n code
n
code codee
:=