Lösen Sie das Halteproblem für Modilar SNISP


10

Im Sinne von Lösen des Halteproblems für Befinge definieren wir eine andere 2D-Sprache namens Modilar SNISP . Modilar SNISP hat die folgenden sechs Anweisungen:

  • \ lenkt den Befehlszeiger wie folgt:
    • Wenn Sie sich von oben nähern, gehen Sie nach rechts.
    • Wenn Sie sich von rechts nähern, gehen Sie nach oben.
    • Wenn Sie sich von unten nähern, gehen Sie nach links.
    • Wenn Sie sich von links nähern, gehen Sie nach unten.
  • / lenkt den Befehlszeiger wie folgt:
    • Wenn Sie sich von oben nähern, gehen Sie nach links.
    • Wenn Sie sich von links nähern, gehen Sie nach oben.
    • Wenn Sie sich von unten nähern, gehen Sie nach rechts.
    • Wenn Sie sich von rechts nähern, gehen Sie nach unten.
  • ! überspringt die nächste Anweisung.
  • @ schiebt den IP-Speicherort und die IP-Richtung auf den Aufrufstapel.
  • #Entfernt einen IP-Speicherort und eine IP-Richtung aus dem Aufrufstapel, stellt sie wieder her und überspringt dann die nächste Anweisung. Wenn der Aufrufstapel leer ist, wird die Ausführung angehalten.
  • . tut nichts.

Der Anweisungszeiger beginnt in der oberen linken Ecke und geht nach rechts. Wenn es jemals das Spielfeld verlässt, wird die Ausführung angehalten.

Modilares SNISP kann nicht leistungsfähiger sein als ein PDA , da seine einzige Quelle für unbegrenzten Speicher ein Stapel (der Aufrufstapel) mit einem endlichen Alphabet (die Menge aller IP-Paare (Standort, Richtung)) ist. Das Problem des Anhaltens ist für PDAs entscheidbar , daher sollte diese Herausforderung immer möglich sein.

Die Herausforderung

Ihr Ziel ist es, ein Programm zu schreiben, das eine Zeichenmatrix verwendet, die ein Modilar-SNISP-Programm darstellt, und eine von zwei unterschiedlichen Ausgaben zurückgibt, je nachdem, ob es angehalten wird oder nicht.

Dies ist , also gewinnt das kürzeste gültige Programm (gemessen in Bytes ).

Spezifikationen

  • Die Art und Weise, wie Sie eine Zeichenmatrix verwenden, ist flexibel: Eine durch Zeilenumbrüche getrennte Zeichenfolge, ein Zeichenfolgenarray, ein Array von Zeichenarrays, ein 2D-Zeichenarray, ein flaches Zeichenarray mit einer Ganzzahl, die die Breite darstellt usw. sind zulässig. Die Testfälle entscheiden sich für die erste dieser Optionen.
  • Sie können davon ausgehen, dass die Eingabematrix rechteckig ist (Sie müssen also keine kurzen Zeilen auffüllen) und eine Länge und Breite ungleich Null hat.
  • Sie können zwei verschiedene Ausgänge auswählen, nicht nur wahr / falsch.
  • Sie können davon ausgehen , dass die Eingangsmatrix nur gültiger Befehle bestehen ( \, /, !, @, #, und .).
  • Wenn ein Befehl "die nächste Anweisung überspringen" soll, können Sie davon ausgehen, dass eine nächste Anweisung übersprungen werden muss. Insbesondere wird es niemals auftreten, wenn (1) es am Rand des Spielfelds liegt und (2) sich die IP senkrecht zu diesem Rand bewegt, so dass der "nächste Befehl" danach außerhalb des Spielfelds liegen würde.

Testfälle

Das folgende Snippet kann zum Testen von Programmen in der Sprache verwendet werden. Beachten Sie, dass es etwas freizügiger ist als die hier angegebene tatsächliche Spezifikation (z. B. erlaubt es andere Zeichen .als No-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>

Die ungolfed Form finden Sie hier .

Anhalten

.

Das kleinstmögliche Programm. Geht rechts raus.


\\
\/

Windet sich um das Programm und geht nach oben.


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

Geht in einer Schleife. Windet sich durch einen Teil der Strecke in zwei verschiedene Richtungen.


@\!/#
.\@/#

Verwendet alle sechs Befehle.


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

Die Ausführungszeit dieses Programms ist exponentiell in der Anzahl der Wiederholungen von @., aber es wird immer noch angehalten.


Nicht anhalten

!/\
.\/

Ich glaube, dies ist die kürzeste Endlosschleife.


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

Dies windet sich um die Spur und erzeugt gelegentlich Stapelrahmen, bevor es schließlich in einen Zyklus gerät, der unendlich Stapelrahmen erzeugt. Nicht alle Befehle werden tatsächlich verwendet.

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

Erstellt weiterhin Stapelrahmen, aber keiner von ihnen kehrt jemals zurück.


Sandbox (jetzt gelöscht)
Esolanging Fruit

Die Sprache erinnert mich an eine stark vereinfachte Spaltung .
Sundar - Reinstate Monica

1
@sundar Es ist eine Teilmenge von Modular SNUSP , genauso wie Befinge eine Art Teilmenge von Befunge ist.
Esolanging Fruit

Antworten:


4

Python 3 , 639 Bytes, 630 Bytes, 593 Bytes

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

Probieren Sie es online aus!

Ich denke, dies ist eher eine minimale Quelle als Golf ... Ich bin sicher, es gibt einen besseren Weg, um dorthin zu gelangen.

Das Programm arbeitet als vollständiger Dolmetscher für die Sprache. Es hört entweder auf, wenn:

  1. Wir verlassen das Programm
  2. Wir stellen fest, dass wir uns in einer Schleife befinden.

Die Schleifenerkennung ist etwas naiv (und speicherintensiv). Bevor wir jede Bewegung auswerten, werden unsere aktuellen Richtungen, Positionen und Stapel zwischengespeichert. Wenn wir sehen, dass wir an eine Position gekommen sind, an der wir zuvor waren, und uns in dieselbe Richtung bewegen, und unser aktueller Stapel eine Supermenge eines vorherigen Stapels an dieser Position + Richtung ist, dann wissen wir, dass wir uns in einer Schleife befinden und Der Stapel wächst entweder (oder bleibt konstant).

Edit 1 - Danke an Herman L für das Schneiden von "Pass". Schneiden Sie auch "True".

Edit 2 - Lambda-ified einige Funktionen. Reduzierte Anzahl von Retouren. Gibt "True" zum Beenden und "False" zum Nichtbeenden zurück. Nutzung der vorhandenen O-Klasse als Tracking-Objekt, sodass keine N-Klasse erforderlich ist.


Ersetzen class N():passmit class N():0und def t():passmit def t():0scheint zu funktionieren
Herman L

Sie könnten zu einem vollen Programm von einer Funktion ändern , indem Sie ersetzen def e(I)mit I=input(). Auf diese Weise können Sie alle Einrückungen entfernen. Die return xAnweisungen können durch ersetzt werden exit(x).
Amphibological

Könnte def u():return len(O.S)==0 or y()auch werden u=lambda:len(O.S)==0or y(). PS schöne Lösung!
Amphibological

1

JavaScript (ES6), 258 254 Byte

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

Erwartet ein nicht leeres Programm als Array von Zeichenfolgen, wobei jedes Element eine Zeile von Modilar SNISP darstellt. Ausgänge , 1wenn die gegebenen Programm hält, und aus 0anderen Gründen .

Gleiche Logik wie die Antwort von @ machina.widmo . Ein paar fehlgeschlagene Versuche mit alternativen Methoden führten mich zu dem Schluss, dass sie sowieso längeren Code produzieren würden!

Probieren Sie es online aus!

Erläuterung

Ähnlich wie bei der anderen Antwort wird diese Funktion beendet mit:

  • 1 wenn das Programm angehalten wird (z. B. wenn sich die IP vom Raster entfernt oder ein leerer Stapel platzt)
  • 0Wenn die IP eine Position erreicht, die sie bereits besucht hat, bewegt sie sich in dieselbe Richtung und mit einer Obermenge des Stapels, die beim vorherigen Besuch vorhanden war.

Warum die gleiche Richtung?

 1
!\/

Das obige Programm wird angehalten, trifft jedoch dieselbe Position (Zeichen 1) mit einem identischen Stapel, jedoch aus einer anderen Richtung.

Warum eine Obermenge und nicht nur eine Stapelgröße?

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

Dies wird ebenfalls angehalten und die IP trifft viermal aus einer konsistenten Richtung auf Zeichen 4 mit den folgenden Stapelzuständen ( *zeigt die Oberseite des Stapels an):

  • Größe = 2 [a, b] *
  • Größe = 1 [a] *
  • Größe = 1 [b] *
  • Größe = 0 [] *

Wie der Dolmetscher funktioniert

Anweisungen ( q) werden cwie folgt in binär ( ) übersetzt (wobei alle anderen Zeichen .oder auf andere Weise als Nops dienen):

1 2 4 8 16
\ ! @ / #

Direction ( d) wird als Bitfeld dargestellt:

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

Spiegel ( \/) verändern die Richtung:

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

d/4 | 4*d&12

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

(d+5) % 10

Neue Richtungen verändern die Position:

x += d>>2 - 1

y += d&3 - 1

Andere globale Variablen

  • x, y: IP-Position
  • r: Hält den Wert vom Stapel
  • k: falsch, wenn die nächste Anweisung übersprungen wird (z. B. von !#)
  • s: Stapel
  • v: Zwischenspeichert besuchte Positionen, Richtung, Momentaufnahme des Stapels
  • w: [x, y, d], der Wert, der auf dem Stapel gespeichert und als Schlüsselwert für verwendet wirdv
  • e: falsch, wenn das Programm aufgrund einer Cache-Übereinstimmung nicht angehalten wird

Ungolfed

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.