Python Pygame Tank Battle Game Implementation
This article presents a complete Python 3 + Pygame implementation of a classic tank battle game, detailing the visual layout, full source code with sprite classes for tanks, bullets, obstacles, and game logic, and explains how to run and customize the game.
In this tutorial the author, a nostalgic 80s gamer, shares a full Python 3 + Pygame implementation of a "Tank Battle" game, providing around 1000 lines of source code that can be run directly.
Display Effect
The article includes screenshots demonstrating the game's visual appearance, showing the tank sprites, battlefield layout, and in‑game action.
Code
The complete source code defines multiple sprite classes for the game elements, including walls, ice, river, trees, bricks, bullets, player tanks, enemy tanks, the home base, and the overall game controller. It also handles input, collision detection, bullet firing, enemy AI movement, and game state management.
import random
import sys
import pygame
# Screen dimensions
WIDTH = 630
HEIGHT = 630
# Border size
BORDER_LEN = 3
# Font path
FONTPATH = 'resources/font/font.ttf'
class Iron(pygame.sprite.Sprite):
"""Iron wall class"""
group = pygame.sprite.Group()
def __init__(self, position):
super().__init__()
self.image = pygame.image.load('resources/images/scene/iron.png')
self.rect = self.image.get_rect()
self.rect.topleft = position
self.group.add(self)
@classmethod
def show(cls, screen):
for temp in cls.group:
screen.blit(temp.image, temp.rect)
class Ice(pygame.sprite.Sprite):
"""Ice class"""
group = pygame.sprite.Group()
def __init__(self, position):
super().__init__()
image = pygame.Surface((24, 24))
for i in range(2):
for j in range(2):
image.blit(pygame.image.load('resources/images/scene/ice.png'), (12 * i, 12 * j))
self.image = image
self.rect = self.image.get_rect()
self.rect.topleft = position
self.group.add(self)
@classmethod
def show(cls, screen):
for temp in cls.group:
screen.blit(temp.image, temp.rect)
class River(pygame.sprite.Sprite):
"""River class"""
group = pygame.sprite.Group()
def __init__(self, position):
super().__init__()
image = pygame.Surface((24, 24))
for i in range(2):
for j in range(2):
image.blit(pygame.image.load('resources/images/scene/river1.png'), (12 * i, 12 * j))
self.image = image
self.rect = self.image.get_rect()
self.rect.topleft = position
self.group.add(self)
@classmethod
def show(cls, screen):
for temp in cls.group:
screen.blit(temp.image, temp.rect)
class Tree(pygame.sprite.Sprite):
"""Tree class"""
group = pygame.sprite.Group()
def __init__(self, position):
super().__init__()
image = pygame.Surface((24, 24))
for i in range(2):
for j in range(2):
image.blit(pygame.image.load('resources/images/scene/tree.png'), (12 * i, 12 * j))
self.image = image
self.rect = self.image.get_rect()
self.rect.topleft = position
self.group.add(self)
@classmethod
def show(cls, screen):
for temp in cls.group:
screen.blit(temp.image, temp.rect)
class Brick(pygame.sprite.Sprite):
"""Brick wall class"""
group = pygame.sprite.Group()
def __init__(self, position):
super().__init__()
self.image = pygame.image.load('resources/images/scene/brick.png')
self.rect = self.image.get_rect()
self.rect.topleft = position
self.group.add(self)
@classmethod
def show(cls, screen):
for temp in cls.group:
screen.blit(temp.image, temp.rect)
class Bullet(pygame.sprite.Sprite):
"""Bullet class"""
group = pygame.sprite.Group()
group_enemy = pygame.sprite.Group()
def __init__(self, _type, direction, position):
super().__init__()
if direction == "up":
image_path = "resources/images/bullet/bullet_up.png"
elif direction == "down":
image_path = "resources/images/bullet/bullet_down.png"
elif direction == "left":
image_path = "resources/images/bullet/bullet_left.png"
elif direction == "right":
image_path = "resources/images/bullet/bullet_right.png"
self.image = pygame.image.load(image_path)
self.rect = self.image.get_rect()
self.rect.center = position
self.direction = direction
self.speed = 8
if _type == "player":
self.group.add(self)
else:
self.group_enemy.add(self)
@classmethod
def auto_move(cls):
for temp in cls.group:
if temp.rect.x < BORDER_LEN or temp.rect.x > WIDTH - BORDER_LEN or temp.rect.y < BORDER_LEN or temp.rect.y > HEIGHT - BORDER_LEN:
cls.group.remove(temp)
print("子弹超出边界,移除子弹")
continue
if temp.direction == "up":
temp.rect = temp.rect.move((0, -temp.speed))
elif temp.direction == "down":
temp.rect = temp.rect.move((0, temp.speed))
elif temp.direction == "left":
temp.rect = temp.rect.move((-temp.speed, 0))
elif temp.direction == "right":
temp.rect = temp.rect.move((temp.speed, 0))
for temp in cls.group_enemy:
if temp.rect.x < BORDER_LEN or temp.rect.x > WIDTH - BORDER_LEN or temp.rect.y < BORDER_LEN or temp.rect.y > HEIGHT - BORDER_LEN:
cls.group_enemy.remove(temp)
print("子弹超出边界,移除子弹")
continue
if temp.direction == "up":
temp.rect = temp.rect.move((0, -temp.speed))
elif temp.direction == "down":
temp.rect = temp.rect.move((0, temp.speed))
elif temp.direction == "left":
temp.rect = temp.rect.move((-temp.speed, 0))
elif temp.direction == "right":
temp.rect = temp.rect.move((temp.speed, 0))
pygame.sprite.groupcollide(cls.group, Brick.group, True, True)
pygame.sprite.groupcollide(cls.group_enemy, Brick.group, True, True)
for bullet in cls.group:
if pygame.sprite.spritecollide(bullet, Iron.group, False, None):
cls.group.remove(bullet)
for bullet in cls.group_enemy:
if pygame.sprite.spritecollide(bullet, Iron.group, False, None):
cls.group_enemy.remove(bullet)
@classmethod
def show(cls, screen):
for temp in cls.group:
screen.blit(temp.image, temp.rect)
for temp in cls.group_enemy:
screen.blit(temp.image, temp.rect)
@classmethod
def move_and_show(cls, screen):
cls.auto_move()
cls.show(screen)
class PlayerTank(pygame.sprite.Sprite):
"""Player tank class"""
player_group = []
group = pygame.sprite.Group()
def __init__(self, player, top_left):
super().__init__()
image_path = "resources/images/playerTank/tank_T1_0.png" if player == "player1" else "resources/images/playerTank/tank_T2_0.png"
self.tank_all_image = pygame.image.load(image_path).convert_alpha()
self.image = self.tank_all_image.subsurface((0, 0), (48, 48))
self.rect = self.image.get_rect()
self.rect.topleft = top_left
self.origin_position = top_left
self.step_length = 8
self.direction = "up"
self.move_cache_time = 4
self.move_cache_count = 0
self.switch_count = 0
self.switch_time = 2
self.switch_image_index = False
self.image_postion_index = 0
self.is_bullet_cooling = False
self.bullet_cooling_count = 0
self.bullet_cooling_time = 30
self.life_num = 3
self.is_show_flag = True
self.__class__.player_group.append(self)
self.group.add(self)
def update_direction(self):
if self.direction == 'up':
self.image = self.tank_all_image.subsurface((0, 0), (48, 48))
self.image_postion_index = 0
elif self.direction == 'down':
self.image = self.tank_all_image.subsurface((0, 48), (48, 48))
self.image_postion_index = 48
elif self.direction == 'left':
self.image = self.tank_all_image.subsurface((0, 96), (48, 48))
self.image_postion_index = 96
elif self.direction == 'right':
self.image = self.tank_all_image.subsurface((0, 144), (48, 48))
self.image_postion_index = 144
def move(self, direction, group_list):
"""Move tank based on direction and handle collisions"""
if self.direction != direction:
self.direction = direction
self.update_direction()
return
self.move_cache_count += 1
if self.move_cache_count < self.move_cache_time:
return
else:
self.move_cache_count = 0
rect_ori = self.rect
if direction == "up":
self.rect = self.rect.move((0, -self.step_length))
elif direction == "down":
self.rect = self.rect.move((0, self.step_length))
elif direction == "left":
self.rect = self.rect.move((-self.step_length, 0))
elif direction == "right":
self.rect = self.rect.move((self.step_length, 0))
for group in group_list:
if pygame.sprite.spritecollide(self, group, False, None):
self.rect = rect_ori
if self.rect.top < BORDER_LEN:
self.rect.top = BORDER_LEN
elif self.rect.bottom > HEIGHT - BORDER_LEN:
self.rect.bottom = HEIGHT - BORDER_LEN
elif self.rect.left < BORDER_LEN:
self.rect.left = BORDER_LEN
elif self.rect.right > WIDTH - BORDER_LEN:
self.rect.right = WIDTH - BORDER_LEN
self.switch_count += 1
if self.switch_count > self.switch_time:
self.switch_count = 0
self.switch_image_index = not self.switch_image_index
self.image = self.tank_all_image.subsurface((48 * int(self.switch_image_index), self.image_postion_index), (48, 48))
def fire(self):
"""Fire a bullet if not cooling"""
if not self.is_bullet_cooling:
if self.direction == "up":
position = (self.rect.centerx, self.rect.y)
elif self.direction == "down":
position = (self.rect.centerx, self.rect.y + 48)
elif self.direction == "left":
position = (self.rect.x, self.rect.centery)
elif self.direction == "right":
position = (self.rect.x + 48, self.rect.centery)
Bullet("player", self.direction, position)
print("我方坦克发射子弹")
@classmethod
def move_player_tank(cls, is_dual_mode, group_list):
"""Handle keyboard input to move player tanks"""
key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_w]:
cls.player_group[0].move("up", group_list)
elif key_pressed[pygame.K_s]:
cls.player_group[0].move("down", group_list)
elif key_pressed[pygame.K_a]:
cls.player_group[0].move("left", group_list)
elif key_pressed[pygame.K_d]:
cls.player_group[0].move("right", group_list)
elif key_pressed[pygame.K_SPACE]:
cls.player_group[0].fire()
if is_dual_mode:
if key_pressed[pygame.K_UP]:
cls.player_group[1].move("up", group_list)
elif key_pressed[pygame.K_DOWN]:
cls.player_group[1].move("down", group_list)
elif key_pressed[pygame.K_LEFT]:
cls.player_group[1].move("left", group_list)
elif key_pressed[pygame.K_RIGHT]:
cls.player_group[1].move("right", group_list)
elif key_pressed[pygame.K_KP0]:
cls.player_group[1].fire()
def bullet_cooling(self):
"""Manage bullet cooldown timer"""
self.bullet_cooling_count += 1
if self.bullet_cooling_count > self.bullet_cooling_time:
self.is_bullet_cooling = False
self.bullet_cooling_count = 0
print("冷却完毕...")
else:
self.is_bullet_cooling = True
def judge_bomb(self):
"""Check if hit by enemy bullet"""
if pygame.sprite.spritecollide(self, Bullet.group_enemy, True, None):
self.life_num -= 1
if self.life_num == 0:
self.is_show_flag = False
self.rect.topleft = self.origin_position
@classmethod
def show(cls, screen, is_dual_mode):
"""Render player tanks"""
if cls.player_group:
if cls.player_group[0].is_show_flag:
screen.blit(cls.player_group[0].image, cls.player_group[0].rect)
cls.player_group[0].bullet_cooling()
cls.player_group[0].judge_bomb()
if is_dual_mode and cls.player_group[1].is_show_flag:
screen.blit(cls.player_group[1].image, cls.player_group[1].rect)
cls.player_group[1].bullet_cooling()
cls.player_group[1].judge_bomb()
class PlayerHome(pygame.sprite.Sprite):
"""Player home base"""
home = None
def __init__(self, position):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load('resources/images/home/home1.png')
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = position
self.__class__.home = self
@classmethod
def show(cls, screen):
screen.blit(cls.home.image, cls.home.rect)
class EnemyTank(pygame.sprite.Sprite):
"""Enemy tank class"""
group = pygame.sprite.Group()
def __init__(self, position):
super().__init__()
self.direction = random.choice(["up", "down", "left", "right"])
self.tank_all_image = pygame.image.load('resources/images/enemyTank/enemy_1_0.png')
self.image = None
self.update_direction()
self.rect = self.image.get_rect()
self.rect.topleft = position
self.speed = random.choice([2, 4])
self.is_bullet_cooling = True
self.bullet_cooling_count = 0
self.bullet_cooling_time = 5
self.group.add(self)
def update_direction(self):
if self.direction == 'up':
self.image = self.tank_all_image.subsurface((0, 0), (48, 48))
elif self.direction == 'down':
self.image = self.tank_all_image.subsurface((0, 48), (48, 48))
elif self.direction == 'left':
self.image = self.tank_all_image.subsurface((0, 96), (48, 48))
elif self.direction == 'right':
self.image = self.tank_all_image.subsurface((0, 144), (48, 48))
@classmethod
def show(cls, screen):
for temp in cls.group:
screen.blit(temp.image, temp.rect)
def move(self, group_list):
"""Automatic enemy movement with collision handling"""
rect_ori = self.rect
if self.direction == "up":
self.rect = self.rect.move((0, -self.speed))
elif self.direction == "down":
self.rect = self.rect.move((0, self.speed))
elif self.direction == "left":
self.rect = self.rect.move((-self.speed, 0))
elif self.direction == "right":
self.rect = self.rect.move((self.speed, 0))
for group in group_list:
if pygame.sprite.spritecollide(self, group, False, None):
self.rect = rect_ori
self.direction = random.choice(["up", "down", "left", "right"])
self.update_direction()
self.group.remove(self)
if pygame.sprite.spritecollide(self, self.group, False, None):
self.rect = rect_ori
self.direction = random.choice(["up", "down", "left", "right"])
self.update_direction()
self.group.add(self)
if pygame.sprite.spritecollide(self, PlayerTank.group, False, None):
self.rect = rect_ori
self.direction = random.choice(["up", "down", "left", "right"])
self.update_direction()
if pygame.sprite.spritecollide(self, Bullet.group, True, None):
self.group.remove(self)
if self.rect.top < BORDER_LEN:
self.rect.top = BORDER_LEN
self.direction = random.choice(["up", "down", "left", "right"])
self.update_direction()
elif self.rect.bottom > HEIGHT - BORDER_LEN:
self.rect.bottom = HEIGHT - BORDER_LEN
self.direction = random.choice(["up", "down", "left", "right"])
self.update_direction()
elif self.rect.left < BORDER_LEN:
self.rect.left = BORDER_LEN
self.direction = random.choice(["up", "down", "left", "right"])
self.update_direction()
elif self.rect.right > WIDTH - BORDER_LEN:
self.rect.right = WIDTH - BORDER_LEN
self.direction = random.choice(["up", "down", "left", "right"])
self.update_direction()
@classmethod
def auto_move(cls, group_list):
for temp in cls.group:
temp.move(group_list)
@classmethod
def auto_move_and_show(cls, screen, group_list):
cls.auto_move(group_list)
cls.show(screen)
def judge_cooling_and_fire(self):
"""Fire bullet after cooldown"""
self.bullet_cooling_count += 1
if self.bullet_cooling_count > self.bullet_cooling_time:
self.bullet_cooling_count = 0
if random.randint(1, 10) == 6:
self.is_bullet_cooling = False
if not self.is_bullet_cooling:
if self.direction == "up":
position = (self.rect.centerx, self.rect.y)
elif self.direction == "down":
position = (self.rect.centerx, self.rect.y + 48)
elif self.direction == "left":
position = (self.rect.x, self.rect.centery)
elif self.direction == "right":
position = (self.rect.x + 48, self.rect.centery)
Bullet("enemy", self.direction, position)
self.is_bullet_cooling = True
@classmethod
def fire(cls):
"""Make all enemy tanks attempt to fire"""
for temp in cls.group:
temp.judge_cooling_and_fire()
class Game(object):
"""Main game controller"""
def __init__(self):
"""Initialize pygame and screen"""
pygame.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
def game_start_interface(self):
"""Show start screen for player count selection"""
background_img = pygame.image.load('resources/images/others/background.png')
logo_img = pygame.image.load('resources/images/others/logo.png')
logo_img = pygame.transform.scale(logo_img, (446, 70))
logo_rect = logo_img.get_rect()
logo_rect.centerx, logo_rect.centery = WIDTH / 2, HEIGHT // 4
font = pygame.font.Font(FONTPATH, 60)
font_color_white = (255, 255, 255)
one_player_text = font.render('1 PLAYER', True, font_color_white)
one_player_rect = one_player_text.get_rect()
one_player_rect.left, one_player_rect.top = WIDTH / 2 - 50, HEIGHT / 2 - 60
two_players_text = font.render('2 PLAYERS', True, font_color_white)
two_players_rect = two_players_text.get_rect()
two_players_rect.left, two_players_rect.top = WIDTH / 2 - 50, HEIGHT / 2
select_player_num_tank = pygame.image.load('resources/images/playerTank/tank_T1_0.png').convert_alpha().subsurface((0, 144), (48, 48))
select_player_num_tank_rect = select_player_num_tank.get_rect()
game_tip = font.render('press <Enter> to start', True, font_color_white)
game_tip_rect = game_tip.get_rect()
game_tip_rect.centerx, game_tip_rect.top = WIDTH / 2, HEIGHT / 1.4
clock = pygame.time.Clock()
is_dual_mode = False
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
return is_dual_mode
elif event.key in (pygame.K_UP, pygame.K_DOWN, pygame.K_w, pygame.K_s):
is_dual_mode = not is_dual_mode
self.screen.blit(background_img, (0, 0))
self.screen.blit(logo_img, logo_rect)
self.screen.blit(one_player_text, one_player_rect)
self.screen.blit(two_players_text, two_players_rect)
if is_dual_mode:
select_player_num_tank_rect.right, select_player_num_tank_rect.top = two_players_rect.left - 10, two_players_rect.top
else:
select_player_num_tank_rect.right, select_player_num_tank_rect.top = one_player_rect.left - 10, one_player_rect.top
self.screen.blit(select_player_num_tank, select_player_num_tank_rect)
self.screen.blit(game_tip, game_tip_rect)
pygame.display.update()
clock.tick(60)
def game_end_interface(self, is_win):
"""Show win/lose screen and handle restart/quit"""
background_img = pygame.image.load('resources/images/others/background.png')
game_over_img = pygame.image.load('resources/images/others/gameover.png')
game_over_img = pygame.transform.scale(game_over_img, (150, 75))
game_over_img_rect = game_over_img.get_rect()
game_over_img_rect.midtop = WIDTH / 2, HEIGHT / 8
color_white = (255, 255, 255)
font = pygame.font.Font(FONTPATH, 60)
if is_win:
font_render = font.render('Congratulations, You win!', True, color_white)
else:
font_render = font.render('Sorry, You fail!', True, color_white)
font_rect = font_render.get_rect()
font_rect.centerx, font_rect.centery = WIDTH / 2, HEIGHT / 3
tank_cursor = pygame.image.load('resources/images/playerTank/tank_T1_0.png').convert_alpha().subsurface((0, 144), (48, 48))
tank_rect = tank_cursor.get_rect()
restart_render_white = font.render('RESTART', True, color_white)
restart_rect = restart_render_white.get_rect()
restart_rect.left, restart_rect.top = WIDTH / 2.4, HEIGHT / 2
quit_render_white = font.render('QUIT', True, color_white)
quit_rect = quit_render_white.get_rect()
quit_rect.left, quit_rect.top = WIDTH / 2.4, HEIGHT / 1.6
is_quit_game = False
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
return is_quit_game
elif event.key in (pygame.K_UP, pygame.K_DOWN, pygame.K_w, pygame.K_s):
is_quit_game = not is_quit_game
self.screen.blit(background_img, (0, 0))
self.screen.blit(game_over_img, game_over_img_rect)
self.screen.blit(font_render, font_rect)
if not is_quit_game:
tank_rect.right, tank_rect.top = restart_rect.left - 10, restart_rect.top
self.screen.blit(tank_cursor, tank_rect)
self.screen.blit(quit_render_white, quit_rect)
self.screen.blit(restart_render_white, restart_rect)
else:
tank_rect.right, tank_rect.top = quit_rect.left - 10, quit_rect.top
self.screen.blit(tank_cursor, tank_rect)
self.screen.blit(quit_render_white, quit_rect)
self.screen.blit(restart_render_white, restart_rect)
pygame.display.update()
clock.tick(60)
def parse_game_level_file(self):
"""Parse level configuration and create game objects"""
grid_size = 24
with open("./levels/3.lvl", errors='ignore') as f:
num_row = -1
for line in f.readlines():
line = line.strip('\n')
if line[0] in ["S", "B", "I", "R", "C", "T"]:
num_row += 1
for num_col, elem in enumerate(line.split(' ')):
position = BORDER_LEN + num_col * grid_size, BORDER_LEN + num_row * grid_size
if elem == 'B':
Brick(position)
elif elem == 'I':
Iron(position)
elif elem == 'R':
River(position)
elif elem == 'C':
Ice(position)
elif elem == 'T':
Tree(position)
elif line.startswith('%HOMEPOS'):
home_position = line.split(':')[-1]
home_position = int(home_position.split(',')[0]), int(home_position.split(',')[1])
home_position = (BORDER_LEN + home_position[0] * grid_size, BORDER_LEN + home_position[1] * grid_size)
PlayerHome(home_position)
elif line.startswith('%PLAYERTANKPOS'):
player_tank_positions = line.split(':')[-1]
player_tank_positions = [(int(pos.split(',')[0]), int(pos.split(',')[1])) for pos in player_tank_positions.split(' ')]
player_tank_positions = [[BORDER_LEN + pos[0] * grid_size, BORDER_LEN + pos[1] * grid_size] for pos in player_tank_positions]
PlayerTank('player1', player_tank_positions[0])
PlayerTank('player2', player_tank_positions[1])
elif line.startswith('%ENEMYTANKPOS'):
position = line.split(':')[-1]
position = [[int(pos.split(',')[0]), int(pos.split(',')[1])] for pos in position.split(' ')]
position = [(BORDER_LEN + pos[0] * grid_size, BORDER_LEN + pos[1] * grid_size) for pos in position]
for pos in position:
EnemyTank(pos)
def game_run_level(self, is_dual_mode):
"""Run the main game loop for a level"""
background_img = pygame.image.load('resources/images/others/background.png')
self.parse_game_level_file()
clock = pygame.time.Clock()
is_win = False
is_running = True
while is_running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
PlayerTank.move_player_tank(is_dual_mode, [Brick.group, River.group, Ice.group, Iron.group])
EnemyTank.fire()
if pygame.sprite.spritecollide(PlayerHome.home, Bullet.group, True, None) or pygame.sprite.spritecollide(PlayerHome.home, Bullet.group_enemy, True, None):
is_running = False
is_win = False
if len(EnemyTank.group) == 0:
is_running = False
is_win = True
if (not is_dual_mode and PlayerTank.player_group[0].life_num == 0) or (is_dual_mode and PlayerTank.player_group[0].life_num == 0 and PlayerTank.player_group[1].life_num == 0):
is_running = False
is_win = False
self.screen.blit(background_img, (0, 0))
Brick.show(self.screen)
Iron.show(self.screen)
River.show(self.screen)
Ice.show(self.screen)
Tree.show(self.screen)
PlayerHome.show(self.screen)
PlayerTank.show(self.screen, is_dual_mode)
Bullet.move_and_show(self.screen)
EnemyTank.auto_move_and_show(self.screen, [Brick.group, River.group, Ice.group, Iron.group])
pygame.display.update()
clock.tick(60)
return is_win
def clean(self):
"""Clear all sprite groups for a fresh start"""
EnemyTank.group.empty()
PlayerTank.group.empty()
PlayerTank.player_group.clear()
Brick.group.empty()
Ice.group.empty()
River.group.empty()
Iron.group.empty()
Tree.group.empty()
Bullet.group.empty()
Bullet.group_enemy.empty()
def run(self):
"""Main entry point to start the game"""
is_dual_mode = self.game_start_interface()
is_win = self.game_run_level(is_dual_mode)
while True:
if self.game_end_interface(is_win):
break
self.clean()
pygame.display.set_mode((WIDTH, HEIGHT))
is_dual_mode = self.game_start_interface()
is_win = self.game_run_level(is_dual_mode)
if __name__ == '__main__':
"""Start the game"""
game = Game()
game.run()Python Programming Learning Circle
A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.