温馨提示
本教程源码素材已更新至公众号菜单栏【源码素材】
如果文章对您有一点点帮助,点
、点
,万分感谢
今天带大家用 Python 的 turtle 模块画一幅中国象棋棋盘。
做完之后,屏幕上会出现一个带粗细边框的棋盘,9 列 10 行的格子线,中间留出楚河汉界的空白,上下各画九宫斜线,最后写上"楚河 汉界"四个字,再把 32 颗棋子按标准开局位置全部摆好。
这个项目看着复杂,拆开就五步:画框、画线、画斜线、写字、摆棋子。每一步都不长,就是循环嵌套多了一层。
准备工作
打开 Python,新建一个文件,先把 turtle 模块导进来。
整个程序会反复用到"移动画笔到某个坐标"这个操作,每次写三行太啰嗦了,我们封装一个函数:
from turtle import*
defgo(x, y):
penup()
goto(x, y)
pendown()
go(x, y) 干的事很简单:抬笔,移到指定坐标,落笔。每次画新东西之前调一下就行。
然后设一下背景和速度:
bgcolor("Wheat3")
speed(0)
tracer(False)背景用小麦色,看着舒服。speed(0) 是最快速度,tracer(False) 关掉动画追踪——这个一定要加,不然 32 颗棋子的圆一个一个慢慢画,等到下课都画不完。
最后定义三个常量:
B = 50
X = -B*4
Y = 4*B+0.5*B
B 是每个格子的边长 50 像素。X 和 Y 是棋盘左上角的坐标,X 设成负数是为了让棋盘居中在屏幕上,不然整盘棋会偏到右边。
第一步:画外框
棋盘有两层边框,外面一层粗线,里面一层细线。
粗外框:
pensize(5)go(X - 10, Y + 10)for i in range(2): forward(8 * B + 20) right(90) forward(9 * B + 20) right(90)
细内框:
x, y = X, Ypensize(2)go(x, y)for i in range(2): forward(8 * B) right(90) forward(9 * B) right(90)
粗框比细框每边宽出 10 像素(8B+20 vs 8B),不是简单地把线加粗,而是另外画了一个更大的矩形套在外面。注意这里用了 x, y = X, Y 复制了一份坐标,这样画细框的时候不会把 X、Y 的值搞乱,后面还要用。
到这里可以跑一下看看,应该能看到两层矩形框。
第二步:画格子线
这是整个项目最费脑子的地方。
棋盘一共 9 行横线区域(前 5 行在上半边,后 4 行在下半边),中间楚河汉界那一行不画线。每一行由 8 个格子组成,每个格子是一个正方形。
for k in range(9): if k == 4: y -= B go(x, y) else: for j in range(8): for i in range(4): forward(B) right(90) forward(B) y -= B go(x, y)
逐行解释:
外层 for k in range(9) 控制画 9 行区域
if k == 4 是关键:第 5 个区域(索引从 0 开始所以是 4)是楚河汉界,只向下移一格,不画任何东西
中层 for j in range(8) 每行画 8 个格子
内层 for i in range(4) 画一个正方形的 4 条边:前进 B,右转 90 度,循环 4 次
画完一个格子之后,forward(B) 向右平移到下一个格子的起点
有个地方容易搞混:内层循环画完 4 条边,方向转了一整圈回到原方向,所以后面的 forward(B) 是向右走而不是向上。在纸上画个箭头试试就清楚了。
跑一下看看,棋盘的格子线应该出来了,中间楚河汉界留了空白。
第三步:画九宫斜线
上下两方各有一个九宫格,画两条对角线就行:
# 上方九宫go(-B, Y)goto(B, Y - 2 * B)go(B, Y)goto(-B, Y - 2 * B)# 下方九宫go(-B, -Y)goto(B, -Y + 2 * B)go(B, -Y)goto(-B, -Y + 2 * B)
上方从 Y 坐标开始,下方从 -Y 开始,关于原点对称。每次 go() 起笔,goto() 落笔画线(因为 go 最后一步是 pendown)。
第四步:写"楚河汉界"
go(-B*2.5, -20)
write("楚 河 汉 界", font=("楷体", 28))
就一行,但坐标要调。(-B*2.5, -20) 这个位置是我试了好几遍才找到的居中点,你可能要根据自己的屏幕尺寸微调一下。编程里这种"调参"很常见,不用一次就对。
第五步:摆放棋子
先定义一个二维列表,把所有棋子的初始位置存进去:
list1 = [
["車","马","相","仕","帥","仕","相","马","車"],
["","","","","","","","",""],
["","炮","","","","","","炮",""],
["兵","","兵","","兵","","兵","","兵"],
["","","","","","","","",""],
["","","","","","","","",""],
["卒","","卒","","卒","","卒","","卒"],
["","炮","","","","","","炮",""],
["","","","","","","","",""],
["車","马","象","士","将","士","象","马","車"],
]
10 行 9 列,有棋子的位置写名字,空位用空字符串。注意红方是"車马相仕帥",黑方是"車马象士将"——同样的棋子位置,名字不一样,这是中国象棋的传统。
然后用双重循环把棋子画出来:
pencolor("red")x, y = X - 15, Y - 15for i in range(len(list1)): for j in range(len(list1[i])): go(x, y) s = list1[i][j] if s: go(x + 13, y - 3) color("white") begin_fill() circle(18) end_fill() go(x, y) if i == 5: pencolor("blue") else: pencolor("red") write(s, font=("楷体", 22)) x += B x = X - 15 y -= B
逻辑是:逐行逐列扫描列表,遇到空字符串就跳过,遇到棋子名字就画一个白色实心圆当底,然后在上面写棋子的名字。
begin_fill() 和 end_fill() 配合 circle(18) 画出一个半径 18 的白色实心圆。go(x + 13, y - 3) 是微调坐标,让圆心对齐格子中心,这个偏移量也是试出来的。
有个小细节:代码里写了 if i == 5: pencolor("blue"),但第 5 行和第 6 行全是空字符串,这个分支永远不会执行。其实是原作者留的一个小 bug。想修正的话,改成这样就行:
if i < 5: pencolor("red")else: pencolor("black")
这样红方显示红色字,黑方显示黑色字,就对了。
最后别忘了:
tracer(True)
done()
done() 让窗口保持不关闭,不然画完一闪就没了。
完整代码
把上面五步拼在一起就是完整的程序。整体结构很清晰:
模块导入 → 环境设置 → 画外框 → 画格子线 → 画九宫斜线 → 写字 → 摆棋子
每一块独立,调好了再加下一块。如果中间哪一步出了问题,就单独跑那一段来排查。
改完之后试试这些
程序跑通之后,改几个参数玩玩,比抄代码有意思多了:
把 B 从 50 改成 30,棋盘瞬间缩小,看看格子变密是什么感觉
把 bgcolor("Wheat3") 改成 bgpic("背景.jpg"),换成一张木纹背景图,马上就有实体棋盘的质感了
修正那个颜色 bug,让黑方棋子显示黑色
把"楚河 汉界"改成你自己想写的字,比如班级名或者你的名字
改一个数字就能看到效果变化,编程的乐趣很大一部分在这种地方。
常见问题
棋盘歪了 / 格子不对齐 —— 大概率是 X、Y 的常量定义算错了,或者 forward 和 right 的顺序搞反了。用 print 把坐标打出来对比一下。
楚河汉界多了一条线 —— if k == 4 写错了或者缩进不对。仔细检查 if 和 else 的缩进层级。
棋子不显示 —— 先确认字体写的是"楷体"(有的电脑没装这个字体会显示不出来),再检查 write 的坐标是不是跑到了画布外面。
程序跑得太慢 —— 检查有没有加 tracer(False)。没加的话,画笔会一格一格慢慢挪。
圆变成了椭圆 —— turtle 的 circle() 圆心在画笔位置的左侧,坐标没调好的话圆会歪。调整 go(x + 13, y - 3) 里的偏移量试试。
整个程序写下来不到 120 行,用到的知识点其实就这几个:函数定义、for 循环嵌套、if 条件判断、二维列表、turtle 绑图指令。难点在格子线那三重循环的嵌套逻辑,搞明白之后后面的代码都是顺理成章的事。
觉得有用的话帮忙点个赞点个爱心转发一下,你们的支持是我更新的动力。