六角,271字节
我向您介绍六角形自我解释器的前3%...
|./...\..._..>}{<$}=<;>'<..../;<_'\{*46\..8._~;/;{{;<..|M..'{.>{{=.<.).|.."~....._.>(=</.\=\'$/}{<}.\../>../..._>../_....@/{$|....>...</..~\.>,<$/'";{}({/>-'(<\=&\><${~-"~<$)<....'.>=&'*){=&')&}\'\'2"'23}}_}&<_3.>.'*)'-<>{=/{\*={(&)'){\$<....={\>}}}\&32'-<=._.)}=)+'_+'&<
在线尝试!您也可以自己运行它,但是大约需要5到10秒。
原则上,这可能适合边长9(分数为217或更小),因为它仅使用201条命令,而我首先编写的非高尔夫球版本(边长为30)仅需要178条命令。但是,我很确定实际上要使所有内容都适合需要永远的时间,因此我不确定是否会尝试。
通过避免使用最后一排或两排,还应该有可能将其打成10码,这样就可以省略尾随的无人操作,但作为第一条路径之一,这将需要大量重写连接利用左下角。
说明
让我们首先展开代码并注释控制流路径:
这仍然很混乱,所以这里是我最初编写的“ unolfed”代码的同一图(实际上,这是侧面长度为20的代码,最初我是用侧面长度为30的代码编写的,但是它是如此稀疏,以至于根本没有提高可读性,所以我只做了一点压缩以使其尺寸更合理):
点击查看大图。
除了一些非常小的细节外,颜色完全相同,non-control-flow命令也完全相同。因此,我将在非高尔夫球版本的基础上解释其工作原理,如果您真的想知道打高尔夫球的部件是如何工作的,则可以检查其中的哪些部分对应于较大的六角形部分。(唯一的问题是高尔夫球代码以镜子开头,因此实际代码从右角开始向左移动。)
基本算法几乎与我的CJam答案相同。有两个区别:
- 我没有计算中心六边形方程,而是计算连续的中心六边形直到一个等于或大于输入的长度。这是因为Hexagony没有简单的方法来计算平方根。
- 而不是立即对输入进行无操作填充,我稍后再检查是否已经用尽了输入中的命令,
.
如果有则打印一个。
这意味着基本思想可以归结为:
- 在计算输入字符串的长度时读取并存储它。
- 找到可以容纳整个输入的最小边长
N
(和相应的居中六边形hex(N)
)。
- 计算直径
2N-1
。
- 对于每一行,计算缩进量和像元数(总和为
2N-1
)。打印缩进,打印单元格(.
如果输入已经用尽,则使用),打印换行符。
请注意,只有无操作,因此实际代码从左上角开始($
,它跳过了>
,所以我们实际上从,
深灰色路径开始于)。
这是初始的内存网格:
因此,内存指针从标记为input的边开始,指向北。,
从STDIN读取一个字节,或者-1
如果我们已经将EOF打入该边缘,则从中读取一个字节。因此,<
紧接其后的条件是我们是否已阅读所有输入的条件。现在让我们保持在输入循环中。我们执行的下一个代码是
{&32'-
这会将32写入边缘标记的空间,然后从边缘标记diff的输入值中减去它。请注意,这绝不能为负,因为我们保证输入仅包含可打印的ASCII。当输入为空格时,它将为零。(正如Timwi指出的那样,如果输入中可以包含换行符或制表符,这仍然可以工作,但是它也会去除字符代码少于32的所有其他不可打印字符。)在这种情况下,<
将指令指针(IP)偏左然后采用浅灰色路径。该路径只是用重置MP的位置,{=
然后读取下一个字符-因此,跳过了空格。否则,如果字符不是空格,则执行
=}}})&'+'+)=}
这首先通过长度边沿六边形移动,直到与diff边相反,即为=}}}
。然后它将值从长度边的对面复制到长度边,并用递增)&'+'+)
。我们将在第二秒看到为什么这是有道理的。最后,我们通过以下方式移动了新的优势=}
:
(特定的边值来自挑战中给出的最后一个测试用例。)这时,循环重复进行,但是所有东西都向东北移动了一个六边形。因此,在阅读了另一个字符之后,我们得到了:
现在您可以看到我们正在沿着东北对角线逐渐写入输入(减去空格),每个其他边上的字符,直到该字符的长度与标记为length的边平行存储。
当我们完成输入循环后,内存将如下所示(我已经为下一部分标记了一些新的边):
该%
是我们读到的最后一个字符,则29
是我们读的非空格字符数。现在我们要找到六边形的边长。首先,深绿色/灰色路径中有一些线性初始化代码:
=&''3{
在这里,=&
将长度(在我们的示例中为29)复制到标记为length的边中。然后''3
移至标记为3的边并将其值设置为3
(我们只需要在计算中将其作为常数)即可。最后{
移动到标记为N(N-1)的边缘。
现在我们进入蓝色循环。该循环递增N
(存储在标记为N的单元格中),然后计算其居中的六边形数,并从输入长度中减去它。这样做的线性代码是:
{)')&({=*'*)'-
这里,{)
移动到并递增Ñ。')&(
移动到标记为N-1的边缘,在N
此处复制并递减。{=*
计算其乘积N(N-1)。'*)
将其乘以常数,3
并在标记为hex(N)的边中增加结果。如预期的那样,这是第N个居中的六角形数字。最后'-
计算该长度与输入长度之间的差。如果结果是肯定的,则边长还不够大,并重复循环(}}
将MP返回到标记为N(N-1)的边缘)。
一旦边长足够大,差异将为零或负,我们得到以下结果:
首先,现在有很长的线性绿色路径,该路径对输出回路进行了一些必要的初始化:
{=&}}}32'"2'=&'*){=&')&}}
在{=&
通过复制结果在开始DIFF边缘插入长度边缘,因为我们以后需要非正面那里。}}}32
在标记为space的边缘写入32 。'"2
将常数2写入diff上方未标记的边缘。'=&
复制N-1
到具有相同标签的第二条边。'*)
将其乘以2并递增,以便在顶部标记为2N-1的边缘中获得正确的值。这是六边形的直径。{=&')&
将直径复制到标记为2N-1的另一边中。最后}}
移回顶部标记为2N-1的边缘。
让我们重新标记边缘:
我们当前位于(仍保留六边形直径)的边缘将用于在输出的各行上进行迭代。标记为缩进的边将计算当前行上需要多少空格。带有边缘标记的单元格将用于迭代当前行中的单元格数量。
我们现在在计算indent的粉红色路径上。('-
减少行迭代器,并将其从N-1中减去(缩进边缘)。代码中的蓝色/灰色短分支只是计算结果的模数(~
如果该值是负数或零,则取反,如果是正数,则无任何反应)。粉色路径的其余部分是从直径"-~{
减去凹痕到像元边缘,然后移回凹痕边缘。
现在,肮脏的黄色路径将打印缩进。循环内容实际上只是
'";{}(
当'"
移动到空间边缘,;
打印它,{}
移动回缩进和(
递减它。
完成后,(第二个)深灰色路径将搜索要打印的下一个字符。的=}
在位置移动时(这意味着,到细胞边缘,指向南)。然后,我们有一个非常紧密的循环,{}
该循环只是简单地沿西南方向向下移动了两个边缘,直到到达存储字符串的末尾:
注意,我在EOF处重新标记了一条边吗?。处理完此字符后,将使该边为负,以便{}
循环将在此处终止而不是下一次迭代:
在代码中,我们位于深灰色路径的末尾,在该路径的'
后退一步到输入字符。如果情况是最后两个图表之一(即输入中仍有一个字符尚未打印),那么我们正在走绿色道路(最下面的道路是针对那些对绿色和绿色不好的人)蓝色)。那很简单:;
打印角色本身。'
移动到相应的空间边缘,该边缘仍保持较早的32,并;
打印该空间。然后{~
使我们的EOF?下一次迭代的结果为负,请'
后退一步,以便我们可以通过另一个紧密}{
循环返回到字符串的西北端。在长度上结束单元格(十六进制(N)下的非正数。最后}
移回到单元格边缘。
如果我们已经用尽了输入,那么搜索EOF的循环?实际上将在此处终止:
在这种情况下,'
移动到长度单元格上,我们改为采用浅蓝色(顶部)路径,该路径打印无操作。该分支中的代码是线性的:
{*46;{{;{{=
在{*46;
写入一个46到边缘标记的无操作并打印(即,周期)。然后{{;
移至空白边缘并进行打印。该{{=
给移回细胞边缘为下一次迭代。
在这一点上,路径重新连接在一起并(
减小了单元的边缘。如果迭代器还不为零,我们将采用浅灰色路径,该路径将MP的方向简单地反转=
,然后寻找下一个要打印的字符。
否则,我们将到达当前行的末尾,而IP将采用紫色路径。这是内存网格在那时的样子:
紫色路径包含以下内容:
=M8;~'"=
将=
再次反转MP的方向。M8
将其值设置为778
(因为M
is 77
和数字的字符代码会将其自身附加到当前值)。碰巧是10 (mod 256)
,因此当我们使用进行打印时;
,我们得到了换行符。然后~
使边缘再次'"
变为负值,移回行边缘并再次=
反转MP。
现在,如果线的边缘为零,就完成了。IP将采用(非常短的)红色路径,从而@
终止程序。否则,我们将继续沿着紫色的路径前进,该路径将循环回到粉红色的路径,以打印另一行。
用Timwi的HexagonyColorer创建的控制流程图。用可视调试器在他的Esoteric IDE中创建的内存图。
abc`defg
实际上会变成pastebin.com/ZrdJmHiR