No espírito de resolver o problema de parada para Befinge , vamos definir outra linguagem 2D chamada Modilar SNISP . O SNISP Modilar tem as seguintes seis instruções:
\
direciona o ponteiro da instrução da seguinte maneira:- se abordado de cima, vá para a direita;
- se for abordado da direita, suba;
- se aproximado de baixo, vá para a esquerda;
- se aproximado da esquerda, desça.
/
direciona o ponteiro da instrução da seguinte maneira:- se abordado de cima, vá para a esquerda;
- se abordado a partir da esquerda, suba;
- se abordado de baixo, vá para a direita;
- se aproximado da direita, desça.
!
pula a próxima instrução.@
coloca a localização e a direção do IP na pilha de chamadas.#
exibe um local IP e uma direção da pilha de chamadas e os restaura e depois pula a próxima instrução. Se a pilha de chamadas estiver vazia, a execução será interrompida..
faz nada.
O ponteiro de instruções começa no canto superior esquerdo, indo para a direita. Se ele sair do campo, a execução será interrompida.
O SNISP Modilar não pode ser mais poderoso que um PDA , porque sua única fonte de armazenamento ilimitado é uma pilha (a pilha de chamadas) com um alfabeto finito (o conjunto de todos os pares IP (local, direção)). O problema da parada é decidível para os PDAs , portanto esse desafio sempre deve ser possível.
O desafio
Seu objetivo é escrever um programa que utilize uma matriz de caracteres representando um programa SNISP Modilar e retorne uma das duas saídas distintas, dependendo de parar ou não.
Isso é código-golfe , então o programa válido mais curto (medido em bytes ) vence.
Especificações
- A maneira como você utiliza uma matriz de caracteres é flexível: uma string separada por nova linha, matriz de cadeias, matriz de matrizes de caracteres, matriz 2D de caracteres, matriz plana de caracteres com um número inteiro representando a largura etc. são todos aceitáveis. Os casos de teste optam pela primeira dessas opções.
- Você pode assumir que a matriz de entrada será retangular (portanto, não será necessário preencher linhas curtas) e terá comprimento e largura diferentes de zero.
- Você pode escolher duas saídas distintas, não apenas verdade / falsidade.
- Você pode assumir que a matriz de entrada irá consistir apenas de comandos válidos (
\
,/
,!
,@
,#
, e.
). - Quando se diz que um comando "ignora a próxima instrução", você pode assumir que haverá uma próxima instrução a ignorar. Em particular, ele nunca será encontrado em circunstâncias em que (1) esteja na borda do campo de jogo e (2) o IP esteja se movendo perpendicularmente a essa borda, de modo que a "próxima instrução" depois dela esteja fora do campo de jogo.
Casos de teste
O fragmento a seguir pode ser usado para testar programas no idioma. Observe que é um pouco mais permissivo do que a especificação real fornecida aqui (por exemplo, permite caracteres diferentes de .
não-ops).
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>
A forma não destruída pode ser encontrada aqui .
Parada
.
O menor programa possível. Sai pela direita.
\\
\/
Enrola o programa e sai do topo.
.\./.\
.\!/./
Entra em loop. Enrola parte da pista em duas direções diferentes.
@\!/#
.\@/#
Usa todos os seis comandos.
@.@.@.@.@.@.@.@.@.#
O tempo de execução deste programa é exponencial no número de repetições de @.
, mas ainda pára.
Non-Halting
!/\
.\/
Eu acredito que este é o loop infinito mais curto.
@!\\#/@\!\
//@//.#./.
.\#.!\./\.
#.\!@!\@//
/..@.@\/#!
\.@.#.\/@.
Isso gira ao redor da pista, gerando quadros de pilha ocasionalmente, antes de eventualmente ser pego em um ciclo gerando infinitamente quadros de pilha. Nem todos os comandos são realmente usados.
.!/@.@.@.@.@.\
/.@.@.@.@.@.@/
\@.@.@.@.@.@.\
/.@.@.@.@.@.@/
.@\@.@.@.@.@.\
\.@.@.@.@.@.@/
Continua criando quadros de pilha, mas nenhum deles retorna.