计算机世界有趣的地方在于,你可以动手构建任何你认为应该可以实现的东西。我们的热身实验是一个很像 “Online Judge” 中做过的题目,只不过是一个真正有意义的 “实用工具”:一个命令行迷宫游戏,帮助你熟悉基本的命令行参数解析和 UNIX 命令行工具的 “基本约定”。
git pull origin M1
下载框架代码。
正如课堂上所说,主动 “参考” 他人的代码、使用他人测试用例都是不严格要求自己的行为。为了使你变得更强,遵守学术诚信可以使你获得真正的训练。坚信计算机世界里没有玄学,无论是 C 代码、汇编代码还是处理器,都可以看作是严格的数学对象,可以使你在遇到问题时少一些焦躁,冷静下来分析下一步应该做什么。为了确保你对操作系统有真实的了解,
请输入 Token 登录。
Rogue-like 游戏可以追溯到 1980 年的《Rogue》,这是一款在 UNIX 系统上运行的文字冒险游戏。在那个图形界面还未普及的年代,程序员们用 ASCII 字符创造了一个充满想象力的世界:
既然是 “文字冒险”,就少不了最基本的命令行命令。在 UNIX 系统中,命令行终端 (Terminal) 是用户与系统交互的基本界面。当你打开终端时,系统会启动一个 Shell 程序来解析和执行平时我们熟悉的命令,如 ls、cd、pwd 等,例如:
cowsay -f dragon "Hello OS!"
会把
argv = {"cowsay", "-f", "dragon", "Hello OS!", NULL}
传递给 cowsay
程序的 main
函数,程序解析参数并在终端 (模拟器) 上画出下面的 ASCII Art:
在这个实验里,我们将构建一个命令行版本的 “游戏后端”,用于支持一个多人对战的 Rogue-like 游戏,并且熟悉命令行工具的命令行解析。这是一个程序本身无状态的后端服务,每运行一次程序就对应了一次用户操作,执行完就会退出,而所有的游戏状态都保存在文件中。这种设计让游戏系统非常灵活——通过实现游戏 “前端”,玩家可以在同一台机器上进行多人游戏,甚至扩展成网络对战模式。
(这个实验融合了 o3-mini 和 jyy 的设计。AI 开始走上历史的舞台,并终有一天会成为主角!)
(Cursor Tab 真懂我 😂)
你需要实现一个命令行工具 labyrinth
,它可以从文件加载迷宫地图,显示玩家位置,并支持玩家在迷宫中移动。除了基本功能外,你还需要实现命令行参数解析、错误处理以及连通性检查等功能。
你可以了解一下 Linux 系统中常见的命令行工具是如何工作的。它们通常支持各种命令行选项,并且有良好的错误处理机制。在这个实验中,我们的 labyrinth
工具需要支持以下功能:
labyrinth [-m|--map FILE] [-p|--player ID] [--move DIRECTION] [--version]
从文件加载迷宫地图,显示和移动指定的玩家。
--map
或 -m
: 指定地图文件路径。--player
或 -p
: 指定玩家 ID (0-9)。--move
: 指定移动方向 (up, down, left, right)。--version
: 显示版本信息。这些参数可以组合使用。
上述实验要求描述是参照 man page 的格式写出的,其中有很多 UNIX 命令行工具遵守的共同约定 (UNIX 的资深用户对此了如指掌;但对给初学者,尤其是从出生以来就生活在 GUI 环境中而不是遇事就读手册的大家造成了很大的困扰),例如 POSIX 对命令行参数有一定的约定。
以下对 labyrinth 的一些具体解释:
-m
, -p
, --move
, --version
都是可选的参数。labyrinth
中,-m
和 --map
的含义是一样的,-p
和 --player
的含义是一样的。main
函数的返回值代表了命令执行的状态,其中 EXIT_SUCCESS
表示命令执行成功,EXIT_FAILURE
表示执行失败。对于 POSIX 来说,0 代表成功,非 0 代表失败。和以前的 Online Judge 编程不同,实际的系统必须在输入不符合规约时及时处理。
地图文件 (map.txt) 的格式如下:
#
:墙壁.
:空地0-9
:玩家 (数字代表玩家ID)labyrinth --map map.txt --player id
labyrinth -m map.txt -p id
--map
或 -m
:指定地图文件路径--player
或 -p
:指定玩家ID(0-9)-m
和 -p
可以互换位置labyrinth --map map.txt --player id --move direction
labyrinth -m map.txt -p id --move direction
--move
:移动方向,可选值为up
、down
、left
、right
labyrinth --version
--version
,显示版本信息并返回错误码 0--version
和其他参数,返回错误码 1为了让游戏更有趣,我们还为 labyrinth
提供了两种不同的前端实现方式,让玩家可以更直观地体验游戏:
labyrinth
命令行工具来更新游戏状态,并在每次移动后刷新屏幕显示最新的迷宫状态。前后端分离的架构设计是现代软件开发中常用的一种模式:后端核心游戏逻辑和状态管理。它提供标准化的命令行接口,处理地图加载、玩家移动验证、游戏状态更新等核心功能,而不关心游戏如何展示给用户。前端则包括本地双人和网络多人界面,负责用户交互和游戏展示。它调用后端命令处理游戏逻辑,将游戏状态以直观方式呈现给玩家,并处理用户输入转换为对后端的调用。
前后端架构带来了关注点分离,后端专注于逻辑的正确性,前端专注于用户体验;其次是灵活性,可以轻松开发不同的前端界面而无需修改核心逻辑;最后是可测试性,可以独立测试后端逻辑,确保其正确性。
本实验没有什么难度,就是为了让大家体验命令行参数的解析。我们可以使用 getopt_long()
函数处理长短选项,该函数在 <getopt.h>
头文件中定义。此外,另一个考察点是平时大家面向 Online Judge 编程时普遍忽略的错误处理,如文件不存在、格式错误等。良好的错误处理是命令行工具的重要特性,请确保在各种异常情况下返回正确的错误码,并提供有意义的错误信息。
我们给同学们提供了非常好用的 testkit。请珍惜这个功能,并在未来编程时保持良好的测试习惯。当然,我们的 testkit 是为同学们准备的,功能并不完善 (例如不同场景下的测试开关),在实际工程项目中,请选择成熟的测试框架 😄