import pygame
import sys
import random
import time
from queue import PriorityQueue
from pygame.locals import *
# 初始化pygame
pygame.init()
# 游戏常量
WIDTH, HEIGHT = 400, 500
GRID_SIZE = 3
TILE_SIZE = 100
MARGIN = 5
FPS = 60
# 颜色定义
BACKGROUND = (240, 240, 240)
GRID_COLOR = (200, 200, 200)
TILE_COLOR = (70, 130, 180)
TEXT_COLOR = (255, 255, 255)
HIGHLIGHT_COLOR = (100, 160, 210)
INFO_COLOR = (50, 50, 50)
# 中文数字
CHINESE_NUMBERS = ["1", "2", "3", "4", "5", "6", "7", "8", ""]
# 创建游戏窗口
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("数字拼图游戏")
clock = pygame.time.Clock()
# 字体
font = pygame.font.SysFont('simhei', 24)
small_font = pygame.font.SysFont('simhei', 18)
class PuzzleGame:
def __init__(self):
self.reset_game()
self.mode = "manual" # "manual" 或 "ai"
self.ai_speed = 0.3 # AI移动速度(秒)
self.last_ai_move = 0
self.moving_tile = None
self.move_start_pos = None
self.move_target_pos = None
self.move_start_time = 0
self.move_duration = 0.2 # 移动动画持续时间(秒)
def reset_game(self):
# 创建初始拼图状态(已解决状态)
self.board = [[1, 2, 3], [4, 5, 6], [7, 8, 0]]
self.empty_pos = (2, 2) # 空白块位置
# 随机打乱拼图
self.shuffle_board(50)
self.moves = 0
self.start_time = time.time()
self.ai_solving = False
self.solution_path = []
self.current_step = 0
self.moving_tile = None # 重置移动动画状态
def shuffle_board(self, moves=50):
# 通过随机移动来打乱拼图
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] # 右, 下, 左, 上
# 先重置到完成状态
self.board = [[1, 2, 3], [4, 5, 6], [7, 8, 0]]
self.empty_pos = (2, 2)
for _ in range(moves):
valid_moves = []
r, c = self.empty_pos
for dr, dc in directions:
nr, nc = r + dr, c + dc
if 0 <= nr < GRID_SIZE and 0 <= nc < GRID_SIZE:
valid_moves.append((dr, dc))
if valid_moves:
dr, dc = random.choice(valid_moves)
self.swap_tiles(r + dr, c + dc, r, c) # 交换空白块和相邻块
def swap_tiles(self, r1, c1, r2, c2):
# 交换两个拼图块
self.board[r1][c1], self.board[r2][c2] = self.board[r2][c2], self.board[r1][c1]
self.empty_pos = (r1, c1) # 更新空白块位置
def move_tile(self, r, c):
# 检查是否可以移动拼图块(只能移动与空白块相邻的块)
er, ec = self.empty_pos
if (abs(r - er) == 1 and c == ec) or (abs(c - ec) == 1 and r == er):
# 设置移动动画
self.moving_tile = (r, c)
self.move_start_pos = (c * (TILE_SIZE + MARGIN) + MARGIN,
r * (TILE_SIZE + MARGIN) + MARGIN)
self.move_target_pos = (ec * (TILE_SIZE + MARGIN) + MARGIN,
er * (TILE_SIZE + MARGIN) + MARGIN)
self.move_start_time = time.time()
# 实际交换拼图块
self.swap_tiles(r, c, er, ec)
self.moves += 1
return True
return False
def is_solved(self):
# 检查拼图是否已解决
solved = [[1, 2, 3], [4, 5, 6], [7, 8, 0]]
return self.board == solved
def draw(self, screen):
# 绘制背景
screen.fill(BACKGROUND)
# 绘制拼图网格
for r in range(GRID_SIZE):
for c in range(GRID_SIZE):
rect = pygame.Rect(
c * (TILE_SIZE + MARGIN) + MARGIN,
r * (TILE_SIZE + MARGIN) + MARGIN,
TILE_SIZE,
TILE_SIZE
)
# 跳过空白块(不绘制)
if self.board[r][c] == 0:
pygame.draw.rect(screen, GRID_COLOR, rect)
continue
# 绘制拼图块
pygame.draw.rect(screen, TILE_COLOR, rect, border_radius=5)
# 绘制数字
number_text = CHINESE_NUMBERS[self.board[r][c] - 1]
text_surface = font.render(number_text, True, TEXT_COLOR)
text_rect = text_surface.get_rect(center=rect.center)
screen.blit(text_surface, text_rect)
# 绘制移动动画
if self.moving_tile and time.time() - self.move_start_time < self.move_duration:
progress = (time.time() - self.move_start_time) / self.move_duration
progress = min(1.0, progress)
# 计算当前位置
current_x = self.move_start_pos[0] + (self.move_target_pos[0] - self.move_start_pos[0]) * progress
current_y = self.move_start_pos[1] + (self.move_target_pos[1] - self.move_start_pos[1]) * progress
# 绘制移动中的拼图块
rect = pygame.Rect(current_x, current_y, TILE_SIZE, TILE_SIZE)
pygame.draw.rect(screen, HIGHLIGHT_COLOR, rect, border_radius=5)
# 绘制数字(使用移动前的数字)
r, c = self.moving_tile
number_value = self.board[self.empty_pos[0]][self.empty_pos[1]] # 移动后的空白块位置是原来的数字
number_text = CHINESE_NUMBERS[number_value - 1]
text_surface = font.render(number_text, True, TEXT_COLOR)
text_rect = text_surface.get_rect(center=rect.center)
screen.blit(text_surface, text_rect)
else:
self.moving_tile = None
# 绘制游戏信息
elapsed_time = time.time() - self.start_time
time_text = f"时间: {int(elapsed_time)}秒"
moves_text = f"移动步数: {self.moves}"
mode_text = f"模式: {'人工操作' if self.mode == 'manual' else 'AI自动'}"
help_text = "F1 切换模式 | F2 重置游戏"
time_surface = small_font.render(time_text, True, INFO_COLOR)
moves_surface = small_font.render(moves_text, True, INFO_COLOR)
mode_surface = small_font.render(mode_text, True, INFO_COLOR)
help_surface = small_font.render(help_text, True, INFO_COLOR)
screen.blit(time_surface, (10, HEIGHT - 120))
screen.blit(moves_surface, (10, HEIGHT - 90))
screen.blit(mode_surface, (10, HEIGHT - 60))
screen.blit(help_surface, (10, HEIGHT - 30))
# 如果AI正在解谜,显示进度
if self.ai_solving and self.solution_path:
progress_text = f"AI解谜进度: {self.current_step}/{len(self.solution_path)}"
progress_surface = small_font.render(progress_text, True, INFO_COLOR)
screen.blit(progress_surface, (WIDTH // 2 - 60, HEIGHT - 60))
def handle_key(self, key):
if key == K_F1:
self.mode = "ai" if self.mode == "manual" else "manual"
print(f"切换到{self.mode}模式")
if self.mode == "ai" and not self.ai_solving:
self.start_ai_solve()
elif key == K_F2:
print("重置游戏")
self.reset_game()
def handle_click(self, pos):
# 检查是否点击了拼图块
x, y = pos
if y < GRID_SIZE * (TILE_SIZE + MARGIN) + MARGIN:
c = x // (TILE_SIZE + MARGIN)
r = y // (TILE_SIZE + MARGIN)
if 0 <= r < GRID_SIZE and 0 <= c < GRID_SIZE and self.board[r][c] != 0:
self.move_tile(r, c)
def start_ai_solve(self):
# 开始AI解谜
self.ai_solving = True
self.solution_path = self.solve_puzzle()
self.current_step = 0
self.last_ai_move = time.time()
if self.solution_path:
print(f"AI找到解决方案,共{len(self.solution_path)}步")
else:
print("AI未找到解决方案")
self.ai_solving = False
def solve_puzzle(self):
# 使用A*算法解决拼图
goal_state = ((1, 2, 3), (4, 5, 6), (7, 8, 0))
# 将当前状态转换为元组
start_state = tuple(tuple(row) for row in self.board)
# 定义启发式函数(曼哈顿距离)
def heuristic(state):
distance = 0
for r in range(GRID_SIZE):
for c in range(GRID_SIZE):
if state[r][c] != 0:
# 目标位置
target_r = (state[r][c] - 1) // GRID_SIZE
target_c = (state[r][c] - 1) % GRID_SIZE
distance += abs(r - target_r) + abs(c - target_c)
return distance
# 定义移动方向
directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]
# 初始化优先队列
open_set = PriorityQueue()
open_set.put((heuristic(start_state), 0, start_state, [])) # (f, g, state, path)
# 记录访问过的状态
visited = set()
visited.add(start_state)
while not open_set.empty():
f, g, current, path = open_set.get()
if current == goal_state:
return path # 返回移动路径
# 找到空白块的位置
empty_r, empty_c = 0, 0
for r in range(GRID_SIZE):
for c in range(GRID_SIZE):
if current[r][c] == 0:
empty_r, empty_c = r, c
break
# 生成可能的移动
for dr, dc in directions:
nr, nc = empty_r + dr, empty_c + dc
if 0 <= nr < GRID_SIZE and 0 <= nc < GRID_SIZE:
# 创建新状态
new_state = [list(row) for row in current]
new_state[empty_r][empty_c], new_state[nr][nc] = new_state[nr][nc], new_state[empty_r][empty_c]
new_state = tuple(tuple(row) for row in new_state)
if new_state not in visited:
visited.add(new_state)
new_g = g + 1
new_f = new_g + heuristic(new_state)
new_path = path + [(nr, nc)] # 记录移动的拼图块位置
open_set.put((new_f, new_g, new_state, new_path))
return [] # 无解
def ai_move(self):
# AI自动移动
current_time = time.time()
if current_time - self.last_ai_move >= self.ai_speed and self.solution_path:
if self.current_step < len(self.solution_path):
# 获取下一步移动
r, c = self.solution_path[self.current_step]
self.move_tile(r, c)
self.current_step += 1
self.last_ai_move = current_time
print(f"AI移动第{self.current_step}步")
# 检查是否完成
if self.current_step >= len(self.solution_path):
self.ai_solving = False
print("AI完成拼图!")
# 创建游戏实例
game = PuzzleGame()
# 主游戏循环
running = True
while running:
for event in pygame.event.get():
if event.type == QUIT:
running = False
elif event.type == KEYDOWN:
game.handle_key(event.key)
elif event.type == MOUSEBUTTONDOWN and event.button == 1:
if game.mode == "manual":
game.handle_click(event.pos)
# AI自动移动
if game.mode == "ai" and game.ai_solving:
game.ai_move()
# 绘制游戏
game.draw(screen)
# 检查是否完成
if game.is_solved():
solved_text = font.render("拼图完成!", True, (0, 150, 0))
text_rect = solved_text.get_rect(center=(WIDTH // 2, HEIGHT - 150))
screen.blit(solved_text, text_rect)
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
sys.exit()