MiniZork:功能冒险

2021-02-06 20:25:01

我玩过的第一批视频游戏之一叫做Scott Scotts Adventure Games。至少根据我在互联网上搜集到的信息。它是随父亲在个人计算的第一天就购买的计算机一起提供的,当时这对我来说就像第二次世界大战一样古老。该游戏是麻省理工学院发明的极简主义游戏Zork的变体。真正的极简主义者……没有图像,只有文字,您走来走去用英语编写游戏命令,其中大多数命令都发回了一些隐秘的消息,例如:“我听不懂”。更糟糕的是,当时我的英语知识很初级,而且我在词典中花费的时间多于玩游戏。至少在一开始...

但是,斯科特·亚当斯冒险游戏(Scott Adams Adventure Games)尽管过时,但可能不自觉地影响了我的兴趣:我在职业生涯中花费了大量时间来编写自然语言处理(NLP)软件。

视频游戏设计师的虐待狂之后可能会产生可怕的影响。我们永远不能说够。如果需要进一步的证据,则只需要看看这款游戏在怪胎文化中的奇怪地位即可:例如,谢尔顿(Sheldon)在《大爆炸理论》第4季的其中一集中扮演Zork。他很快发脾气,很快。

但是,这款游戏的机制为您提供了一个独特的机会,来介绍函数式编程的一些最有趣的方面...

您甚至可以通过放置" breakpoints"来直接调试游戏。用ctrl-b。然后只需使用以下代码执行代码:Ctrl-X d进入调试器。

游戏包含一个3x3的网格,您可以在其中移动。每个方块都与字典中的描述相关联:Castlemap。

(setq castlemap {" 1:1&#34 ;:"您站在大门前。"" 1:2&#34 ;:"。 。"" ..."" 2:1&#34 ;:" ...&#34 ;&#34:0&#34 ;:" ..." 0:2&#34 ;:" ..."&#34 ; 2:0&#34 ;:" ..."" 2:2&#34 ;:" ..."})

每次您在网格中移动时,游戏都会显示相应的描述。有大量的词典和列表来记录框中出现的物体,等待旅行者的危险或从一个点到另一个点的有效方向。

因此,游戏板仅限于一些文字说明,这些文字说明将用于网格中的每一步。

最后,让我们注意字典声明是一个惰性求值的有趣案例。的确,当LispE遇到静态定义时,它不会创建字典,而是会生成该字典的命令序列。

(defun test(s)(setq s {1:x" a&#34 ;:" b"}));以以下形式编译:(defun test((setq s(key(key)1 x)" a"" b"))

键d k返回nil或字典d中为键k存储的值。

键d k v用键k和值v初始化字典d并返回d。

这样,仅在需要时才在调用时创建字典。实际上," {}"仅是使字典更易于使用的语法糖,因为不幸的是,键的嵌套调用往往会很快变得难以理解。

显然,以上描述不是很原始。在1到10的哈欠标度(YS)上,我们可以毫无问题地接近7。确实,困难在于分析诸如以下的命令:往西走并推断出这是一个方向。同样,我们也想说:拿石头,推论这是在地面上捡起物体的问题。

我们将所有操作都基于数据类型和模式匹配功能的结合使用,并带有映射和过滤器提示。

通过输入函数完成输入,该函数返回在键盘上键入的字符串。结果存储在变量对话框中(YS = 6)。

(setq对话框(+(上(在对话框0))(下(提取对话框1(大小对话框))))))(setq命令(map(\(x)(select(关键同义词x)x)))(分割对话框"")))为了匹配,我们将每个字符串转换为原子;我们删除了无用的单词:the,a ...(setq命令(filter(\(x)(不是(key stopwords x)))(map' atom命令))))

第一行接受我们的命令:拿石头变成它:拿石头。换句话说,我们将字符串小写,将第一个字母大写。

对话然后沿着空格切开。然后,我们检查每个单词是否有同义词。单词Get具有一个同义词(请参阅代码中的同义词词典),该单词实际上是单词Take。

最后一行将字符串列表转换为原子列表,从中删除无用的单词(停用词)。

请注意,filter和map的组合允许在两行代码中用其同义词替换单词,将每个单词转换为一个原子,并删除那些被认为多余的单词。最后,请注意使用(\(x)...),这是一种更紧凑的编写方式(lambda(x)...)。

也许是一条使您想到尝试/抓住(YS = 5)的指令。指令按顺序执行,如果其中一个触发错误,则执行最后一行:

请记住,命令是原子的列表,在这种情况下,原子将被视为数据类型。如果评估失败,我们将显示错误消息列表中的随机消息。

(数据[移动原子_] [中断原子_] [打开原子_] [杀死原子_] [取原子] [拖放原子])

这些是游戏中的有效动作...请注意,先前的工作在于将这些定义与玩家​​编写的文本进行匹配。同义词和空单词过滤器除了建立这种对应关系外没有其他目的。

此外,我们然后可以从用户声明的内在验证中受益……如果句子中包含一个单词太多或太少,则系统可以根据这些声明立即返回错误消息。

因此,也许具有所有意义。如果该句子与此处允许的语句之一不符,则会立即返回错误消息。

但是要使机制完整,就缺少一个要素。我们已经展示了话语如何可以转换为数据结构。

(defpat action([Take x])...);我们显然可以预先定义我们要寻找的原子。 ;方向=北,南,西,东(defpat action([Move' north])...)(defpat action([Move' south])...)(defpat action([Move' #39; west])...)(defpat action([Move' east])...)

因此,(动作命令)调用在于选择与处理播放器的命令之后获得的数据结构相对应的功能。

如果命令不是有效的数据类型,则可能会返回并捕获第一条错误消息。

重要的是要注意,这些不同函数的粒度可能会有所不同,具体取决于是否要对参数进行常规或非常细粒度的定义。因此,用附加功能丰富游戏机制非常简单,而且这相当健壮。的确,替代方法是在函数中放置if的级联,最终可能会产生无法控制的副作用,尤其是当您要测试值的许多特定组合时。当然,除非您想将代码作为牺牲品献给Great Spaghetti Monster。

因此,添加新的数据类型和新的图案功能完全独立于其余代码,从而确保了if或switch难以提供的稳定形式,尤其是在它们是游戏机制的核心的情况下。

可以看出,该代码受益于数据结构和模式函数的联合使用。因此,语法检查由简单的数据结构声明覆盖。至于执行,它包括提供一个将这些数据结构作为参数的动作函数。