import random
import math
import sys
import numpy as np
import pygame
# -------------------- 常量 --------------------
CELL = 30 # 像素
COLS, ROWS = 10, 20
SCREEN_W, SCREEN_H = 20 * CELL, ROWS * CELL
FPS = 60 # 渲染帧率
pygame.init()
# 增加顶部信息区域高度
screen = pygame.display.set_mode((SCREEN_W, SCREEN_H + 60))
pygame.display.set_caption("俄罗斯方块(可切换人工/AI)")
clock = pygame.time.Clock()
# 确保中文显示正常 - 尝试多种中文字体
def get_chinese_font(size):
# 尝试常见的中文字体
font_names = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC", "Microsoft YaHei"]
for name in font_names:
try:
return pygame.font.SysFont(name, size)
except:
continue
# 如果没有找到中文字体,返回默认字体
return pygame.font.SysFont(None, size)
# 颜色
COLORS = [(0, 0, 0)] + [(random.randrange(50, 255),
random.randrange(50, 255),
random.randrange(50, 255)) for _ in range(7)]
# 7 种方块的相对坐标(以 (0,0) 为旋转中心)
SHAPES = [
[[0, 0], [1, 0], [0, 1], [1, 1]], # O
[[0, 0], [1, 0], [2, 0], [3, 0]], # I
[[0, 0], [1, 0], [2, 0], [1, 1]], # T
[[0, 1], [1, 1], [1, 0], [2, 0]], # S
[[0, 0], [1, 0], [1, 1], [2, 1]], # Z
[[0, 0], [1, 0], [2, 0], [2, 1]], # L
[[0, 1], [1, 1], [2, 1], [2, 0]] # J
]
# -------------------- 游戏逻辑 --------------------
class Board:
def __init__(self):
self.grid = [[0] * COLS for _ in range(ROWS)]
self.current = None
self.next_piece()
self.game_over = False
self.score = 0
self.drop_timer = 0
self.drop_interval = 500 # ms
def next_piece(self):
idx = random.randrange(len(SHAPES))
self.current = {
'shape': SHAPES[idx],
'x': COLS // 2 - 1,
'y': 0,
'color': idx + 1
}
if self.collision(self.current, 0, 0):
self.game_over = True
def collision(self, piece, dx, dy, rot=None):
shape = self.rotate(piece['shape']) if rot else piece['shape']
for (px, py) in shape:
nx = piece['x'] + px + dx
ny = piece['y'] + py + dy
if nx < 0 or nx >= COLS or ny >= ROWS:
return True
if ny >= 0 and self.grid[ny][nx]:
return True
return False
def rotate(self, shape):
# 顺时针 90°
return [(y, -x) for (x, y) in shape]
def lock_piece(self):
for (px, py) in self.current['shape']:
x = self.current['x'] + px
y = self.current['y'] + py
if 0 <= y < ROWS:
self.grid[y][x] = self.current['color']
self.clear_lines()
self.next_piece()
def clear_lines(self):
lines = [row for row in range(ROWS) if 0 not in self.grid[row]]
for row in lines:
del self.grid[row]
self.grid.insert(0, [0] * COLS)
self.score += len(lines) ** 2 * 100
def soft_drop(self):
if not self.collision(self.current, 0, 1):
self.current['y'] += 1
else:
self.lock_piece()
def hard_drop(self):
while not self.collision(self.current, 0, 1):
self.current['y'] += 1
self.lock_piece()
def move(self, dx):
if not self.collision(self.current, dx, 0):
self.current['x'] += dx
def turn(self):
if not self.collision(self.current, 0, 0, rot=True):
self.current['shape'] = self.rotate(self.current['shape'])
def step(self, dt):
self.drop_timer += dt
if self.drop_timer >= self.drop_interval:
self.soft_drop()
self.drop_timer = 0
# -------------------- AI 策略 --------------------
class AIPlayer:
WEIGHTS = {
'height': -0.510066,
'lines': 0.760666,
'holes': -0.35663,
'bump': -0.184483
}
@staticmethod
def get_possible_moves(board):
piece = board.current
best = None
best_score = -math.inf
for rot in range(4):
shape = piece['shape']
for _ in range(rot):
shape = [(y, -x) for (x, y) in shape]
min_x = -min(x for x, y in shape)
max_x = COLS - max(x for x, y in shape) - 1
for x in range(min_x, max_x + 1):
sim = [row[:] for row in board.grid]
y = 0
while True:
if any(y + py >= ROWS or (y + py >= 0 and sim[y + py][x + px])
for px, py in shape):
y -= 1
break
y += 1
for px, py in shape:
if 0 <= y + py < ROWS:
sim[y + py][x + px] = piece['color']
score = AIPlayer.evaluate(sim)
if score > best_score:
best_score = score
best = (rot, x)
return best
@staticmethod
def evaluate(grid):
mat = np.array(grid)
heights = []
for col in range(COLS):
col_data = mat[:, col]
nz = np.where(col_data != 0)[0]
heights.append(ROWS - nz[0] if len(nz) else 0)
agg_height = sum(heights)
lines = sum(1 for row in grid if 0 not in row)
holes = 0
for col in range(COLS):
block = False
for row in range(ROWS):
if grid[row][col]:
block = True
elif block:
holes += 1
bump = sum(abs(heights[i] - heights[i + 1]) for i in range(COLS - 1))
score = (AIPlayer.WEIGHTS['height'] * agg_height +
AIPlayer.WEIGHTS['lines'] * lines +
AIPlayer.WEIGHTS['holes'] * holes +
AIPlayer.WEIGHTS['bump'] * bump)
return score
# -------------------- 渲染 --------------------
def draw(board, is_ai_mode):
screen.fill((0, 0, 0))
# 创建中文字体
font_large = get_chinese_font(24)
font_small = get_chinese_font(18)
# 显示分数
score_surf = font_large.render(f"分数: {board.score}", True, (255, 255, 255))
screen.blit(score_surf, (10, 10))
# 显示当前模式和操作提示
mode_text = f"当前模式: {'AI自动' if is_ai_mode else '人工操作'}"
mode_surf = font_small.render(mode_text, True, (255, 255, 255))
screen.blit(mode_surf, (10, 40))
# 显示切换提示
hint_surf = font_small.render("按F1切换模式 | 方向键移动 | 空格直接落下", True, (200, 200, 200))
screen.blit(hint_surf, (10, 65))
# 绘制网格
for y in range(ROWS):
for x in range(COLS):
val = board.grid[y][x]
if val:
pygame.draw.rect(screen, COLORS[val],
(x * CELL, y * CELL + 90, CELL - 1, CELL - 1))
# 绘制当前方块
if board.current:
for (px, py) in board.current['shape']:
x = board.current['x'] + px
y = board.current['y'] + py
pygame.draw.rect(screen, COLORS[board.current['color']],
(x * CELL, y * CELL + 90, CELL - 1, CELL - 1))
pygame.display.flip()
# -------------------- 主循环 --------------------
def main():
board = Board()
ai = AIPlayer()
action_timer = 0
is_ai_mode = False # 初始为人工模式
while not board.game_over:
dt = clock.tick(FPS)
action_timer += dt
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
# 按F1切换模式
if event.key == pygame.K_F1:
is_ai_mode = not is_ai_mode
# 仅在人工模式下响应方向键
if not is_ai_mode:
if event.key == pygame.K_LEFT:
board.move(-1)
if event.key == pygame.K_RIGHT:
board.move(1)
if event.key == pygame.K_UP:
board.turn()
if event.key == pygame.K_DOWN:
board.soft_drop()
if event.key == pygame.K_SPACE:
board.hard_drop()
# AI模式下自动决策
if is_ai_mode:
if action_timer >= 50: # AI每50ms决策一次
rot, x = ai.get_possible_moves(board)
if rot > 0:
board.turn()
rot -= 1
elif board.current['x'] < x:
board.move(1)
elif board.current['x'] > x:
board.move(-1)
else:
board.hard_drop()
action_timer = 0
# 自动下落逻辑
board.step(dt)
# 绘制游戏画面
draw(board, is_ai_mode)
# 游戏结束
print("游戏结束!最终分数:", board.score)
pygame.quit()
if __name__ == '__main__':
main()