「图形化编程」Micro:bit 井字棋游戏(三)

titu

提到井字棋,想必大家都不陌生,就是两个玩家轮流在一个井字型的 9 宫格里画 x 和 o,当某一方的棋子连成了一条线(横向,竖向和对角线),那么该方获胜,如果格子被填满也没有人成功连线,则平局。这次我们来学习如何用 micro:bit 实现一个井字棋小游戏。

通过 「图形化编程」Micro:bit 井字棋游戏(一) 我们实现了光标移动,A B 玩家轮流下棋的逻辑,通过 「图形化编程」Micro:bit 井字棋游戏(二) 我们学习了处理异常情况,比如当前光标位置已经有棋子了,显示一个 X,不让玩家再在当前位置下棋,当棋盘满了没有玩家获胜就显示一个 “Draw Game” 表示平局,这节教程我们将会实现井字棋游戏剩下的部分,判断输赢,这节教程会稍微难一些,涉及到一个简单的算法来算输赢,我会用一些图形来解释原理方便理解,不要被算法两个字吓到,没那么难啦,如果实现的过程中碰到什么问题可以在我的公众号给我留言,让我们开始吧

什么是算法

算法,英文名是 Algorithm,表示的是为了解决特定问题的一组指令的集合,一个算法通常会有五个特征。

  1. 有限性(Finiteness):一个算法必须保证执行有限步之后结束。
  2. 确切性(Definiteness): 一个算法的每一步骤必须有确切的定义。
  3. 输入(Input):一个算法有零个或多个输入,以表示运算对象的初始情况,所谓零个输入是指算法本身给定了初始条件。
  4. 输出(Output):一个算法有一个或多个输出。没有输出的算法毫无意义。
  5. 可行性(Effectiveness): 一个算法的任何计算步骤都是可以被分解为基本可执行的操作,每个操作都能够在有限时间内完成。

对于井字棋游戏来说,输入就是当前棋盘上棋子的坐标,输出就是有没有一方胜出或平局。

如何计算井字棋的输赢

井字棋的输赢判定有很多种不同的算法,这里我选了一种比较好理解的,我叫它计分法。井字棋的赢棋情况一共有 8 种,横竖各 3 条线加 2 个对角线,来看下面的图:

wincase

搭配这张坐标图,得到 8 种情况每种对应的 3 个棋子坐标如下

zuobiao

规则编号 数组元素编号 棋子坐标 规律
1 0 (0,0)(0,2)(0,4) x 坐标都是 0
2 1 (2,0)(2,2)(2,4) x 坐标都是 2
3 2 (4,0)(4,2)(4,4) x 坐标都是 4
4 3 (0,0)(2,0)(4,0) y 坐标都是 0
5 4 (0,2)(2,2)(4,2) y 坐标都是 2
6 5 (0,4)(2,4)(4,4) y 坐标都是 4
7 6 (0,0)(2,2)(4,4) x 坐标 = y 坐标
8 7 (4,0)(2,2)(0,4) x 坐标 + y 坐标 = 4

具体算法是

  1. 初始化一个有 8 个元素的数组,每个元素都是 0
  2. 在每次落子时,判断落子的 x 坐标和 y 坐标符合上面哪条规律,给对应数组元素编号(数组元素是从0开始的,还记得吗)的数字加 1 或者减 1,这里规定 如果是玩家 A 下的棋,则 加 1,如果是玩家 B 下的棋,则减 1,需要注意的是上面的规则可能会同时满足,比如 (4,4) 同时满足 3、6、7 这 3条规则
  3. 遍历这个数组,如果有其中一个数字是 3 或者 -3,就表明有玩家胜出了,如果为 3 则玩家 A 胜出,如果是 -3 则为玩家 B 胜出
  4. 如果下到 9 个格子都满了也没有胜出,那就平局,平局的逻辑 上节教程 已经处理过啦。

之所以叫计分法,是指当有一方玩家落子之后,给对应的胜负情况计 1 分或计 -1 分,最后判断只要有一方先到 3 分或者 -3 分则胜出,是不是没有那么难呀。

举个例子(如果聪明的你已经理解了,那么就可以跳过下面的例子解释,直接看实现部分啦)

1.玩家 A 在棋盘中间走了一步,即 (2,2),符合编号 2、5、7,则给数组元素编号为 1、4、6的元素分别加 1

1

0 1 2 3 4 5 6 7
0 1 0 0 1 0 1 0

2.玩家 B 走了右上角 (4,0),符合编号 3、4、8,则给数组元素 2、3、7 分别减 1

2

0 1 2 3 4 5 6 7
0 1 -1 -1 1 0 1 -1

3.玩家 A 走了第一行中间 (2,0),符合编号 2、4,给元素 1、3 加 1

3

0 1 2 3 4 5 6 7
0 2 -1 0 1 0 1 -1

4.玩家 B 封堵,走了下面中间 (2,4),符合 2、6,给元素 1、5 减 1
4

0 1 2 3 4 5 6 7
0 1 -1 0 1 -1 1 -1

5.玩家 A 另辟蹊径下在左上角 (0,0),符合 1、4、7,给元素 0、3、6 加 1

5

0 1 2 3 4 5 6 7
1 1 -1 1 1 -1 2 -1

6.玩家 B 发现破绽逆转局势,下在右下角 (4,4),符合 3、6、7,给元素 2、5、6 减 1

6

0 1 2 3 4 5 6 7
1 1 -2 1 1 -2 1 -1

7.玩家 A 尽力封堵,但为时已晚,下在右边中间 (4,2),符合 3、5,给元素 2、4 加 1

7

0 1 2 3 4 5 6 7
1 1 -1 1 2 -2 1 -1

8.玩家 B 下在 (0,4),符合 1、6、8,给元素 0、5、7 减 1,元素 5 为 -3,即符合编号为 6 的胜出条件,获胜

8

0 1 2 3 4 5 6 7
0 1 -1 1 2 -3 1 -2

你也可以自己画一个井字格和一个数组推导一下整个过程,有助于理解这个算法,如果有不清楚的地方可以给我留言。下面我们来实现这个算法。

代码实现

首先打开 makecode 网站,载入 「图形化编程」Micro:bit 井字棋游戏(二)已经做好的部分,或者你没有来得及看上节教程,可以直接在我的公众号回复 hex 关键字获取 「图形化编程」Micro:bit 井字棋游戏(二) 的 hex 文件,然后上传到 makecode 网站接着实现。

当开机时

  1. 创建一个变量,命名为 jifenliebiao
  2. 设置 jifenliebiao 为 空数组
  3. 从 循环 分类下拖入一个 重复 x 次 模块,把 x 改为 8
  4. 从 数组 分类下拖入一个 将值 x 添加到结尾 模块 到循环体中,把 变量改为 jifenliebiao,把 x 改为 0,意思是 添加 8 个 0 到 jifenliebiao 里
    kaiji3

修改 luozi 函数

  1. 创建一个新变量,命名为 fenshu
  2. 如果 Axiaqi 为 true,则设置 fenshu 为 1
  3. 否则 设置 fenshu 为 -1
  4. 添加一个新的函数,命名为 jifen,我们会在下面实现它
  5. 在 调用 jianchashengfu 之前调用 jifen 函数
    luozi3

计分函数

这里我们需要一个新的函数,用于计分,计分规则上面已经讲过了,一共 8 个规则,我们分别实现这 8 条规则

  1. 创建一个新变量,命名为 x,设置值为 dangqianbu 的 x 坐标
  2. 创建一个新变量,命名为 y,设置值为 dangqianbu 的 y 坐标
  3. 如果 x 为 0,则 将数组 jifenliebiao 中索引 0 的值设置为 从数组 jifenliebiao 中取得索引 0 的值 加上 fenshu,即符合编号 1 的规则,给编号 1 对应的 数组元素 0 计 1 分或 -1 分
  4. 同样的,实现编号 2 的规则,如果 x 为 2,则 将数组 jifenliebiao 中索引 1 的值设置为 从数组 jifenliebiao 中取得索引 1 的值 加上 fenshu,这里有个小技巧,可以在代码区选中 上一步实现的 如果 x 为 0模块,按一下 ctrl+c 复制,然后再 ctrl+v 粘贴,然后修改一下相应的数字就可以快速实现剩下的规则了
  5. 实现编号 3 的规则,如果 x 为 4,则 将数组 jifenliebiao 中索引 2 的值设置为 从数组 jifenliebiao 中取得索引 2 的值 加上 fenshu
  6. 实现编号 4 的规则,如果 y 为 0,则 将数组 jifenliebiao 中索引 3 的值设置为 从数组 jifenliebiao 中取得索引 3 的值 加上 fenshu
  7. 实现编号 5 的规则,如果 y 为 2,则 将数组 jifenliebiao 中索引 4 的值设置为 从数组 jifenliebiao 中取得索引 4 的值 加上 fenshu
  8. 实现编号 6 的规则,如果 y 为 4,则 将数组 jifenliebiao 中索引 5 的值设置为 从数组 jifenliebiao 中取得索引 5 的值 加上 fenshu
  9. 实现编号 7 的规则,如果 x 等于 y,则 将数组 jifenliebiao 中索引 6 的值设置为 从数组 jifenliebiao 中取得索引 6 的值 加上 fenshu
  10. 实现编号 8 的规则,如果 x + y 为 4,则 将数组 jifenliebiao 中索引 7 的值设置为 从数组 jifenliebiao 中取得索引 7 的值 加上 fenshu

jifen3

检查胜负函数

最后一步,修改检查胜负函数,我们需要检查 jifenliebiao 数组里有没有一个元素为 3 或者 -3,如果有一个元素为 3,代表 A 赢了,如果为 -3,代表 B 赢了,如果都没有,就走原来的逻辑,判断棋盘是不是满了,如果满了就平局,如果没满就什么也不做,因为棋还没有分出胜负

  1. 新建一个变量,命名为 fen,这是个用于访问 jifenliebiao 的变量
  2. 从 循环 分类下 拖一个 将数组 x 中的元素逐个取出,以 fen 引用每次取出的值,意思是把 jifenliebiao 中的元素一个一个取出,并把元素的值赋值给变量 fen,然后执行循环体
  3. 判断 如果 fen 为 3,显示字符串 A Win,游戏结束
  4. 判断 如果 fen 为 -3,显示字符串 B Win,游戏结束

shengfu3

到这里就完成整个井字棋游戏啦,快和你的小伙伴一起下井字棋吧,这里也要给自己鼓鼓掌呦,因为你已经实现了第一个算法,棒棒哒

完整程序图

all

Hex 文件

这篇教程的 Hex 文件可以在 这个地址 下载

0 0 vote
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments