我在传统部分充满了Lovecraftian疯狂的气氛中向所有人致意。
在写以前的一篇文章(不要看,不是特别好)的过程中,我想到了quine的事实……是的,以防万一,我提醒您:quine是一个显示自己的文本并“诚实”地执行的程序(而不在硬盘驱动器上的文件中查找此文本)。 总的来说,传统的程序员毫无意义。
因此,我考虑了奎因原则上可以携带任意有效载荷的事实。 也就是说,除了其主要功能外,还要执行其他任何操作。 而且,作为概念证明,我决定写一个能打井字游戏的木马。 并写道。 剪裁处有脏污的细节。

“但是他除了显示文字外还能做什么?” -也许你问。 而且容易。 除输出外,程序还具有输入。 如果程序在没有输入的情况下显示其文本-则为quine。 如果在输入可用时程序执行其他操作,我们该谴责谁?
也许我会立即将桌子上的纸牌摆好。
这是我的创作的链接。 通过引用,文件中包含三个实体:
- 我要说的是quine函数。
- evalAndCall函数是帮助器。
- 游戏课-更多帮手
首先,我们将讨论如何使用quine函数,然后如何使用它。
如何和她一起工作
在quine函数的最开始,您可以看到以下内容:
function quine(input){
该功能最开始的注释是用户界面。 通过它,该功能将与使用它进行游戏的人进行通信。 最初,我考虑过通过控制台执行此操作,但是后来我决定,保持
功能清洁更正确。
让我们检查一下函数是否真的是quine。
在这里,为方便起见,我发布了一个(几乎)空的HTML页面,并附加了quine.js脚本。 打开开发人员的工具,您可以非选择性地驱动以下代码:
const quineText = quine(); const evaluatedQuine = eval("(" + quineText + ")");
孔模实际上,当然,我们只检查了quine函数是否返回了quine的文本,而没有检查它本身就是quine。 确切地说,我们只检查quine函数返回该函数的文本,在我们的例子中,该文本像quine一样工作。 无法保证其中的内容不包含以下内容:
if(Math.random() < 0.99){ beAGoodQuine(); }else{ haltAndCatchFire(); }
现在我们可以尝试和她一起玩。 假设我们先移至该字段的左上角。
let quineText = quine(1);
现在,“用户界面”如下:
function quine(input){
奎因考虑到了我们的举动,并在中上层地区做出了回应。 顺便说一句,结果函数也是quine -不带参数调用,它将返回其新文本,而不是原始函数的文本。 我们可以玩整个游戏,但为方便起见,我们将使用辅助功能evalAndCall。
let quineText = quine();
瞧! Quine发挥,获胜甚至得分。 您可以玩更长的时间,但是为了获得更大的方便,我建议使用Game类,我自己使用该类来测试游戏。 我认为,如果您读到这一点,则无需解释如何使用它。
运作方式
通常,任务包括两部分:如果可能,编写井字游戏的简明功能,然后将其推入木盒。 让我们从井字游戏开始。
“人工智能”的核心位于66-90行,其轮廓类似于一只顽固的松鼠:
const rules = { "o___x____": "ox__x__!_", "ox__x__o_": "ox!_x_xo_", "oxo_x_xo_": "oxo!xxxo_", "oxooxxxo_": "oxooxxxoxd", "_o__x____": "xo__x___!", "xo__x___o": "xo_xx!!_o" }; const next = (field, move) => { if(!~"!_".indexOf(field[--move])){ return null; } field[move] = "o"; const win = field.indexOf("!"); if(~win){ field[win] = "x"; return [...field, "w"]; } for(let n = 0; n < 4; n++){ field = field.map((_, i) => field[[2, 5, 8, 1, 4, 7, 0, 3, 6][i]]); rules[field.join("")] && (field = rules[field.join("")].split("")); } return field; }
这段代码看起来有些混乱,因为我有兴趣使代码尽可能短。 其实质如下:字段状态(由9个元素组成的数组)以及玩家人员进行移动的单元的编号(下一个检查此移动的有效性),输入下一个功能。 字段的每个元素都可以是叉号,零,下划线(空单元格)或感叹号(第一个机会在其上放置叉号的空单元格)。 如果球场上有一个感叹号,我们立即在那移动并赢得胜利。 否则,我们将数组粘贴到字符串中,然后在rules对象中查找相应的规则。 为了节省空间,规则对于旋转是精确的;因此,要进行搜索,该字段将旋转四次。 找到所需的规则后,该字段的状态将替换为该规则的值,并分成多个字符。 接下来,下一个函数返回该字段的新状态。 同时,另外的第十个字符可以加入:“ w”-如果AI获胜,“ d”-如果有平局。
得益于感叹号,“提示”和字段的旋转,当然也要归功于AI排在第一位的事实,因此仅用6条规则描述了最佳策略。
使用下一个函数,quine函数处理输入并将某些字段写入magicHash对象。 在这里,我们顺利地进行第二部分:“ quayne”组件的工作方式。 所有魔术都在magicHash对象及其magicString属性中。
如您所见,magicString行包含几乎完全重复的程序文本。 但是,正如每个曾经尝试写一个quine的人都知道的那样,您不能将一个完全完整的文本推入其中-因为那样一来,它就必须严格包含自身,这对于有限长度的字符串是不可能的。 因此,除了“普通”文本外,它还包含“魔术”通配符序列,该序列在两侧均由“ $”字符分隔。
当时间到X时,我们必须返回函数的文本,我们只需要magicString并将其中的通配符替换为magicHash对象的相应属性。 这些属性可以是静态的(反引号),可以在运行时(字段)进行更改,甚至可以在运行时(消息)添加-没关系。 重要的是,对于每个不能简单地在一行中重复的“有问题”的代码段,我们都有magicHash对象的“ magic”属性。 最后一个代替magicString本身。 后者-否则将有其他通配符序列也将被替换。
总结
根据我自己的经验,我检查了您可以将任何东西塞入奎纳。 原则上,如果您费心的话,可以制作一个quine生成器-该函数可以将任何其他纯函数转换为quine并允许您存储上述纯函数可以访问的任意状态。 但是,此手稿的边距太窄了...
总的来说,我不假装我的“发现”具有特殊性。 顽固的工作人员一定是带着出色的笑容阅读了本文。 但是用我自己的双手捡起来,可以说是将手指放在伤口上,对我来说很有趣。 我希望您对阅读有兴趣。
再见,女孩和男孩。 再见。