解决模块化SNISP的暂停问题


10

本着为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>

可以在这里找到未填写的表格。

停止

.

最小的程序。出去右边。


\\
\/

缠绕程序并退出顶部。


.\./.\
.\!/./

循环进行。沿两个不同方向绕过部分轨道。


@\!/#
.\@/#

使用所有六个命令。


@.@.@.@.@.@.@.@.@.#

该程序的执行时间与的重复次数成指数关系@.,但仍会暂停。


非停止

!/\
.\/

我相信这是最短的无限循环。


@!\\#/@\!\
//@//.#./.
.\#.!\./\.
#.\!@!\@//
/..@.@\/#!
\.@.#.\/@.

它绕着轨道缠绕,偶尔会生成堆栈帧,然后最终陷入无限生成堆栈帧的循环中。并非实际上使用了所有命令。

.!/@.@.@.@.@.\
/.@.@.@.@.@.@/
\@.@.@.@.@.@.\
/.@.@.@.@.@.@/
.@\@.@.@.@.@.\
\.@.@.@.@.@.@/

继续创建堆栈框架,但是没有一个返回。


沙盒(现已删除)
结实的水果

该语言使我想起了简化的裂变
sundar-恢复莫妮卡

1
@sundar是Modular SNUSP的子集,就像Befinge是Befunge的子集(类似)一样。
Esolanging Fruit

Answers:


4

Python 3中639字节 630个字节 593个字节

def e(I):
 m=[(0,-1),(0,1),(1,1),(1,-1)];a=lambda i:(tuple(i[0]),i[1]);b=lambda s,q:s.s==q.s and s.S&q.S==q.S
 class O():i=[[0,0],2];S=[];A={}
 def z():d=m[O.i[1]];O.i[0][d[0]]+=d[1]
 def y():O.i=O.S.pop();z()
 def x():O.i[1]=[3,2,1,0][O.i[1]]
 def w():O.i[1]=[2,3,0,1][O.i[1]]
 def v():O.S+=[[O.i[0][:],O.i[1]]]
 while 1:
  p=O();p.s=a(O.i);p.S={a(i)for i in O.S};l=O.A.setdefault(p.s,[]);c=any((b(p,s)for s in l));l+=[p];e=O.i[0];d=not((0<=e[0]<len(I))and(0<=e[1]<len(I[0])))or((x,w,z,v,lambda:len(O.S)==0 or y(),lambda:0)["\\/!@#.".find(I[e[0]][e[1]])]()==1);z()
  if d!=c:return not c or d

在线尝试!

我觉得这比高尔夫更像是矿物质……我敢肯定,有更好的方法去那里。

该程序可作为该语言的完整口译员。它在以下情况下停止:

  1. 我们退出程序
  2. 我们检测到我们处于循环中。

循环检测有些天真(并且占用大量内存)。在评估每个动作之前,我们先缓存当前的“方向”,“位置”和“堆栈”。如果我们看到我们到达了以前去过的位置,朝着相同的方向移动,并且我们当前的堆栈是该位置+方向上先前堆栈的超集,那么我们就知道我们处于循环中,堆栈正在增长(或保持不变)。

编辑1-感谢Herman L削减“通过”。还切“真”。

编辑2-Lambda化了一些功能。减少退货数量。返回“ True”表示终止,返回“ False”表示不终止。利用现有的O类作为跟踪对象,从而消除了对N类的需求。


更换class N():passclass N():0def t():passdef t():0,似乎工作
赫尔曼大号

你可以通过替换从一个功能改变为一个完整的程序def e(I)I=input()。这使您可以删除所有缩进。该return x语句可以被替换exit(x)
血液学

也有def u():return len(O.S)==0 or y()可能成为u=lambda:len(O.S)==0or y()。PS不错的解决方案!
血液学

1

的JavaScript(ES6),258个 254字节

p=>(d=>{for(x=y=r=k=1,s=[],v={};w=[--x,--y,d],c=1<<"\\!@/#".indexOf(q=(p[y]||0)[x]),q&&r&&(e=v[w]?v[w].some(u=>!s.some(t=>u+0==t+0)):1);x+=d>>2,y+=d&3)v[w]=[...s],k=k?c&9?d=c&1?d/4|4*d&12:(d+5)%10:c&4?s.push(w):c&16?(r=s.pop())&&!([x,y,d]=r):c-2:1})(9)|e

期望将非空程序作为字符串数组,其中每个元素代表一行Modilar SNISP。1如果给定程序停止,0则输出, 否则返回。

@ machina.widmo的答案具有相同的逻辑。在替代方法上的几次失败尝试使我得出结论,无论如何它们都会产生更长的代码!

在线尝试!

说明

与其他答案类似,此函数以以下形式退出:

  • 1 如果程序停止(例如,IP从网格移出或弹出了一个空堆栈)
  • 0如果IP到达一个已经访问过的位置,并朝同一 方向移动,并且具有上一次访问中存在的堆栈的超集

为什么方向相同?

 1
!\/

上面的程序暂停,但是击中相同位置(字符1),但堆栈相同,但方向不同。

为什么是超集而不是仅堆栈大小?

  ab4
!/@@.\
.\..#/

这也会暂停,并且IP从一致的方向击中字符4四次,并具有以下堆栈状态(*指示堆栈的顶部):

  • 大小= 2 [a,b] *
  • 大小= 1 [a] *
  • 大小= 1 [b] *
  • 大小= 0 [] *

口译员的工作方式

指令(q)转换为二进制(c)的方式如下(带有所有其他字符,.否则用作点):

1 2 4 8 16
\ ! @ / #

方向(d)表示为位字段:

9 -> right : 1001
1 -> left  : 0001
6 -> down  : 0110
4 -> up    : 0100

镜子(\/)改变方向:

\:6-> 9 9-> 6 4-> 1 1-> 4

d/4 | 4*d&12

/:1-> 6 6-> 1 4-> 9 9-> 4

(d+5) % 10

新方向改变了位置:

x += d>>2 - 1

y += d&3 - 1

其他全局变量

  • xy:IP位置
  • r:持有从堆栈中弹出的值
  • k:如果跳过下一条指令(例如,来自!#),则错误
  • s:堆栈
  • v:缓存访问的位置,方向,堆栈快照
  • w[x, y, d],该值存储在堆栈中并用作以下项的键值:v
  • e:如果由于缓存匹配而导致程序没有停止,则为假

不打高尔夫球

p => (d => {                                                  // set initial direction and avoid a verbose `return` statement
    for (
        x = y = r = k = 1,
        s = [],
        v = {};
        w = [--x, --y, d],                                    // decrement positions early so that x,y 
                                                              // do not require a separate assignment to 0
        c = 1 << "\\!@/#".indexOf(q = (p[y]||0)[x]),          // taking an index of undefined produces an error; using 0 does not
        q && r && (
            e = v[w]
                ? v[w].some(u => !s.some(t => u+0 == t+0))    // in order to compare two arrays, must coerce to strings
                : 1
        );
        x += d>>2,
        y += d&3
    )
        v[w] = [...s],                         // clone stack
        k = k
            ?
                c&9                            // if \ or /
                    ? d = c&1
                        ? d/4 | 4*d&12
                        : (d+5) % 10
                : c&4                          // if @
                    ? s.push(w)
                : c&16                         // if #
                    ? (r = s.pop())
                        && !([x, y, d] = r)    // destructure value in stack if any exists
                : c-2                          // 0 if !
            : 1
})(9) | e
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.