本着为Befinge解决停机问题的精神,让我们定义另一种称为Modilar SNISP的 2D语言。Modilar SNISP具有以下六个指令:
\
指令指令指针如下:- 如果从顶部接近,请向右走;
- 如果从右边靠近,则往上走;
- 如果从底部接近,则向左走;
- 如果从左侧接近,则向下走。
/
指令指令指针如下:- 如果从顶部接近,则向左走;
- 如果从左侧接近,则上升;
- 如果从底部接近,请向右走;
- 如果从右边靠近,则往下走。
!
跳过下一条指令。@
将IP位置和方向推送到调用堆栈中。#
从调用堆栈中弹出IP位置和方向并进行恢复,然后跳过下一条指令。如果调用堆栈为空,则执行停止。.
什么也没做。
指令指针从右上角的左上角开始。如果它离开了运动场,执行将停止。
模块化SNISP不能比PDA强大,因为它的唯一无限存储源是带有有限字母(所有IP(位置,方向)对的集合)的堆栈(调用堆栈)。暂停问题对于PDA来说是可以决定的,因此这一挑战应该总是可能的。
挑战
您的目标是编写一个程序,该程序使用代表Modilar SNISP程序的字符矩阵,并根据是否停止而返回两个不同的输出之一。
这是代码高尔夫球,因此最短的有效程序(以字节为单位)获胜。
技术指标
- 字符矩阵的处理方式很灵活:可以使用换行符分隔的字符串,字符串数组,字符数组,二维字符数组,带整数表示宽度的扁平字符数组等。测试用例选择其中的第一个。
- 您可以假设输入矩阵将是矩形的(因此您不必填充短行)并且长度和宽度将为非零。
- 您可以选择任意两个不同的输出,而不仅仅是真/假。
- 你可以假设输入矩阵将只包含有效的命令(
\
,/
,!
,@
,#
,和.
)。 - 当说一条命令“跳过下一条指令”时,您可以假定将要跳过下一条指令。特别是,在以下情况下将永远不会遇到它:(1)它位于运动场的边缘,并且(2)IP垂直于该边缘移动,以使IP之后的“下一条指令”位于运动场之外。
测试用例
以下代码段可用于测试使用该语言的程序。请注意,它比此处给出的实际规范稍微宽松一些(例如,它允许使用.
非禁忌字符)。
function htmlEscape(t){let i=document.createElement("span");return i.innerText=t,i.innerHTML}function tick(){snisp.tick(),snisp.update()}function run(){runButton.style.display="none",stopButton.style.display="",code.style.display="none",executionArea.style.display="",snisp.initialize(),intervalId=setInterval(tick,INTERVAL_MS)}function stop(){runButton.style.display="",stopButton.style.display="none",code.style.display="",executionArea.style.display="none",clearInterval(intervalId)}let TICKS_PER_SECOND=5,INTERVAL_MS=1e3/TICKS_PER_SECOND,runButton=document.getElementById("run-button"),stopButton=document.getElementById("stop-button"),code=document.getElementById("code"),executionArea=document.getElementById("execution-display"),intervalId,snisp={x:null,y:null,direction:null,callStack:null,stopped:null,playfield:null,padRows:function(){let t=Math.max(...this.playfield.map(t=>t.length));for(let i=0;i<this.playfield.length;i++)this.playfield[i]=this.playfield[i].padEnd(t,".")},initialize:function(){this.x=0,this.y=0,this.direction="right",this.callStack=[],this.stopped=!1,this.playfield=code.value.split("\n"),this.padRows(),this.update()},getCurrentChar:function(){let t=this.playfield[this.y];if(void 0!=t)return t[this.x]},backslashMirror:function(){let t={up:"left",right:"down",down:"right",left:"up"};this.direction=t[this.direction]},slashMirror:function(){let t={up:"right",right:"up",down:"left",left:"down"};this.direction=t[this.direction]},forward:function(){switch(this.direction){case"up":this.y-=1;break;case"down":this.y+=1;break;case"left":this.x-=1;break;case"right":this.x+=1;break;default:throw"direction is invalid"}},pushState:function(){this.callStack.push({x:this.x,y:this.y,direction:this.direction})},restoreState:function(){let t=this.callStack.pop();void 0!=t?(this.x=t.x,this.y=t.y,this.direction=t.direction):this.stopped=!0},tick:function(){if(this.stopped)return;let t=this.getCurrentChar();if(void 0!=t){switch(t){case"\\":this.backslashMirror();break;case"/":this.slashMirror();break;case"!":this.forward();break;case"@":this.pushState();break;case"#":this.restoreState(),this.forward()}this.forward()}else this.stopped=!0},generatePlayfieldHTML:function(t,i){let e=[];for(let n=0;n<this.playfield.length;n++){let s=[],l=this.playfield[n];for(let e=0;e<l.length;e++){let a=htmlEscape(l[e]);e==t&&n==i&&(a='<span class="highlight">'+a+"</span>"),s.push(a)}e.push(s.join(""))}return e.join("<br>")},update:function(){let t=[];for(let i=0;i<this.callStack.length;i++){let e=this.callStack[i];t.push(this.generatePlayfieldHTML(e.x,e.y))}t.push(this.generatePlayfieldHTML(this.x,this.y));let i=t.join("<br><br>");executionArea.innerHTML=i}};
#code{font-family:monospace;}#execution-display{font-family:monospace;white-space:pre;}.highlight{background-color:yellow;}
<b>Code:</b><br/><textarea id="code" width="300" height="300"></textarea><br/><button id="run-button" onclick="run()">Run</button><button id="stop-button" onclick="stop()" style="display: none;">Stop</button><br/><div id="execution-display"></div>
可以在这里找到未填写的表格。
停止
.
最小的程序。出去右边。
\\
\/
缠绕程序并退出顶部。
.\./.\
.\!/./
循环进行。沿两个不同方向绕过部分轨道。
@\!/#
.\@/#
使用所有六个命令。
@.@.@.@.@.@.@.@.@.#
该程序的执行时间与的重复次数成指数关系@.
,但仍会暂停。
非停止
!/\
.\/
我相信这是最短的无限循环。
@!\\#/@\!\
//@//.#./.
.\#.!\./\.
#.\!@!\@//
/..@.@\/#!
\.@.#.\/@.
它绕着轨道缠绕,偶尔会生成堆栈帧,然后最终陷入无限生成堆栈帧的循环中。并非实际上使用了所有命令。
.!/@.@.@.@.@.\
/.@.@.@.@.@.@/
\@.@.@.@.@.@.\
/.@.@.@.@.@.@/
.@\@.@.@.@.@.\
\.@.@.@.@.@.@/
继续创建堆栈框架,但是没有一个返回。