一个经典的滑块拼图游戏的完整实现指南
数字华容道是一款经典的滑块拼图游戏,玩家需要通过移动数字方块,将它们按顺序排列。本教程将详细介绍如何使用 Python 的 turtle 图形库实现这个游戏。
目标状态:将数字按顺序排列,空白块位于右下角
turtle 库是 Python 中一个简单易用的图形绘制库,特别适合初学者学习编程和实现简单的图形界面游戏。下面我们将介绍一些基本的 turtle 功能:
from turtle import *
# 设置画布大小和位置
setup(400, 400, 300, 300)
# 隐藏海龟图标
hideturtle()
# 移动海龟到指定坐标
goto(100, 100)
# 绘制线条
pendown() # 放下画笔,开始绘制
forward(100) # 向前移动100个单位
left(90) # 左转90度
forward(100)
# 抬起画笔,停止绘制
penup()
# 填充颜色
color('black', 'red') # 设置边框颜色为黑色,填充颜色为红色
begin_fill() # 开始填充
# 绘制一个正方形
for _ in range(4):
forward(100)
left(90)
end_fill() # 结束填充
# 更新屏幕显示
update()
# 保持窗口打开
done()
我们使用字典来存储每个格子的状态,以及定义合法的移动方向:
from freegames import vector
tiles = {} # 存储格子坐标和对应数字
neighbors = [
vector(100, 0), # 右
vector(-100, 0), # 左
vector(0, 100), # 上
vector(0, -100), # 下
] # 定义相邻格子的向量
创建初始游戏板并随机打乱数字:
def load():
"""加载并打乱数字方块"""
count = 1 # 数字从1开始
# 生成4×4的网格,每个格子间隔100像素
for y in range(-200, 200, 100):
for x in range(-200, 200, 100):
mark = vector(x, y) # 当前格子坐标
tiles[mark] = count # 存储数字
count += 1
# 将最后一个格子设为空白
tiles[mark] = None
# 随机移动1000次,打乱数字顺序
for _ in range(1000):
neighbor = choice(neighbors) # 随机选择一个方向
spot = mark + neighbor # 计算相邻位置
# 如果相邻位置有效,则交换数字
if spot in tiles:
number = tiles[spot]
tiles[spot] = None
tiles[mark] = number
mark = spot
使用 turtle 绘制每个数字方块:
def square(mark, number):
"""绘制带数字的白色方块"""
penup() # 抬起画笔,移动时不绘制
goto(mark.x, mark.y) # 移动到指定坐标
pendown() # 放下画笔,开始绘制
# 设置边框和填充颜色
color('black', 'white')
begin_fill() # 开始填充
# 绘制正方形
for _ in range(4):
forward(99) # 边长99像素,留出边框空间
left(90)
end_fill() # 结束填充
# 如果不是空白格子,绘制数字
if number is None:
return
# 调整数字位置使其居中
if number < 10:
forward(20) # 个位数向右偏移一些
# 绘制数字,设置字体和大小
write(number, font=('Arial', 60, 'normal'))
响应玩家的点击并移动方块:
def tap(x, y):
"""处理鼠标点击,交换方块和空白位置"""
# 将点击坐标对齐到最近的100×100网格
x = floor(x, 100)
y = floor(y, 100)
mark = vector(x, y)
# 检查点击位置的相邻格子
for neighbor in neighbors:
spot = mark + neighbor # 相邻位置坐标
# 如果相邻位置是空白格子,则交换它们
if spot in tiles and tiles[spot] is None:
number = tiles[mark]
tiles[spot] = number
square(spot, number) # 绘制新位置的方块
tiles[mark] = None
square(mark, None) # 绘制原位置的空白
break
设置游戏窗口并启动主循环:
def draw():
"""绘制所有方块"""
clear() # 清除画布
for mark in tiles:
square(mark, tiles[mark]) # 绘制每个方块
update() # 更新屏幕显示
# 设置游戏窗口
setup(420, 420, 370, 0) # 窗口大小和位置
hideturtle() # 隐藏海龟图标
tracer(False) # 关闭自动刷新,手动控制屏幕更新
load() # 初始化游戏板
draw() # 绘制游戏界面
onscreenclick(tap) # 绑定鼠标点击事件
done() # 保持窗口打开
from random import *
from turtle import *
from freegames import floor, vector
tiles = {}
neighbors = [
vector(100, 0),
vector(-100, 0),
vector(0, 100),
vector(0, -100),
]
def load():
"""Load tiles and scramble."""
count = 1
for y in range(-200, 200, 100):
for x in range(-200, 200, 100):
mark = vector(x, y)
tiles[mark] = count
count += 1
tiles[mark] = None
for count in range(1000):
neighbor = choice(neighbors)
spot = mark + neighbor
if spot in tiles:
number = tiles[spot]
tiles[spot] = None
tiles[mark] = number
mark = spot
def square(mark, number):
"""Draw white square with black outline and number."""
up()
goto(mark.x, mark.y)
down()
color('black', 'white')
begin_fill()
for count in range(4):
forward(99)
left(90)
end_fill()
if number is None:
return
elif number < 10:
forward(20)
write(number, font=('Arial', 60, 'normal'))
def tap(x, y):
"""Swap tile and empty square."""
x = floor(x, 100)
y = floor(y, 100)
mark = vector(x, y)
for neighbor in neighbors:
spot = mark + neighbor
if spot in tiles and tiles[spot] is None:
number = tiles[mark]
tiles[spot] = number
square(spot, number)
tiles[mark] = None
square(mark, None)
def draw():
"""Draw all tiles."""
for mark in tiles:
square(mark, tiles[mark])
update()
setup(420, 420, 370, 0)
hideturtle()
tracer(False)
load()
draw()
onscreenclick(tap)
done()