在经典的游戏领域,俄罗斯方块无疑占据了重要的一席之地。自其诞生以来,这款简单而又富有挑战性的游戏便吸引了无数玩家的青睐。而今,随着人工智能技术的飞速发展,将机器学习应用于传统游戏中已成为一个热门话题。本文将详细介绍如何使用Python及其相关库,结合机器学习算法,开发一款具备AI智能的俄罗斯方块游戏。
一、项目背景与目标
1.1 项目背景
俄罗斯方块是一款经典的休闲益智游戏,玩家需要控制不同形状的方块下落,并旋转、移动这些方块,以填满游戏的每一行。每填满一行,这些行就会消失,并为玩家腾出空间继续游戏。随着游戏的进行,方块下落的速度会逐渐加快,增加了游戏的挑战性。
1.2 项目目标
本项目旨在通过Python编程语言,结合机器学习技术,实现一个具备AI智能的俄罗斯方块游戏。具体目标包括:
- 设计并实现一个基本的俄罗斯方块游戏框架。
- 集成机器学习算法,让AI能够自动进行方块的旋转、移动和下落操作。
- 提供用户交互界面,允许玩家与AI进行对战或观察AI游戏过程。
- 实现AI性能的可视化展示,包括得分、等级等信息。
二、技术选型与工具
2.1 Python编程语言
Python因其简洁的语法、丰富的库支持和强大的社区资源,成为实现本项目的首选编程语言。
2.2 Pygame库
Pygame是一个跨平台的Python模块,专为电子游戏开发设计,提供了图形和声音库,用于创建视频游戏。它非常适合用来实现俄罗斯方块的图形界面。
2.3 NumPy库
NumPy是Python的一个扩展库,支持大量的维度数组与矩阵运算,为AI算法中的数据处理提供了极大便利。
2.4 机器学习算法
虽然俄罗斯方块游戏本质上并非典型的机器学习应用场景,但可以通过模拟人类玩家的决策过程,设计一套基于规则的AI系统。同时,也可以探索使用强化学习等算法来训练AI模型,以进一步提升游戏表现。
三、系统设计与实现
3.1 游戏框架设计
游戏框架主要包括以下几个部分:
- 游戏界面:使用Pygame库绘制游戏界面,包括游戏区域、下一个方块预览、得分显示等。
- 方块控制:实现方块的生成、旋转、移动和下落逻辑。
- 游戏逻辑:处理游戏得分、等级提升、游戏结束判断等逻辑。
- AI控制:集成机器学习算法或预设规则,实现AI自动操作。
3.2 AI设计思路
AI的设计可以采用多种策略,包括但不限于以下几种:
- 预设规则:根据游戏状态(如当前方块形状、游戏区域填满情况等),预设一系列规则来决定方块的旋转、移动和下落。
- 模拟退火算法:通过模拟物理退火过程,在解空间中寻找最优解,以指导AI的决策。
- 强化学习:利用强化学习算法,通过与环境(即游戏)的交互来学习最优策略。这通常需要大量的训练数据和计算资源。
在本项目中,我们将主要采用预设规则来实现AI,并简单探讨强化学习的应用前景。
3.3 关键代码实现
3.3.1 游戏界面绘制
使用Pygame绘制游戏界面,包括游戏区域、得分板、等级显示等。
3.3.2 AI决策逻辑
AI通过预设规则来决定方块的旋转、移动和下落。
四、系统测试与优化
4.1 测试方案
- 单元测试:针对游戏框架的各个模块编写单元测试,确保每个功能按预期工作。
- 集成测试:测试整个游戏系统的集成情况,确保各模块协同工作无误。
- 性能测试:评估游戏在不同配置下的运行性能,确保流畅的游戏体验。
4.2 优化策略
- 算法优化:对AI决策算法进行优化,提高决策效率和准确性。
- 资源管理:优化游戏资源的使用,减少内存和CPU消耗。
- 用户体验:改进用户界面和交互设计,提升用户体验。
五、结论与展望
本文介绍了一个基于Python的AI俄罗斯方块游戏的设计与实现过程。通过Pygame库构建游戏界面,结合NumPy库处理数据,我们实现了一个基本的俄罗斯方块游戏框架,并探讨了如何集成机器学习算法来提升游戏的AI智能。
然而,当前的AI实现仍较为简单,主要依赖于预设规则。未来工作可以进一步探索强化学习等高级机器学习算法在游戏AI中的应用,通过训练模型来学习更复杂的游戏策略。此外,还可以考虑增加网络对战功能,允许玩家与全球玩家进行实时对战,进一步提升游戏的趣味性和挑战性。
通过本项目的实践,我们不仅掌握了Python在游戏开发中的应用技巧,还深刻理解了机器学习在游戏AI中的潜力和挑战。相信随着技术的不断进步和创新思维的不断激发,未来的游戏世界将更加丰富多彩、充满惊喜。
完整代码:
import pygame
import random
import numpy as np
from collections import deque
import copy
from typing import List, Tuple
# 初始化 Pygame
pygame.init()
# 设置游戏窗口大小
WINDOW_WIDTH = 900 # 增大窗口宽度
WINDOW_HEIGHT = 920
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption("俄罗斯方块 AI")
# 定义方块颜色
COLORS = [
(0, 0, 0),
(255, 0, 0),
(0, 255, 0),
(0, 0, 255),
(255, 255, 0),
(255, 0, 255),
(0, 255, 255),
]
# 定义游戏网格大小
GRID_ROWS = 23
GRID_COLS = 12
BLOCK_SIZE = 40 # 稍微减小方块大小
# 定义方块的形状
SHAPES = [
[[1, 1, 1, 1]],
[[1, 1], [1, 1]],
[[1, 0, 0], [1, 1, 1]],
[[0, 0, 1], [1, 1, 1]],
[[0, 1, 1], [1, 1, 0]],
[[1, 1, 0], [0, 1, 1]],
[[0, 1, 0], [1, 1, 1]],
[[1, 1, 1, 1]],
]
# 定义按键重复延迟和间隔
KEY_REPEAT_DELAY = 100
KEY_REPEAT_INTERVAL = 5
# 定义游戏状态
class GameState:
def __init__(self):
self.board = np.zeros((GRID_ROWS, GRID_COLS), dtype=int)
self.current_shape = random.choice(SHAPES)
self.next_shape = random.choice(SHAPES)
self.current_color = random.randint(1, len(COLORS) - 1)
self.current_x = GRID_COLS // 2 - len(self.current_shape[0]) // 2
self.current_y = 0
self.score = 0
self.level = 1
self.lines_cleared = 0
self.game_over = False # 游戏结束标志
self.history = deque(maxlen=100) # 保存最近 10 个游戏状态
self.fall_time = 0 # 记录方块下落的时间
self.fall_speed = 1.0 # 初始下落速度
self.add_blocks_time = 0 # 添加底部方块的计时器
self.add_blocks_interval = 1000000 # 每60秒添加一次底部方块
self.ai_speed = 0.01 # AI 速度参数
self.next_color = random.randint(1, len(COLORS) - 1)
self.ai_rotation_speed = 0.01 # AI 旋转速度
self.ai_move_speed = 0.01 # AI 平移速度
self.ai_move_queue = [] # 存储 AI 的移动指令队列
def update(self, dt):
self.fall_time += dt
if self.fall_time >= self.fall_speed:
self.fall_time = 0
self.current_y += 1
if self.check_collision():
self.current_y -= 1
self.lock_shape()
self.remove_full_rows()
self.spawn_new_shape()
if self.check_collision():
self.game_over = True
self.add_blocks_time += dt
if self.add_blocks_time >= self.add_blocks_interval:
self.add_blocks_time = 0
self.add_bottom_blocks()
def move_left(self):
self.current_x -= 1
if self.check_collision():
self.current_x += 1
def move_right(self):
self.current_x += 1
if self.check_collision():
self.current_x -= 1
def rotate_shape(self):
rotated_shape = list(zip(*reversed(self.current_shape)))
old_shape = self.current_shape
self.current_shape = rotated_shape
if self.check_collision():
self.current_shape = old_shape
def soft_drop(self):
self.current_y += 1
if self.check_collision():
self.current_y -= 1
self.lock_shape()
self.remove_full_rows()
self.spawn_new_shape()
if self.check_collision():
self.game_over = True
def hard_drop(self):
while not self.check_collision():
self.current_y += 1
self.current_y -= 1
self.lock_shape()
self.remove_full_rows()
self.spawn_new_shape()
if self.check_collision():
self.game_over = True
def check_collision(self):
for y, row in enumerate(self.current_shape):
for x, cell in enumerate(row):
if cell:
if (self.current_y + y >= GRID_ROWS or
self.current_x + x < 0 or
self.current_x + x >= GRID_COLS or
self.board[self.current_y + y][self.current_x + x]):
return True
return False
def lock_shape(self):
for y, row in enumerate(self.current_shape):
for x, cell in enumerate(row):
if cell:
self.board[self.current_y + y][self.current_x + x] = self.current_color
def remove_full_rows(self):
full_rows = [i for i, row in enumerate(self.board) if all(row)]
if full_rows:
print(f"Removing full rows: {full_rows}") # 添加日志记录
# 删除满行
self.board = np.delete(self.board, full_rows, axis=0)
# 在顶部添加新的空行
new_rows = np.zeros((len(full_rows), GRID_COLS), dtype=int)
self.board = np.vstack((new_rows, self.board))
cleared_rows = len(full_rows)
self.lines_cleared += cleared_rows
# 根据消除的行数计算得分
if cleared_rows == 1:
self.score += 100
elif cleared_rows == 2:
self.score += 300
elif cleared_rows == 3:
self.score += 700
elif cleared_rows == 4:
self.score += 1500
self.level = self.lines_cleared // 100 + 1
self.fall_speed = max(0.5, 1.0 - (self.level - 1) * 0.05)
print(f"Cleared {cleared_rows} rows. New score: {self.score}, New level: {self.level}")
def spawn_new_shape(self):
self.current_shape = self.next_shape
self.current_color = self.next_color
self.next_shape = random.choice(SHAPES)
self.next_color = random.randint(1, len(COLORS) - 1)
self.current_x = GRID_COLS // 2 - len(self.current_shape[0]) // 2
self.current_y = 0
def add_bottom_blocks(self):
# 将现有方块向上移动两行
self.board = np.roll(self.board, -2, axis=0)
# 在底部两行添加交错的方块
for i in range(GRID_ROWS - 2, GRID_ROWS):
for j in range(GRID_COLS):
if (i + j) % 2 == 0:
self.board[i][j] = random.randint(1, len(COLORS) - 1)
else:
self.board[i][j] = 0
# 检查游戏是否结束
if np.any(self.board[0] != 0) or np.any(self.board[1] != 0):
self.game_over = True
def clear_screen(self):
self.board = np.zeros((GRID_ROWS, GRID_COLS), dtype=int)
def execute_ai_move(self, rotation, move):
self.ai_move_queue = []
for _ in range(rotation):
self.ai_move_queue.append(('rotate', None))
if move < 0:
for _ in range(abs(move)):
self.ai_move_queue.append(('move', 'left'))
elif move > 0:
for _ in range(move):
self.ai_move_queue.append(('move', 'right'))
self.ai_move_queue.append(('drop', None))
def process_ai_move(self):
if self.ai_move_queue:
action, direction = self.ai_move_queue.pop(0)
if action == 'rotate':
self.rotate_shape()
elif action == 'move':
if direction == 'left':
self.move_left()
elif direction == 'right':
self.move_right()
elif action == 'drop':
self.hard_drop()
return True
return False
class TetrisAI:
def __init__(self, game_state, well_score_weight=5, i_piece_well_bonus=2000):
self.game_state = game_state
self.well_score_weight = well_score_weight
self.i_piece_well_bonus = i_piece_well_bonus # 新增:I型方块放入竖井的奖励分数
def find_deepest_well(self, board):
"""
找到最深竖井的列索引和深度。
返回 (column_index, depth) 或 (None, None) 如果没有找到竖井。
"""
max_depth = 0
deepest_well_col = None
for col in range(GRID_COLS):
# 检查左右两边的高度
left_height = GRID_ROWS
right_height = GRID_ROWS
if col > 0:
left_height = np.argmax(board[:, col - 1] > 0)
if col < GRID_COLS - 1:
right_height = np.argmax(board[:, col + 1] > 0)
current_height = np.argmax(board[:, col] > 0)
# 检查是否为竖井(当前列比左右两列都低)
if current_height > 0 and (left_height - current_height >= 4 or right_height - current_height >= 4):
if GRID_ROWS - current_height > max_depth:
max_depth = GRID_ROWS - current_height
deepest_well_col = col
return deepest_well_col, max_depth
def get_best_move(self):
best_score = float('-inf')
best_rotation = 0
best_move = 0
for rotation in range(4):
for move in range(-8, 9):
score = self.evaluate_move(rotation, move)
if score > best_score:
best_score = score
best_rotation = rotation
best_move = move
# 检查是否可以强制放置长条
if np.max(self.game_state.board > 0) > 10 and self.game_state.current_shape == [[1, 1, 1, 1]]:
deepest_col, _ = self.find_deepest_well(self.game_state.board)
if deepest_col is not None:
# 尝试将长条放入最深竖井
test_state = copy.deepcopy(self.game_state)
test_state.current_x = deepest_col - len(test_state.current_shape[0]) // 2
if not test_state.check_collision():
# 如果没有碰撞,则锁定方块并返回特殊标记(或None,表示已处理)
test_state.lock_shape()
self.game_state = test_state # 直接更新游戏状态
return None, None # 或其他特殊标记
return best_rotation, best_move
def try_force_place_i_piece_in_rightmost_well(game_state):
# 假设这个函数实现了检测竖井和尝试放置长条的逻辑
# 如果成功放置,返回True;否则返回False
pass
def evaluate_move(self, rotation, move):
test_state = copy.deepcopy(self.game_state)
for _ in range(rotation):
test_state.rotate_shape()
if move < 0:
for _ in range(abs(move)):
test_state.move_left()
elif move > 0:
for _ in range(move):
test_state.move_right()
while not test_state.check_collision():
test_state.current_y += 1
test_state.current_y -= 1
test_state.lock_shape()
# 检查游戏板的高度(这里简化为检查整个游戏板)
max_height = np.max(self.game_state.board > 0)
is_i_piece = self.game_state.current_shape == [[1, 1, 1, 1]]
# 如果游戏板高度很高且当前是I型方块,则调整评估逻辑以优先考虑竖井
if max_height > 13 and is_i_piece:
# 可以在这里添加逻辑来检测竖井并尝试将I型方块放入竖井
# 但由于这涉及到复杂的游戏板分析和可能的方块预览,我们在这里不实现它
# 相反,我们只是在评估分数时给予一个额外的奖励来鼓励这种行为
well_bonus = 1000 # 假设的额外奖励
else:
well_bonus = 0
height = self.get_height(test_state.board)
holes = self.count_holes(test_state.board)
bumpiness = self.get_bumpiness(test_state.board)
complete_lines = self.count_complete_lines(test_state.board)
well_score = self.get_well_score(test_state.board)
bonus_for_multiple_lines = 0
if complete_lines >= 3:
bonus_for_multiple_lines += 500 # 假设消除3行或更多行的额外奖励
if complete_lines == 4:
# 如果同时消除了4行,我们可以再给一个额外的奖励,或者增加上面的奖励
bonus_for_multiple_lines += 1000 # 额外奖励,仅当消除4行时
# 新增:检查是否为I型方块并是否放入了竖井
i_piece_well_bonus = self.check_i_piece_in_well(test_state) * self.i_piece_well_bonus
if height <= 11 and holes == 0:
score = (complete_lines * -1000000 -
height * 400 -
holes * 2000000000 -
bumpiness * 800 -
well_score * self.well_score_weight * 10 +
i_piece_well_bonus * 2000 + bonus_for_multiple_lines * 2000000000) # 加入I型方块放入竖井的奖励
elif height >= 12 or holes > 0:
score = (complete_lines * 10000 -
height * 40000 -
holes * 2000000000 -
bumpiness * 800 -
well_score * self.well_score_weight * 10 +
i_piece_well_bonus * 2000 + bonus_for_multiple_lines * 2000000000) # 加入I型方块放入竖井的奖励
return score
def check_i_piece_in_well(self, test_state):
# 检查是否为I型方块
if test_state.current_shape == [[1, 1, 1, 1]]:
# 检查是否放入了竖井
for col in range(GRID_COLS):
if (col == 0 or np.all(test_state.board[:, col-1] != 0)) and \
(col == GRID_COLS-1 or np.all(test_state.board[:, col+1] != 0)) and \
np.all(test_state.board[:, col] == 0):
return 1 # 找到了竖井,并且I型方块放入其中
return 0 # 不是I型方块或没有放入竖井
def get_height(self, board):
return GRID_ROWS - np.argmax(board.sum(axis=1) > 0)
def count_holes(self, board):
holes = 0
for col in range(GRID_COLS):
block_found = False
for row in range(GRID_ROWS):
if board[row][col]:
block_found = True
elif block_found:
holes += 1
return holes
def get_bumpiness(self, board):
heights = [0] * GRID_COLS
for col in range(GRID_COLS):
for row in range(GRID_ROWS):
if board[row][col]:
heights[col] = GRID_ROWS - row
break
return sum(abs(heights[i] - heights[i + 1]) for i in range(GRID_COLS - 1))
def get_well_score(self, board):
well_score = 0
for col in range(GRID_COLS):
# 检查当前列是否比左右两列(或边缘)低至少4格
left_height = GRID_ROWS # 假设左边没有方块,高度为网格行数
right_height = GRID_ROWS # 假设右边没有方块,高度为网格行数
if col > 0:
left_height = GRID_ROWS - np.argmax(board[:, col - 1] > 0)
if col < GRID_COLS - 1:
right_height = GRID_ROWS - np.argmax(board[:, col + 1] > 0)
current_col_height = GRID_ROWS - np.argmax(board[:, col] > 0)
# 如果当前列比左右两列都低至少4格,则认为是竖井的一部分
if current_col_height >= 4 and (
left_height - current_col_height >= 4 or right_height - current_col_height >= 4):
# 实际上,我们不需要检查两边都低4格,只要一边低4格就足够了
# 但为了保持逻辑的清晰,这里还是按照两边都检查的方式写(虽然效率稍低)
# 更优化的方式是只检查一边,并累加竖井长度
well_start = col
well_length = 1
# 向左扩展竖井范围
while well_start > 0 and GRID_ROWS - np.argmax(board[:, well_start - 1] > 0) - current_col_height >= 4:
well_start -= 1
well_length += 1
# 无需再向右扩展,因为我们已经检查了右边(除非你想要连续的低列都被算作一个竖井)
# 但为了简化,我们只计算当前列作为竖井的一部分
# 累加竖井分数(这里我们简单地将竖井长度作为分数)
well_score += well_length
return well_score
def count_complete_lines(self, board):
return sum(1 for row in board if all(row))
def render_game(screen, game_state, font, ai_enabled):
# 绘制游戏区域背景
pygame.draw.rect(screen, (0, 0, 0), (0, 0, GRID_COLS * BLOCK_SIZE, GRID_ROWS * BLOCK_SIZE))
# 绘制游戏区域
for y in range(GRID_ROWS):
for x in range(GRID_COLS):
color = COLORS[game_state.board[y][x]]
pygame.draw.rect(screen, color, (x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE - 1, BLOCK_SIZE - 1))
# 绘制当前方块
for i in range(len(game_state.current_shape)):
for j in range(len(game_state.current_shape[i])):
if game_state.current_shape[i][j] == 1:
x = game_state.current_x + j
y = game_state.current_y + i
color = COLORS[game_state.current_color]
pygame.draw.rect(screen, color, (x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE - 1, BLOCK_SIZE - 1))
# 绘制下一个方块
next_shape_surface = pygame.Surface((6 * BLOCK_SIZE, 6 * BLOCK_SIZE))
next_shape_surface.fill((255, 255, 255))
for i in range(len(game_state.next_shape)):
for j in range(len(game_state.next_shape[i])):
if game_state.next_shape[i][j] == 1:
color = COLORS[game_state.next_color]
pygame.draw.rect(next_shape_surface, color, ((j+1) * BLOCK_SIZE, (i+1) * BLOCK_SIZE, BLOCK_SIZE - 1, BLOCK_SIZE - 1))
screen.blit(next_shape_surface, (GRID_COLS * BLOCK_SIZE + 20, 20))
# 绘制分数和等级
score_text = font.render(f"Score: {game_state.score}", True, (0, 0, 0))
level_text = font.render(f"Level: {game_state.level}", True, (0, 0, 0))
lines_text = font.render(f"Lines: {game_state.lines_cleared}", True, (0, 0, 0))
screen.blit(score_text, (GRID_COLS * BLOCK_SIZE + 20, 200))
screen.blit(level_text, (GRID_COLS * BLOCK_SIZE + 20, 240))
screen.blit(lines_text, (GRID_COLS * BLOCK_SIZE + 20, 280))
# 绘制AI状态和速度
ai_status = "ON" if ai_enabled else "OFF"
ai_status_text = font.render(f"AI: {ai_status}", True, (0, 0, 0))
ai_speed_text = font.render(f"AI Speed: {game_state.ai_speed:.3f}", True, (0, 0, 0))
screen.blit(ai_status_text, (GRID_COLS * BLOCK_SIZE + 20, 320))
screen.blit(ai_speed_text, (GRID_COLS * BLOCK_SIZE + 20, 360))
ai_rotation_speed_text = font.render(f"AI Rotation Speed: {game_state.ai_rotation_speed:.3f}", True, (0, 0, 0))
ai_move_speed_text = font.render(f"AI Move Speed: {game_state.ai_move_speed:.3f}", True, (0, 0, 0))
screen.blit(ai_rotation_speed_text, (GRID_COLS * BLOCK_SIZE + 20, 400))
screen.blit(ai_move_speed_text, (GRID_COLS * BLOCK_SIZE + 20, 440))
# 绘制游戏说明
instructions = [
"Controls:",
"Left/Right: Move",
"Up: Rotate",
"Down: Soft Drop",
"Space: Hard Drop",
"A: Toggle AI",
"1/2 Arrows: Adjust AI Speed",
"3/4 Arrows: Adjust AI Rotation Speed",
"5/6 Arrows: Adjust AI Move Speed",
"-: Remove Bottom Row",
"=: Clear Screen"
]
for i, instruction in enumerate(instructions):
instruction_text = font.render(instruction, True, (0, 0, 0))
screen.blit(instruction_text, (GRID_COLS * BLOCK_SIZE + 20, 500 + i * 30))
# 绘制游戏结束文本
if game_state.game_over:
game_over_text = font.render("Game Over", True, (255, 0, 0))
screen.blit(game_over_text, (WINDOW_WIDTH // 2 - 70, WINDOW_HEIGHT // 2 - 18))
def main():
game_state = GameState()
ai = TetrisAI(game_state, well_score_weight=20, i_piece_well_bonus=1000)
clock = pygame.time.Clock()
font = pygame.font.Font(None, 30)
running = True
ai_enabled = False
pygame.key.set_repeat(KEY_REPEAT_DELAY, KEY_REPEAT_INTERVAL)
ai_move_time = 0
ai_rotation_time = 0
ai_action_in_progress = False
while running:
dt = clock.tick(60) / 1000.0
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
ai_enabled = not ai_enabled
elif event.key == pygame.K_2:
game_state.ai_speed = min(1.0, game_state.ai_speed + 0.001)
elif event.key == pygame.K_1:
game_state.ai_speed = max(0.001, game_state.ai_speed - 0.001)
elif event.key == pygame.K_3:
game_state.ai_rotation_speed = max(0.001, game_state.ai_rotation_speed - 0.001)
elif event.key == pygame.K_4:
game_state.ai_rotation_speed = min(1.0, game_state.ai_rotation_speed + 0.001)
elif event.key == pygame.K_5:
game_state.ai_move_speed = max(0.001, game_state.ai_move_speed - 0.001)
elif event.key == pygame.K_6:
game_state.ai_move_speed = min(1.0, game_state.ai_move_speed + 0.001)
elif not ai_enabled:
if event.key == pygame.K_LEFT:
game_state.move_left()
elif event.key == pygame.K_RIGHT:
game_state.move_right()
elif event.key == pygame.K_DOWN:
game_state.soft_drop()
elif event.key == pygame.K_SPACE:
game_state.hard_drop()
elif event.key == pygame.K_UP:
game_state.rotate_shape()
elif event.key == pygame.K_MINUS:
game_state.remove_bottom_row()
elif event.key == pygame.K_EQUALS:
game_state.clear_screen()
if not game_state.game_over:
game_state.update(dt)
if ai_enabled:
if not ai_action_in_progress:
rotation, move = ai.get_best_move()
game_state.execute_ai_move(rotation, move)
ai_action_in_progress = True
if ai_action_in_progress:
ai_move_time += dt
ai_rotation_time += dt
if game_state.ai_move_queue and game_state.ai_move_queue[0][0] == 'rotate':
if ai_rotation_time >= game_state.ai_rotation_speed:
ai_rotation_time = 0
game_state.process_ai_move()
elif ai_move_time >= game_state.ai_move_speed:
ai_move_time = 0
if not game_state.process_ai_move():
ai_action_in_progress = False
screen.fill((255, 255, 255))
render_game(screen, game_state, font, ai_enabled)
pygame.display.flip()
if game_state.game_over:
pygame.time.wait(2000) # 等待2秒
running = False
pygame.quit()
print("Final Score:", game_state.score)
if __name__ == "__main__":
main()
游戏演示: