利用Python编写一个记忆翻牌游戏
作者:Charles的皮卡丘 发布时间:2022-09-24 02:20:54
导语
昨天看到有留言竟然说我是月更博主,我明明更新地这么勤快(心虚.jpg)。看吧,昨天刚更新过,今天又来更新了。
今天还是带大家写个小游戏吧,不过老是用pygame也没啥意思,这次我们换点新花样,用python自带的tkinter包写一个记忆翻牌小游戏呗。
废话不多说,让我们愉快地开始吧~
开发工具
Python版本:3.7.4
相关模块:
pygame模块;
tkinter模块;
pillow模块;
以及一些python自带的模块。
环境搭建
安装Python并添加到环境变量,pip安装需要的相关模块即可。
先睹为快
在终端运行如下命令即可:
python Game27.py
效果如下:
视频链接
原理简介
ok,这里我们还是来简单介绍一下游戏的实现原理吧。
首先,我们还是借助pygame来播放一首我们喜欢的背景音乐吧:
'''播放背景音乐'''
def playbgm(self):
pygame.init()
pygame.mixer.init()
pygame.mixer.music.load(cfg.AUDIOPATHS['bgm'])
pygame.mixer.music.play(-1, 0.0)
然后,我们初始化一下tkinter的主界面:
# 主界面句柄
self.root = Tk()
self.root.wm_title('Flip Card by Memory —— Charles的皮卡丘')
并在主界面上显示16张未被翻面的卡片:
# 游戏界面中的卡片字典
self.game_matrix = {}
# 背景图像
self.blank_image = PhotoImage(data=cfg.IMAGEPATHS['blank'])
# 卡片背面
self.cards_back_image = PhotoImage(data=cfg.IMAGEPATHS['cards_back'])
# 所有卡片的索引
cards_list = list(range(8)) + list(range(8))
random.shuffle(cards_list)
# 在界面上显示所有卡片的背面
for r in range(4):
for c in range(4):
position = f'{r}_{c}'
self.game_matrix[position] = Label(self.root, image=self.cards_back_image)
self.game_matrix[position].back_image = self.cards_back_image
self.game_matrix[position].file = str(cards_list[r * 4 + c])
self.game_matrix[position].show = False
self.game_matrix[position].bind('<Button-1>', self.clickcallback)
self.game_matrix[position].grid(row=r, column=c)
这16张卡片共包含8张完全不同的图像,我们游戏的目标就是在有限的时间内,将16张卡片中包含相同的图像的卡片两两配对。匹配的规则是鼠标连续地点击两张卡片,若卡片背面的图像相同,则匹对成功,否则配对失败。游戏主要考察玩家的记忆力,因为游戏还规定游戏翻开的卡片数量至多有两张,否则一开始被点击而翻开的卡片将再次被盖上(若该张卡片没有匹对成功)。
接着,我们来定义一些有用的变量:
# 已经显示正面的卡片
self.shown_cards = []
# 场上存在的卡片数量
self.num_existing_cards = len(cards_list)
# 显示游戏剩余时间
self.num_seconds = 30
self.time = Label(self.root, text=f'Time Left: {self.num_seconds}')
self.time.grid(row=6, column=3, columnspan=2)
并让界面一开始可以出现在电脑屏幕的居中位置:
# 居中显示
self.root.withdraw()
self.root.update_idletasks()
x = (self.root.winfo_screenwidth() - self.root.winfo_reqwidth()) / 2
y = (self.root.winfo_screenheight() - self.root.winfo_reqheight()) / 2
self.root.geometry('+%d+%d' % (x, y))
self.root.deiconify()
由于是在有限的时间内完成所有卡片的匹对,所以我们来写一个定时函数,并实时等更新显示当前游戏的剩余时间:
'''计时'''
def tick(self):
if self.num_existing_cards == 0: return
if self.num_seconds != 0:
self.num_seconds -= 1
self.time['text'] = f'Time Left: {self.num_seconds}'
self.time.after(1000, self.tick)
else:
is_restart = messagebox.askyesno('Game Over', 'You fail since time up, do you want to play again?')
if is_restart: self.restart()
else: self.root.destroy()
最后,我们在鼠标左键点击卡片时,用代码定义一下游戏的响应规则,以实现我们想要的功能:
'''点击回调函数'''
def clickcallback(self, event):
card = event.widget
if card.show: return
# 之前没有卡片被翻开
if len(self.shown_cards) == 0:
self.shown_cards.append(card)
image = ImageTk.PhotoImage(Image.open(os.path.join(self.card_dir, card.file+'.png')))
card.configure(image=image)
card.show_image = image
card.show = True
# 之前只有一张卡片被翻开
elif len(self.shown_cards) == 1:
# --之前翻开的卡片和现在的卡片一样
if self.shown_cards[0].file == card.file:
def delaycallback():
self.shown_cards[0].configure(image=self.blank_image)
self.shown_cards[0].blank_image = self.blank_image
card.configure(image=self.blank_image)
card.blank_image = self.blank_image
self.shown_cards.pop(0)
self.score_sound.play()
self.num_existing_cards -= 2
image = ImageTk.PhotoImage(Image.open(os.path.join(self.card_dir, card.file+'.png')))
card.configure(image=image)
card.show_image = image
card.show = True
card.after(300, delaycallback)
# --之前翻开的卡片和现在的卡片不一样
else:
self.shown_cards.append(card)
image = ImageTk.PhotoImage(Image.open(os.path.join(self.card_dir, card.file+'.png')))
card.configure(image=image)
card.show_image = image
card.show = True
# 之前有两张卡片被翻开
elif len(self.shown_cards) == 2:
# --之前翻开的第一张卡片和现在的卡片一样
if self.shown_cards[0].file == card.file:
def delaycallback():
self.shown_cards[0].configure(image=self.blank_image)
self.shown_cards[0].blank_image = self.blank_image
card.configure(image=self.blank_image)
card.blank_image = self.blank_image
self.shown_cards.pop(0)
self.score_sound.play()
self.num_existing_cards -= 2
image = ImageTk.PhotoImage(Image.open(os.path.join(self.card_dir, card.file+'.png')))
card.configure(image=image)
card.show_image = image
card.show = True
card.after(300, delaycallback)
# --之前翻开的第二张卡片和现在的卡片一样
elif self.shown_cards[1].file == card.file:
def delaycallback():
self.shown_cards[1].configure(image=self.blank_image)
self.shown_cards[1].blank_image = self.blank_image
card.configure(image=self.blank_image)
card.blank_image = self.blank_image
self.shown_cards.pop(1)
self.score_sound.play()
self.num_existing_cards -= 2
image = ImageTk.PhotoImage(Image.open(os.path.join(self.card_dir, card.file+'.png')))
card.configure(image=image)
card.show_image = image
card.show = True
card.after(300, delaycallback)
# --之前翻开的卡片和现在的卡片都不一样
else:
self.shown_cards.append(card)
self.shown_cards[0].configure(image=self.cards_back_image)
self.shown_cards[0].show = False
self.shown_cards.pop(0)
image = ImageTk.PhotoImage(Image.open(os.path.join(self.card_dir, card.file+'.png')))
self.shown_cards[-1].configure(image=image)
self.shown_cards[-1].show_image = image
self.shown_cards[-1].show = True
# 判断游戏是否已经胜利
if self.num_existing_cards == 0:
is_restart = messagebox.askyesno('Game Over', 'Congratulations, you win, do you want to play again?')
if is_restart: self.restart()
else: self.root.destroy()
ok,大功告成。代码逻辑比较简单,就不展开讲啦,小伙伴们简单看下,肯定就能看懂啦。
来源:https://zhuanlan.zhihu.com/p/373217730
猜你喜欢
- 目前搜索到的方法有:np.where(‘元素')还有就是pandas的方法:df.index(‘元素')但是第二个方法的问题
- 1.相信大家,在经过前面的初步学习之后,相信大家也想要有一个舒适的编程环境了。接下来将交给大家一个简单的配置环境Anaconda + VSC
- FileField中的upload_to属性可以设定上传文件的存储目录和名称,它可以是个字符串,也可以是个callable,比如一个方法。当
- 在进行数据库的查询时,会经常遇到这样的情况:例如想在一个用户数据库中查询他的用户名和他的密码,但恰好该用户使用的名字和密码中有特殊的字符,例
- 目录一、两个模块二、SMTP端口三、四大步骤1、构造邮件内容2、连接邮件服务器3、登陆邮件服务器4、发送邮件四、常用场景1、纯文本邮件2、发
- 通过企业管理器设置数据库的定期自动备份计划。 1、打开企业管理器,双击打开你的服务器 2、然后点上面菜单中的工具-->选择数据库维护计
- 1. 场景描述linux服务器下安装了Anaconda3,执行Pyhton的K-means算法,结果出现如下图的中文字符乱码。上次已经解决了
- golang 空结构体 struct{} 可以用来节省内存a := struct{}{}println(unsafe.Sizeof(a))/
- 前言php转go是大趋势,越来越多公司的php服务都在用go进行重构,重构过程中,会发现php的json解析操作(系列化与反序列化)是真的香
- 由于marquee标签现在用得是越来越少了,所以滚动效果的做法大多也都改用javascript来实现了,至于不明白为什么不直接用marque
- javascript中给数组加元素是一个非常简单的问题,javascript本身就提供了大量这类函数,我们可以使用js自带函数快速给数组增加
- asp之家注:在网页设计或编程中如何以最方便的方法来处理图片的宽高,以达到最佳的显示效果,这个问题相信很多网页制作人员都遇到过,最麻烦最费时
- 目录urllib库作用Urllib 库下的几种模块的基本使用一、urllib.request模块1.功能2.常用方法参数说明:总结urlli
- numpy对数组求平均时忽略nan值在对numpy数组求平均np.mean()或者求数组中最大最小值np.max()/np.min()时,如
- 选择排序算法步骤:找到数组中最小的那个元素中,将它和数组的第一个元素交换位置,在剩下的元素中找到最小的元素,将它和数组的第二个元素交换位置,
- 其实很简单,用len函数:>>> array = [0,1,2,3,4,5] >>> print len
- pycharm全局修改pycharm 全局改函数方法1ctrl shift r全局替换方法2点击函数,右键 Refactor Ch
- 如果我有个list,想判断其中的元素是否满一个条件,后面的元素大于或等于前面元素。Python中的写法就比较多了。下面就主要介绍下一般的写法
- 当然如果想了解更多编辑器的原理制作方法,只有你自己去下载一个在线编辑器,慢慢研究,相信会有更多的收获!HTML在线编辑器的基本概念1,什么是
- 数据库镜像方案有两种镜像运行模式。一种是“高安全性模式”,它支持同步操作。在高安全性模式下,当会话开