Go实现2048小游戏
Go 实现一个命令行界面的 2048 游戏,仅涉及 Git 和 Go,用来熟悉基本语言特性。原型项目来自 https://github.com/chhabraamit/2048
1. 环境准备
Win10 环境,go 1.14.3,编辑器为 VScode,使用 Github 管理代码。
首先在网页端建立 Github 仓库,选择 MIT 协议,然后克隆仓库到本地
|
|
在项目根目录创建 main.go
文件
|
|
初始化项目
|
|
2. 显示游戏面板
2048 的游戏界面是一个 4×4 的网格,我们使用一个二维切片作为底层结构存储数字,然后按照网格的形式输出到终端,数字随机生成。
|
|
然后修改 main.go 如下
|
|
运行 go run main.go
可以看到一个临时的游戏面板。
3. 添加元素
上面的程序随机生成了 16 个数字填充游戏面板,但这只是初始测试,正式游戏的做法是:初始时随机填充两个数字,然后每个键盘输入新增一个数字。这里还要注意每一步生成的数字有两种选择,2 或 4,我们可以控制这两个数字生成的概率。
所以添加新元素被抽象为一个独立的函数,如下
|
|
board 结构体新增了 nx,ny 两个字段,是为了标记新添加的元素在游戏面板中的位置,我们需要将新元素以不同的颜色表示,这里用到了 fatih/color 包。
|
|
导入该包后修改显示函数如下,用不同的颜色输出新添加的元素。
|
|
相应的,主函数修改如下,添加 10 个元素并输出
|
|
4. 先清屏后显示
上面的程序会把每一步的面板都输出到终端,我们应当添加的一个功能是,每一步只输出当前的游戏面板。该功能通过清屏函数实现,注意,清屏的实现在不同操作系统可能会有区别,下面的实现适用于 Windows 系统。
|
|
5. 获取键盘输入
游戏的每一步操作肯定都是根据键盘的输入来的,可以使用 {W, A, S, D} 和 方向键,如果使用 fmt 包中的输入函数,那么每次输入后都需要手动按下回车。为了不必每次输入字符后都敲一下回车键,我们使用 eiannone/keyboard 包
|
|
将键盘输入对应的几个操作定义为几个常量,然后调用 keyboard 包中的 GetKeyStrokes() 函数接收键盘输入,返回对应的常量,最后在 TakeInput() 函数中根据不同常量交给对应的操作函数处理。
|
|
游戏退出有两种情况,一个是上面程序中定义的 QUIT 操作,用于游戏过程中主动输入 ESC 按键退出,另一个是游戏面板 16 个数字已满,Game over,通过添加以下函数实现
|
|
最后是程序开始的逻辑,即输入任意键开始。这部分逻辑在 main 函数中
|
|
6. 数字移动合并
每个键盘输入都对应一个操作函数,四个方向的数字移动和合并是游戏的核心逻辑。如下,向左移动数字和合并单独实现,向右、向上和向下都能通过矩阵旋转转换为向左移动和合并的问题。
|
|
由于这部分逻辑比较复杂,需要测试一下
|
|
7. 分数计算
游戏正常结束后显示当前最大分数和总分数,算是一个小功能。
|
|
|
|
8. 代码重构
所有功能完成后,对代码进行重构整理,包括
- 将数字移动合并的相关代码移动到单独的
move.go
源文件中; - (可选)将输入和显示的相关函数都拆分到单独的源文件中;
- 为所有代码添加注释,并编写 README.md 文档;
9. 结果与收获
完整的项目代码可以查看我的 github 仓库,与原项目有一些实现上的区别,并完善了一些功能。
编写该项目的收获有
- 开源协议的选择;
- 对结构体和接口作用和意义的思考;
- 一个项目是从简单到复杂一步步建立的,不要想一步登天做的很完善;
- 方法中 (b *board) 和 (b board) 的区别;
- 一些重要的可定制的参数可以抽取作为为常量,比如面板规格,常量命名时前面最好加下划线
_
加以区分; - fmt.Printf 可以输出固定长度的空字符用于占位,fmt.Println() 可以用来换行;
- 随机数的生成方法,一个小技巧是使用数组存放待选择数字,然后随机生成数组长度范围内的数字作为索引进行选择;
- 结构体对象的生成可以使用工厂模式,比如 NewBoard 函数;
- 格式化输出的颜色控制(fatih/color包);
- 清屏的实现方法;
- 无需回车不断读取键盘输入的实现方法(eiannone/keyboard包);
- 矩阵旋转等大量关于二维切片的算法实现(PS:刷题还是有用的);
- 测试用例的编写;
- 所有功能完成后,根据情况进行重构,比如代码的解耦等,然后完成添加注释、编写文档等工作;
- Go 文档的编写与使用;
- 日志系统的使用。