编程启蒙|Scratch 转 Python 系列第 12 天:记忆翻牌游戏实战(AI 图案主题包 + 存档排行榜)
一、本篇摘要
Day 11 我们学会了让 Python “记住昨天”——把数据存进文件。今天就用这个能力做一款经典的记忆翻牌游戏——关掉游戏,最高纪录依然在。
本篇是串联复习大合集:Day 6 列表画卡牌矩阵、Day 9 字典管 AI 图案主题包、Day 11 文件存排行榜。学完这一篇,孩子第一次感受到"多个知识点合作做一个真项目"的乐趣。
二、本节课学习目标
- 用二维列表表示卡牌矩阵,理解"游戏地图"的编程本质。
- 用字典管理 AI 生成的多套图案主题包(动物/水果/交通工具/
emoji),可自由切换。 - 用文件读写实现排行榜持久化,理解"游戏存档"的最小可行方案。
- 掌握
random.shuffle()洗牌技巧,理解"程序随机性"是所有小游戏的灵魂。
三、专业名词通俗释义
| 专业名词 | 通俗解释 |
|---|---|
| 二维列表 | 列表里套列表,用来表示"行 × 列"的表格/地图 |
| 洗牌 | 打乱列表顺序的操作,Python 用 random.shuffle(list) |
| 翻牌状态 | 每张卡有三种状态:未翻 / 已翻开 / 已配对 |
| 主题包 | 一组同类图案的集合,用字典 {主题名: [图案列表]} 组织 |
| 排行榜 | 记录玩家最好成绩的表格文件(CSV) |
四、分模块双向对照
模块 1:卡牌矩阵——用二维列表画游戏地图

# 【Scratch积木】新建列表 [board],用一个大列表装 4 个小列表
# 【Scratch积木】每个小列表代表一行 4 张卡
board = [
["🐶", "🐱", "🐰", "🐼"],
["🐶", "🐱", "🐰", "🐼"],
["🦁", "🐸", "🐵", "🐷"],
["🦁", "🐸", "🐵", "🐷"],
]
# 访问某一张卡:board[行][列]
print(board[0][2]) # 🐰
核心易错说明:二维列表访问必须双方括号 board[row][col];row 和 col 都从 0 开始数。
模块 2:AI 图案主题包——字典分类管理
# 【Scratch积木】新建字典 [themes],键是主题名,值是图案列表
themes = {
"动物": ["🐶", "🐱", "🐰", "🐼", "🦁", "🐸", "🐵", "🐷"],
"水果": ["🍎", "🍌", "🍇", "🍉", "🍓", "🍑", "🍒", "🥝"],
"交通": ["🚗", "🚕", "🚙", "🚌", "🚑", "🚒", "🚚", "✈️"],
"emoji": ["😀", "😎", "🥳", "🤖", "👻", "🎃", "🎁", "⭐"],
}
# 【Scratch积木】询问 "选择主题" 并等待,从字典中取出对应列表
theme_name = input("选择主题(动物/水果/交通/emoji):")
icons = themes.get(theme_name, themes["动物"])
核心易错说明:.get(key, default) 比 dict[key] 安全——键不存在时返回默认值而不是报错。
模块 3:洗牌——random.shuffle 实战
import random
# 【Scratch积木】将列表 [cards] 打乱顺序
cards = ["🐶", "🐶", "🐱", "🐱", "🐰", "🐰", "🐼", "🐼"]
random.shuffle(cards) # 原地打乱,不返回新列表
print(cards) # 每次运行结果都不同
核心易错说明:random.shuffle() 是原地修改,不像 sorted() 返回新列表;x = random.shuffle(cards) 得到的是 None,是新手大坑。
模块 4:翻牌与配对逻辑

# 【Scratch积木】定义积木 flip(row, col) → 翻开某张卡
# 【Scratch积木】如果 两张卡相同 → 保持翻开,否则盖回
def flip_and_check(board, revealed, r1, c1, r2, c2):
if board[r1][c1] == board[r2][c2]:
revealed[r1][c1] = True
revealed[r2][c2] = True
return True # 配对成功
return False # 配对失败
模块 5:排行榜持久化——CSV 读写(复习 Day 11)

import csv, os
RANK_FILE = "memory_rank.csv"
# 【Scratch积木】读取排行榜文件到列表
def load_rank():
if not os.path.exists(RANK_FILE):
return []
with open(RANK_FILE, "r", encoding="utf-8") as f:
reader = csv.reader(f)
next(reader, None)
return list(reader)
# 【Scratch积木】追加一条成绩
def save_rank(name, flips, theme):
exists = os.path.exists(RANK_FILE)
with open(RANK_FILE, "a", encoding="utf-8", newline="") as f:
writer = csv.writer(f)
if not exists:
writer.writerow(["玩家", "翻牌次数", "主题"])
writer.writerow([name, flips, theme])
五、综合实战项目:4×4 记忆翻牌完整游戏
游戏规则:4×4 共 16 张卡(8 对图案),每回合输入两张卡坐标翻开。相同保持翻开,不同盖回。全部配对后记录翻牌次数入排行榜。
Scratch 积木流程:
- 选择主题 → 从字典取图案 →
random.shuffle洗牌 - 展开成 4×4 二维列表
board - 建立同尺寸的
revealed状态矩阵(全 False) - 重复执行直到 全部配对:
- 打印当前牌面(配对的显示图案,未配对显示❓)
- 询问两张卡坐标 → 翻开对比 → 更新 revealed
- 翻牌次数 +1
- 记录成绩 → 显示排行榜

Python 完整可运行源码:
import random
import csv
import os
THEMES = {
"动物": ["🐶", "🐱", "🐰", "🐼", "🦁", "🐸", "🐵", "🐷"],
"水果": ["🍎", "🍌", "🍇", "🍉", "🍓", "🍑", "🍒", "🥝"],
"交通": ["🚗", "🚕", "🚙", "🚌", "🚑", "🚒", "🚚", "✈️"],
"emoji": ["😀", "😎", "🥳", "🤖", "👻", "🎃", "🎁", "⭐"],
}
RANK_FILE = "memory_rank.csv"
# 【Scratch积木】定义积木 build_board(theme) → 返回 4×4 洗牌后的二维列表
def build_board(theme):
icons = THEMES[theme][:]
cards = icons + icons # 每个图案配一对
random.shuffle(cards)
return [cards[i * 4:(i + 1) * 4] for i in range(4)]
# 【Scratch积木】定义积木 print_board(board, revealed)
def print_board(board, revealed):
print("\n 0 1 2 3")
for r in range(4):
row_str = f"{r} "
for c in range(4):
row_str += f"[{board[r][c] if revealed[r][c] else '❓'}] "
print(row_str)
# 【Scratch积木】定义积木 ask_coord(prompt) → 返回 (row, col)
def ask_coord(prompt):
while True:
raw = input(prompt).strip().split()
if len(raw) == 2 and all(x.isdigit() for x in raw):
r, c = int(raw[0]), int(raw[1])
if 0 <= r < 4 and 0 <= c < 4:
return r, c
print("⚠️ 请输入两个 0-3 的数字,用空格分隔,例如:1 2")
# 【Scratch积木】定义积木 save_rank(name, flips, theme)
def save_rank(name, flips, theme):
exists = os.path.exists(RANK_FILE)
with open(RANK_FILE, "a", encoding="utf-8", newline="") as f:
writer = csv.writer(f)
if not exists:
writer.writerow(["玩家", "翻牌次数", "主题"])
writer.writerow([name, flips, theme])
# 【Scratch积木】定义积木 show_rank()
def show_rank():
if not os.path.exists(RANK_FILE):
print("📭 暂无排行榜")
return
with open(RANK_FILE, "r", encoding="utf-8") as f:
reader = csv.reader(f)
next(reader, None)
rows = [row for row in reader]
# 【Scratch积木】按翻牌次数升序排序(次数越少越靠前)
rows.sort(key=lambda x: int(x[1]))
print("\n" + "=" * 30)
print(" 🏆 记忆翻牌排行榜")
print("=" * 30)
for i, (name, flips, theme) in enumerate(rows[:10], 1):
print(f" {i}. {name} {flips} 次 [{theme}]")
print("=" * 30)
# 【Scratch积木】主循环:一局游戏
def play_one_round():
print("\n可选主题:", "、".join(THEMES.keys()))
theme = input("选择图案主题:").strip()
if theme not in THEMES:
theme = "动物"
print("默认使用「动物」主题")
board = build_board(theme)
revealed = [[False] * 4 for _ in range(4)]
flips = 0
pairs_found = 0
while pairs_found < 8:
print_board(board, revealed)
print(f"🎯 已配对 {pairs_found}/8 | 翻牌 {flips} 次")
r1, c1 = ask_coord("翻开第 1 张(行 列):")
if revealed[r1][c1]:
print("⚠️ 这张卡已经配对啦,换一张")
continue
r2, c2 = ask_coord("翻开第 2 张(行 列):")
if (r2, c2) == (r1, c1) or revealed[r2][c2]:
print("⚠️ 两张卡不能相同或已配对")
continue
# 【Scratch积木】临时翻开两张卡展示给玩家看
print(f"翻开:[{board[r1][c1]}] 和 [{board[r2][c2]}]")
flips += 1
if board[r1][c1] == board[r2][c2]:
print("🎉 配对成功!")
revealed[r1][c1] = True
revealed[r2][c2] = True
pairs_found += 1
else:
print("❌ 不匹配,再想想")
# 全部配对
print_board(board, revealed)
print(f"\n🏆 恭喜通关!共翻牌 {flips} 次")
name = input("请输入你的昵称:").strip() or "匿名玩家"
save_rank(name, flips, theme)
show_rank()
# 【Scratch积木】游戏总入口
def main():
print("=" * 40)
print(" 🎴 记忆翻牌游戏 · AI 主题包版")
print("=" * 40)
while True:
play_one_round()
again = input("\n再来一局?(y/n):")
if again.lower() != "y":
print("👋 下次见!")
break
if __name__ == "__main__":
main()
运行方式:保存为 day12_code.py → 命令行 python day12_code.py → 按提示输入两张卡的行列坐标。
💡 三大数据结构会师:卡牌矩阵是二维列表、主题包是字典、排行榜是文件——一款翻牌游戏把 Day 6/9/11 三个知识点全部串起来。这就是"综合项目"的味道——不再是学一个用一个,而是几个知识点合作解决一个真问题。
六、高频易错点总结
1. 二维列表初始化陷阱
# ❌ 错误:所有行共享同一个引用
board = [[False] * 4] * 4
board[0][0] = True # 结果所有行第 0 列都变 True
# ✅ 正确:每行独立创建
board = [[False] * 4 for _ in range(4)]
2. random.shuffle 返回值是 None
新手常写 cards = random.shuffle(cards),然后 cards 变成 None——shuffle 原地修改,不返回。
3. 字典取值用 .get() 更安全
themes[key] 键不存在直接崩溃;themes.get(key, default) 优雅兜底。
4. CSV 排序前要转数字
rows.sort(key=lambda x: x[1]) 是字符串排序,“10” 会排在 “2” 前面;必须 key=lambda x: int(x[1])。
八、💡 学习提示
为鼓励大家动手练习、吃透编程逻辑,本篇不提供 Scratch 成品源码,请对照截图亲手搭建积木完成复刻!
配套 Python 代码可直接复制运行学习。
九、往期历史笔记
-
编程启蒙|Scratch 转 Python 系列第 3 天:循环重复积木双向对照(AI 批量循环处理,已收录 AI Agent 技术社区)
-
编程启蒙|Scratch 转 Python 系列第 5 天:自定义积木 / 函数双向对照(AI 工具封装、重复指令简化实战)
-
编程启蒙|Scratch 转 Python 系列第 6 天:列表、数组积木双向对照(AI 底层语法 / AI 批量数据处理实战)
-
编程启蒙|Scratch 转 Python 系列第 7 天:猜数字大挑战·升级版实战(AI 出题 + 二分查找最优解 + 完整命令行游戏)
十、下一章预告
Scratch 转 Python 系列第 13 天:异常处理 try/except 积木双向对照(AI 接口调用容错实战)
当程序遇到"不听话"的输入或网络出错,如何优雅兜底?——try/except 会告诉你答案,这也是所有 AI API 调用的必备技能。
十一、原创声明
本文为「梅雅达编程笔记」原创启蒙教程,禁止私自搬运、二次转载,转发请标注来源。
想要系统学完整套 Scratch 转 Python 课程,欢迎订阅下方专栏持续追更。
更多推荐

所有评论(0)