Modilar SNISPの停止問題を解決する


10

Befingeの停止問題を解決するという精神でModilar SNISPと呼ばれる別の2D言語を定義しましょう。Modilar SNISPには、次の6つの手順があります。

  • \ 命令ポインタを次のように指示します。
    • 上から近づいた場合は右に進みます。
    • 右から近づいたら上へ。
    • 下から近づいた場合は左に進みます。
    • 左から近づいたら下へ。
  • / 命令ポインタを次のように指示します。
    • 上から近づいた場合は左へ。
    • 左から近づいたら上へ。
    • 下から近づいた場合は右に進みます。
    • 右から近づいたら下がる。
  • ! 次の命令をスキップします。
  • @ IPの場所と方向を呼び出しスタックにプッシュします。
  • #呼び出しスタックからIPの場所と方向をポップして復元し、次の命令をスキップします。呼び出しスタックが空の場合、実行は停止します。
  • . 何もしません。

命令ポインタは、左上隅から右に向かって始まります。それがプレイフィールドを離れると、実行は停止します。

Modilar SNISPはPDAよりも強力ではありません。無制限のストレージの唯一のソースは有限のアルファベット(すべてのIP(場所、方向)ペアのセット)を持つスタック(呼び出しスタック)であるためです。停止の問題はPDA決定できるため、この課題は常に可能です。

チャレンジ

あなたの目標は、Modilar SNISPプログラムを表す文字のマトリックスを受け取り、停止するかどうかに応じて2つの異なる出力の1つを返すプログラムを作成することです。

これはなので、有効な最短のプログラム(バイト単位)が優先されます

仕様書

  • 文字のマトリックスを取る方法は柔軟です。改行で区切られた文字列、文字列の配列、文字の配列の配列、2次元の文字の配列、幅を表す整数を持つ文字のフラットな配列などはすべて受け入れられます。テストケースでは、最初の選択肢を選択します。
  • 入力行列は長方形で(短い行にパディングする必要がないため)、長さと幅がゼロでないと想定できます。
  • 真実/偽物だけでなく、2つの異なる出力を選択できます。
  • あなたが入力行列が唯一の有効なコマンドで構成されることを前提とすることができます(\/!@#、および.)。
  • コマンドが「次の命令をスキップする」と言われた場合、スキップする次の命令があると想定できます。特に、(1)IPがプレイフィールドのエッジにあり、(2)IPがそのエッジに対して垂直に移動しているため、「次の命令」がプレイフィールドの外側にあるような状況では発生しません。

テストケース

次のスニペットは、言語でプログラムをテストするために使用できます。これは、ここで指定された実際の仕様よりも若干許容範囲が広いことに注意してください(たとえば.、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>

非ゴルフフォームはここにあります

停止

.

可能な最小のプログラム。右に出ます。


\\
\/

プログラムに巻き付き、トップを出ます。


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

ループに入ります。トラックの一部を2つの異なる方向に巻き付けます。


@\!/#
.\@/#

6つのコマンドすべてを使用します。


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

このプログラムの実行時間は、の繰り返し数で指数関数的です@.が、それでも停止します。


停止しない

!/\
.\/

これが最短の無限ループだと思います。


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

これはトラックを巻き、たまにスタックフレームを生成し、最終的には無限にスタックフレームを生成するサイクルに巻き込まれます。すべてのコマンドが実際に使用されるわけではありません。

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

スタックフレームを作成し続けますが、どれも戻りません。



言語は私にはるかに単純化された分裂を思い出させます。
sundar-モニカを

1
@sundarこれは、モジュラーSNUSPのサブセットです。これは、BefingeがBefungeのサブセット(の一種)と同じです。
Esolanging Fruit '19

回答:


4

Pythonの3639バイト 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に感謝。「True」もカット。

編集2-ラムダ化されたいくつかの関数。返品数の削減。終了する場合は「True」、終了しない場合は「False」を返します。既存のOクラスを追跡オブジェクトとして活用し、Nクラスの必要性を排除しました。


class N():passwith class N():0def t():passwithの置き換えはdef t():0うまくいくようです
Herman L

あなたは交換することにより、完全なプログラムに関数から変えることができるdef e(I)I=input()。これにより、すべてのインデントを削除できます。return xステートメントはと交換することができますexit(x)
2018

またdef u():return len(O.S)==0 or y()なる可能性がありu=lambda:len(O.S)==0or y()ます。PS素敵なソリューション!
2018

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がグリッドから移動した場合、または空のスタックがポップされた場合)
  • 0IPが到達した場合の位置を、それはすでに同じに移動し、訪問した 方向、および持つスーパーセット以前の訪問中に、スタックに存在します。

なぜ同じ方向ですか?

 1
!\/

上記のプログラムは停止しますが、同じスタックで同じ位置(文字1)をヒットしますが、方向は異なります。

単にスタックサイズではなく、なぜスーパーセットなのですか?

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

これも停止し、IPは文字4を一貫した方向から4回ヒットし、次のスタック状態を*示します(スタックの先頭を示します)。

  • サイズ= 2 [a、b] *
  • サイズ= 1 [a] *
  • サイズ= 1 [b] *
  • サイズ= 0 [] *

インタープリターの仕組み

指示(q)はc、次のようにバイナリ()に変換されます(他のすべての文字を使用する.か、それ以外の場合はnopsとして機能します)。

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.