决战三字经
游戏说明
《三字经》与《百家姓》、《千字文》并称为三大国学启蒙读物。《三字经》是中华民族珍贵的文化遗产,它短小精悍、琅琅上口,千百年来,家喻户晓。其内容涵盖了历史、天文、地理、道德以及一些民间传说,所谓“熟读《三字经》,可知千古事”。
刻在竹简上的《三字经》
《三字经》每三个字为一句,每四句为一段。《三字经》共1476字,492句,123段。
决战《三字经》游戏与俄罗斯方块游戏类似,三字一句组成一个矩形方块,该方块从画布的底部出现并向顶部移动,方块到达顶部或碰到其它方块则停止运动。方块中的文字次序随机排列,游戏运行时,按空格键可以调整文字的顺序,如果方块停止移动时,文字的顺序正确,玩家将得到5分。一段中的四句出现的位置和次序也是随机变化的,游戏运行时,按左右箭头键可以改变正在上升的文字块的水平位置。一行填满后,如果行中四句诗文左右位置正确且句内文字顺序正确,玩家将得到20分,同时游戏程序会清除该行。
当错误行数累计达到10行时,或者全部诗文播放完毕时,游戏结束,挑战者的能力用游戏得分的高低来衡量。
程序说明
点击添加Bookmark this page
本项目我们将尝试用Python语言设计开发一个计算机程序来以游戏的形式让玩家熟悉《三字经》。程序的文件名称为ThreeCharacterPrimer.py。程序将使用到以下Python语言的功能特性:
(1)Python保留词:
import、import…as、True、False、None、def、return、global、if/elif/else、and、or、class、for/in等。
(2)导入的模块:
import simpleguitk as simplegui
import random
import copy
(3)使用到的内置函数及模块中的函数:
print()、open()、close()、readlines()、split()、join()、random.randrange()
random.shuffle()、simplegui.create_frame()、simplegui.create_timer()、add_button()
add_label()、set_keydown_handler()、set_draw_handler()、get_canvas_textwidth()、
draw_text()、set_text()、draw_polygon()、copy.deepcopy()等。
(4)使用到的数据结构:
集合(set)、列表(list)等
(5)其它知识点和概念:
类和对象、事件驱动、全局变量、数学运算表达式、逻辑条件表达式、字符串运算表达式、自定义函数、代码格式、注释等Python语言的一些特性。
通过该项目你可以获得以下能力:
进一步理解计算机的工作原理
进一步熟悉事件驱动的程序设计方法
进一步理解面向对象的程序设计方法
熟练掌握利用simpleguitk模块开发小游戏的程序框架
掌握Python集合(set)、列表(list)数据结构的使用方法
掌握游戏逻辑到Python代码的转换过程
练习解决问题的步骤
基于simpleguitk模块的小游戏程序框架通常由全局变量的定义、辅助函数的定义、辅助类的定义、事件处理函数的定义、程序窗口的创建和初始化、事件处理函数的注册、游戏的启动等7部分组成。
编程步骤
第一步,首先编写read_from_file(filename)函数,参数filename为《三字经》文本文件的URL(Uniform Resource Locator,统一资源定位符,是对互联网资源的位置和访问方法的一种简洁表示),我们已经为你准备了该文件,URL为:http://202.201.225.74/video/PythonResoure/ProjectResource/other/szj.txt。你应当观察一下该文件的内容和格式,请使用urllib.request.urlopen()方法打开该文件,逐行处理(readlines()可以逐行读入文件,假设读入的内容保存在变量line中,可以用str(line.decode(“utf8”)).split()将一行分解为句子列表),每三个汉字为一个元素存入列表,处理完所有行之后,关闭文件,函数应当返回一个列表,可以用以下调用测试该函数是否满足要求:
print(read_from_file ('http://vquiz.xjau.edu.cn/ProjectResource/other/szj.txt ')[0])
输出应当为:人之初
print(read_from_file (' http://vquiz.xjau.edu.cn/ProjectResource/other/szj.txt ')[491])
输出应当为:宜勉力
text_list = read_from_file (' http://vquiz.xjau.edu.cn/ProjectResource/other/szj.txt')
for text in text_list:
print(text)
输出应当为492句诗文,1行1句
第二步,编写text_shuffle(text)函数,该函数以字符串text为参数,功能为返回以次序随机的新字符串,当然新字符串中的字符或汉字必须与text参数相同,可以用以下调用测试该函数是否满足要求:
print(text_shuffle (‘人之初’))
输出可能为:人之初、人初之、之初人、之人初、初之人、初人之
第三步,编写generate_current_section_list()函数,每次调用该函数时将从prime_sentence_list列表中取出4句诗词文本并把它们添加到一个新的列表中,同时将这4句诗词文本从prime_sentence_list列表中删除。为了实现一段诗歌中的4句诗词随机从画布底部飞出,我们要用random.shuffle函数对新产生的列表进行混洗处理,请在每句诗词前面加上表示其正确位置的数字代码(0、1、2、3),函数应当返回一个包含4个元素的列表。请用以下调用测试该函数是否满足要求:
prime_sentence_list = read_from_file(’ http://vquiz.xjau.edu.cn/ProjectResource/other/szj.txt ')
print(generate_current_section_list())
print(generate_current_section_list())
print(generate_current_section_list())
输出可能为:
[‘2性相近’, ‘3习相远’, ‘1性本善’, ‘0人之初’]
[‘3贵以专’, ‘2教之道’, ‘1性乃迁’, ‘0苟不教’]
[‘2子不学’, ‘1择邻处’, ‘0昔孟母’, ‘3断机杼’]
请注意0、1、2或3代表一句诗文在一段中的正确位置。每调用一次该函数,诗文下移一段,一段中四句的位置随机变化。
第四步,完善Box类,该类是三字一句文字块的抽象,我们已经提供了该类的属性(私有变量)及其访问方法(getter、setter),你的任务为:
1、完善shuffle_sentence (self)方法,该方法实现文字的混洗功能,可以调用text_shuffle。
2、完善collide(self,moving_object)方法,该方法实现检查该文字块是否和上升的文字块发生碰撞。如果发生碰撞返回True,否则返回False。
3、完善draw(self, canvas) 方法,该方法实现在画布中绘制(显示)文字块的功能,canvas.draw_polygon方法可以会矩形,canvas.draw_text方法可以绘制文本。请注意用不同颜色组合来提示玩家块内文字次序是否正确,也要用颜色区别块间次序是否正确。
4、完善update(self) 方法,利用rising_speed变量对正在上升的文字块显示的位置(self.pos[1])进行更新。
请用以下调用测试Box类是否满足要求:
test_box = Box([120,100], box_width, box_height, ‘人之初’, ‘之人初’, 0)
print(test_box.get_pos())
print(test_box.get_sentence())
print(test_box.get_correct_sentence())
print(test_box.get_order())
print(test_box.get_processed())
test_box.shuffle_sentence()
print(test_box.get_sentence())
输出应当为:
[120, 100]
人之初
之人初
0
False
之初人
第五步,编写draw_all_stopped_box(stopped_box,canvas)函数,stopped_box参数为保存所有已经停止的文字块的集合,请用for循环遍历每个元素,调用它的draw方法完成绘制功能。
第六步,编写check_collision(group, moving_box)函数,group代表所有已经停止的文字块,而moving_box代表正在上升的文字块,请用for循环遍历group集合每个元素, 调用它的collide方法检测是否和moving_box发生碰撞,只要有一个元素和moving_box发生碰撞就返回True,否则返回False。
第七步,完善stop_box(group, moving_box)函数,该函数是游戏逻辑实现的重要函数。
如果moving_box到达顶部或与其它停止的文字块发生碰撞,首先将moving_box的rising属性设置为False,将has_rising_box全局变量设置为False。
检查文字顺序,如果正确,游戏得分增加5分。
将该文字块添加到代表静止块的stopped_box集合中。
如果是四句中的最后一句,检查一行四句的位置,如果正确,游戏得分增加20分,并且从stopped_box删除最后4个文字块完成一行的消解。
如果该块停止使得停止的行数超过10行,设置game_over为True。
第八步,编写draw_game_over_msg(canvas, msg)函数,游戏结束时会调用该函数,在画布的正中间用红色48号字体显示msg代表的信息。
第九步,编写box_spawner()函数,调用该函数会创建一个上升的文字块,请注意事件处理函数注册部分我们有timer = simplegui.create_timer(1000.0, box_spawner)语句,这表明游戏程序每1秒会调用box_spawner()函数1次。
任何时候游戏界面中最多只有一个上升的文字块,因此首先要判断全局变量has_rising_box值,如果为True,表明游戏中有一个正在上升的文字块,因此该函数无需再做什么,否则:
若current_section_list空,调用generate_current_section_list()为其赋值,从current_section_list弹出一个,计算随机位置,调用Box类的构造方法创建一个新的文字块并赋值到全局变量rising_box,最后别忘了正确设置has_rising_box的值。
第十步,编写draw(canvas) 函数,该函数负责屏幕刷新,如果game_over为True,请用draw_game_over_msg函数显示游戏结束画面,否则:
依次调用rising_box的draw方法和update方法实现对上升文字块绘制和位置更新。
然后调用draw_all_stopped_box绘制所有的静止文字块。
最后调用stop_box对发生接触或到达顶部的上升文字块进行处理并更新游戏得分。
第十一步,编写keydown(key)函数,当玩家按下左箭头键时,rising_box的位置左移一个文字块距离,当玩家按下右箭头键时,rising_box的位置右移一个文字块距离,当玩家按下空格键时,rising_box的文字次序随机变换一次。
第十二步,完善start_game()函数,为游戏开始或重新开始初始化全局变量。
一切都搞定后,提交项目之前请注释掉你添加的所有测试代码,保存并运行确认后再提交你的代码。
项目模板
# “决战三字经”游戏
import simpleguitk as gui
import random
import urllib.request
# 全局变量
has_rising_box = True # 是否有文字块正在上升
canvas_height = 600 # 画布高度,单位为像素
canvas_width = 480 # 画布宽度,单位为像素
box_height = 30 # 文字块高度,单位为像素
box_width = 120 # 文字块宽度,单位为像素,包含3个汉字
rising_speed = -1 # 上升速度,单位为像素
game_over = False # 游戏是否结束
rising_box = None # 正在运动的文字块对象
stopped_box = [] # 停止移动的所有文字块列表
prime_sentence_list = [] # 三字经全文列表,3个字构成1句,每句为一个列表元素
current_section_list = [] # 当前处理的段落文字列表,每句为一个列表元素,4句为一个段落
score = 0 # 游戏得分
def read_from_file(filename):
"""从文本文件读入三字经,存储在列表中,并返回该列表"""
# 三字经文件的格式如下:
'''
# rén zhī chū xìng běn shàn xìng xiāng jìn xí xiāng yuǎn
#人之初 性本善 性相近 习相远
gǒu bú jiào xìng nǎi qiān jiào zhī dào guì yǐ zhuān
苟不教 性乃迁 教之道 贵以专
......
'''
# 奇数行为诗句拼音,偶数行为诗句文本
# 三字经每3个汉字为1句,每1句作为列表的1个元素,四句为一行(一段),句间隔为空格
pass
def text_shuffle(text):
"""打乱文字的次序"""
pass
def draw_all_stopped_box(boxes, canvas):
"""绘制所有的停止块"""
pass
def generate_current_section_list():
"""生成当前段落四句诗歌列表"""
pass
def check_collision(group, moving_box):
"""检查移动块和所有停止块是否发生碰撞"""
pass
def delete_last_four(group):
"""消除最后4个块,字序和句序都正确时调用该函数"""
n = len(group)
group.pop(n-1)
group.pop(n-2)
group.pop(n-3)
group.pop(n-4)
def last_four_ok(group):
"""判断最后4个块的字序和句序是否都正确"""
n = len(group)
if group[n-1].proper_order and group[n-1].sentence == group[n-1].correct_sentence and \
group[n-2].proper_order and group[n-2].sentence == group[n-2].correct_sentence and \
group[n-3].proper_order and group[n-3].sentence == group[n-3].correct_sentence and \
group[n-4].proper_order and group[n-4].sentence == group[n-4].correct_sentence:
return True
def stop_box(group, moving_box):
"""停止移动块"""
global has_rising_box, game_over, score, label
pass
def draw_game_over_msg(canvas, msg):
"""绘制游戏结束信息"""
pass
class Box:
"""Box类的定义"""
def __init__(self, pos, width, height, sentence, correct_sentence, order):
self.pos = [pos[0], pos[1]] # 文字块左上角在画布的坐标位置
self.width = width # 文字块的宽度
self.height = height # 文字块的高度
self.sentence = sentence # 文字块实际显示的句子
self.correct_sentence = correct_sentence # 文字块正确字序的句子
self.rising = True # 文字块是否正在上升
self.processed = False # 反射碰撞后是否处理完毕
self.order = order # 该文字块在一段中的正确句序
self.proper_order = True # 实际句序是否正确
def shuffle_sentence(self):
"""打乱字序"""
pass
def collide(self, moving_object):
"""检查是否和移动块发生碰撞"""
pass
def draw(self, canvas):
"""绘制自己"""
pass
def update(self):
"""更新垂直位置,使其向上移动"""
pass
def box_spawner():
"""时钟事件处理函数,生产一个上升的方块"""
global has_rising_box, rising_box, current_section_list, game_over
pass
def draw(canvas):
"""屏幕刷新事件处理函数"""
pass
def key_down(key):
"""处理键盘按下事件的函数"""
pass
def start_game():
"""为游戏开始或重新开始初始化全局变量,也是鼠标点击按钮的事件处理函数"""
global prime_sentence_list, stopped_box, rising_box, current_section_list, has_rising_box, game_over, score
pass
# 创建窗口初始化画布
frame = gui.create_frame("决战《三字经》", canvas_width, canvas_height)
label = frame.add_label("游戏得分 = 0分")
# 注册事件处理函数
frame.set_keydown_handler(key_down) # 按键处理,每次按键会调用key_down函数
frame.set_draw_handler(draw) # 显示处理,每秒调用draw函数60次
timer = gui.create_timer(1000.0, box_spawner) # 每秒调用box_spawner函数1次
button = frame.add_button('重新开始游戏', start_game, 100) # 鼠标每次点击“重新开始游戏”按钮,调用start_game函数1次
# 启动游戏
start_game() # 为游戏开始或重新开始初始化全局变量
timer.start() # 启动定时器
frame.start() # 显示窗口