编写一个机器人模拟器。
一家机器人工厂的测试设施需要一个程序来验证机器人的移动。
机器人有三种可能的移动方式:
机器人被放置在一个假想的无限网格上,面朝某个特定的方向(北、东、南或西),并位于一组 {x,y} 坐标处,例如 {3,8},坐标向北和向东递增。
随后,机器人会收到若干条指令,此时测试设施会验证机器人的新位置,以及它面朝的方向。
测试分为 3 步。
使用 go test 运行所有测试,或者用 -tags 选项运行指定的测试。
示例:
go test # run all tests
go test -tags step1 # run just step 1 tests.
go test -tags 'step1 step2' # run step1 and step2 tests
你会拿到源文件 defs.go,它定义了测试程序所需要的许多东西。这个文件按步骤分为三个部分。
要完成第 1 步,你需要定义 Right、Left、Advance、N、S、E、W 和 Dir。 完成第 1 步之后,再进入第 2 步。
在第 1 步里,你实现了机器人的移动,但这还算不上真正的模拟。比如,源代码里“机器人”在哪里?“网格”又在哪里?把机器人的动作转换成网格位置的运算在哪里,在机器人里,还是在网格里?物理世界可不是这样。
第 2 步引入了一个“房间”。这看起来只是个小改动,但我们会做大幅调整,来厘清“房间”“机器人”和“测试程序”各自的角色,并开始厘清这个模拟的物理规则。你要把 Room 和 Robot 定义为函数,测试程序把它们作为 goroutine 启动,从而“让它们诞生”。信息通过 Go channel 在测试程序、机器人和房间之间流动。
把 Room 看作一个“物理引擎”,它负责建模并模拟一个带墙壁和机器人的物理房间。它应当以某种方式对房间的坐标空间、机器人和墙壁的位置建模,并确保比如机器人不会穿墙。我们希望 Robot 是执行动作的代理,同时希望 Room 维护一致的事实。
测试程序创建 channel,并启动 Room 和 Robot。然后测试程序向 Robot 发送命令。命令发送完毕后,它会关闭命令 channel。Robot 必须接收命令,并把它正在尝试执行的动作告知 Room。当它感知到命令 channel 关闭时,必须自行关闭。房间必须解释机器人动作带来的物理后果。当它感知到机器人关闭时,会向测试程序发回一份最终报告,说明机器人的最终位置和方向。
第 3 步有三个重大变化:
对于 Room3 发出的最终位置报告,你可以直接返回从 robots channel 收到的那个切片,只需更新其中的位置和方向。遇到以下情况时,必须在日志 channel 上发送消息: