使用 Python 制作翻牌游戏 (记忆游戏)

2025 年 1 月 11 日 | 阅读 9 分钟

每个人都可以通过玩翻转图块游戏来测试自己的记忆力。在这个集合中,每个数字或图形都有一对图块,数量是偶数。我们必须翻转图块,以便能够看到它们,因为它们是背面朝上的。一次翻转两个图块,如果匹配,则将它们取出。如果不匹配,它们会被翻转过来并放回原位。直到所有图块都匹配并消除,我们才停止。

介绍游戏

现在我们的网格中的图块可以显示为正面朝上或背面朝上。但是我们无法直接参与游戏。这里提醒一下游戏如何进行:

  • 游戏开始时,所有图块都正面朝下
  • 然后玩家翻转两个卡片点击它们进行选择
  • 如果两个图块具有相同的图像,则保持正面朝上。如果不匹配,则在短暂延迟后必须再次翻转为背面朝下

翻转可点击图块

我们现在有一个程序,它只绘制图块网格然后停止。随着时间的推移,我们希望我们的程序能够生成各种图像。如果玩家一切顺利,它将首先绘制背面朝上的图块,然后是已点击的图块以及胜利画面。

将我们所有的绘图代码移到这个语言的 draw() 函数是下一步的逻辑。在应用程序运行时,计算机将继续执行 draw(),导致图块根据它们是正面朝上还是背面朝下而继续绘制。

现在让我们将其中一些图块翻到正面!玩家必须点击一个图块才能翻转它。在这个语言项目中,我们可以编写一个 mouseClicked() 函数来响应鼠标点击,并且计算机将在每次鼠标点击时运行该代码

当我们的应用程序检测到玩家在某个地方点击时,我们希望使用 mouseXmouseY 来检测他们是否点击了一个图块。让我们先给 Tile 添加一个 isUnderMouse() 方法,如果给定的 xy在图块区域内,则该方法返回 true

由于我们创建图块的方式,图块的 xy 对应于图块的左上角,因此我们应该仅在提供的 x 介于 this.xthis.x + this.size 之间,并且提供的 y 介于 this.ythis.y + this.size 之间时返回 true

既然我们现在有了这个方法,我们就可以在 mouseClicked() 中使用for 循环检查每个图块是否在 mouseXmouseY 范围内。如果是,那么我们将图块的 isFaceUp() 属性设置为 true

限制卡片翻转

我们应该已经开发了玩家翻转图块的功能,但是我们忽略了一个关键限制:- 用户一次不应翻转超过两个图块

需要以某种方式记录翻转的图块数量。一种简单的方法是使用一个名为 numFlipped 的全局变量,每次玩家翻转一张卡片时,我们就将其递增。如果 numFlipped 小于 2 且图块尚未正面朝上,我们才翻转它。

延迟图块翻转

因此,翻转两个图块的逻辑已经完成。接下来呢?让我们再次回顾一下规则:

如果图块具有相同的图像,则它们保持正面朝上。否则,过一会儿,图块会翻转回来

如果我们不能简单地寻找新匹配项,那么测试第一部分将很困难,因此我们将首先开发第二部分,该部分自动将图块翻转回来

通过将 isFaceUp 设置为 false,我们知道如何反转图块的方向,但过一段时间后会怎样呢?在不同的语言和环境中,延迟代码执行有多种形式,我们需要弄清楚如何在此语言中实现它。为了确定延迟期是否已过,我们需要一种机制来跟踪时间。我们还需要一种方法来在延迟期结束后调用代码。我们的建议是:

  • 我们创建了全局变量 delayStartFC,它最初是空的。
  • mouseClicked() 方法中翻转第二个图块后,我们将 frameCount 的当前值保存在 delayStartFC 中。我们可以通过使用指示自程序开始执行以来已通过帧数的变量来跟踪我们程序中的时间。
  • draw() 函数中,我们确定新的 frameCount 值是否远高于旧值。如果是,则所有图块都被翻转,并将 numFlipped 设置为 0。此外,我们将 delayStartFC 设为 null。

事实上,这是一个不需要编写太多代码的绝佳解决方案。我们可以使用 loopnoLoop 方法来确保 draw 代码仅在存在延迟时才被调用,以提高性能。以下是我们解释的所有内容:

检查匹配项

如果我们能够匹配任何图块,当它们被翻转回去时,我们可能会感到失望,因为,嘿,我们成功了!所以是时候将这个游戏规则付诸实践了。

当两个图块完美匹配时,它们应该保持正面朝上。

这意味着每当有两个翻转的图块时,以及在用户设置延迟之前,我们都应该寻找匹配的图块。其伪代码如下:

我们如何确定两个图块是否朝向同一方向?我们已经对此进行了检查(numFlipped === 2)。首先,我们需要一种方法来获取正面朝上的两个图块。我们如何找到它们?

每次,我们可以遍历我们的数组来识别所有 isFaceUp 属性设置为 true 的图块,然后将它们存储到另一个数组中。

让我们采取捷径,始终将翻转的图块保存在数组中以方便使用。通过这样做,每次玩家翻转一个图块时,我们都避免了循环遍历整个图块数组。

我们可以先用数组替换 numFlipped,然后在 numFlipped 之前使用过的地方使用 flippedTiles.length。我们的 mouseClicked() 函数如下所示:

现在,要确定 flippedTiles 数组中的两个图块是否具有相同的面,还需要进一步研究。那么面属性是什么?它是一个对象,由于变量在计算机内存中指向同一个位置,因此匹配图块的面实际上应该指向完全相同的项。这是因为每个图像对象只创建了一次(例如,getImage("avatars/old-spice-man")),然后该对象被两次推送到 faces 数组中。

如果使用引用对象的两个变量,并且这两个变量都对应于内存中的同一对象,那么相等运算符,至少在 JavaScript 中,将返回 true。因此,我们可以通过对每个图块的面属性使用相等运算符来进行快速检查。

既然我们确定它们匹配,我们现在必须让图块保持正面朝上。它们现在都会在一段时间延迟后翻转过来。在这种情况下,我们也可以选择不设置动画,但我们不能仅依赖它,因为后续回合会有动画

我们需要一种方法来表示:“嘿,当我们把它们全部翻回来时,我们不应该翻转这些特定的图块。”这是一个布尔属性的好应用!让我们将 isMatch 属性添加到 Tile 构造函数中,然后在该if 块内部,将 isMatch 属性设置为 true。它也显示在下面:

现在,我们可以根据该属性决定是带有延迟还是不带延迟地翻转图块。

完整代码

输出

Flipping Tiles (Memory game) using Python
下一主题Python Curl