Вирішіть задачу зупинки для Моділярного SNISP


10

В дусі розв’язання задачі зупинки для Befinge давайте визначимося з іншою двовимірною мовою під назвою Modilar SNISP . Моділар SNISP має наступні шість інструкцій:

  • \ спрямовує покажчик інструкцій наступним чином:
    • якщо підійти зверху, йдіть праворуч;
    • якщо підійти праворуч, підніміться вгору;
    • якщо підійти знизу, йдіть ліворуч;
    • якщо підійти зліва, спуститися вниз.
  • / спрямовує покажчик інструкцій наступним чином:
    • якщо підійти зверху, йдіть ліворуч;
    • якщо підійти зліва, підніміться вгору;
    • якщо підійти знизу, йдіть праворуч;
    • якщо підійти праворуч, спустіться вниз.
  • ! пропускає наступну інструкцію.
  • @ висуває IP-адресу та напрямок на стек виклику.
  • #з'являється IP-адреса та напрямок зі стеку викликів та відновлює їх, а потім пропускає наступну інструкцію. Якщо стек викликів порожній, виконання зупиняється.
  • . нічого не робить.

Покажчик інструкцій починається у верхньому лівому куті, йдучи праворуч. Якщо він коли-небудь покине поле гри, виконання зупиняється.

Моділярний SNISP не може бути більш потужним, ніж КПК , оскільки єдиним його джерелом необмеженого сховища є стек (стек виклику) з кінцевим алфавітом (набір усіх пар IP (місцезнаходження, напрямок)). Проблема зупинки вирішується для КПК , тому ця проблема завжди повинна бути можливою.

Змагання

Ваша мета - написати програму, яка бере матрицю символів, що представляє програму Modilar SNISP, і повертає один з двох різних виходів, залежно від того, зупиняється вона чи ні.

Це , тому виграє найкоротша дійсна програма (вимірюється в байтах ).

Технічні умови

  • Те, як ви приймаєте матрицю символів, є гнучким: прийнятний розділений рядок у новому рядку, масив рядків, масив масивів символів, 2d масив символів, плоский масив символів з цілим числом, що представляє ширину тощо. Тестові випадки вибирають перший із них.
  • Ви можете припустити, що вхідна матриця буде прямокутною (тому вам не доведеться прокладати короткі рядки) і матиме ненульову довжину та ширину.
  • Ви можете вибрати будь-які два різних виходи, а не лише триутюкі / хибні.
  • Можна припустити , що вхідні матриця буде складатися тільки з допустимих команд ( \, /, !, @, #, і .).
  • Коли команду кажуть "пропустити наступну інструкцію", ви можете припустити, що буде наступна інструкція пропустити. Зокрема, його ніколи не зустрінеш за обставин, коли (1) він лежить на краю ігрового поля та (2) 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>

Ungolfed форму можна знайти тут .

Зупинка

.

Найменша можлива програма. Виходить праворуч.


\\
\/

Накручується навколо програми і виходить наверх.


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

Іде в петлі. Вітер проходить через частину колії у двох різних напрямках.


@\!/#
.\@/#

Використовує всі шість команд.


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

Час виконання цієї програми є експоненціальним у кількості повторень @., але вона все ще зупиняється.


Неприпинення

!/\
.\/

Я вважаю, що це найкоротший нескінченний цикл.


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

Цей вітер навколо доріжки, нерегулярно нерестуючи рамки стека, перш ніж врешті-решт потрапляє в цикл нескінченно генеруючи кадри стека. Не всі команди фактично використовуються.

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

Продовжує створювати кадри стека, але жоден з них ніколи не повертається.


Пісочниця (зараз видалено)
Esolanging Fruit

Мова нагадує мені значно спрощений Fission .
sundar

1
@sundar Це підмножина модульного SNUSP , так само як Befinge є (різновидом) підмножиною Befunge.
Esolanging Fruit

Відповіді:


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 - Дякую Герману Л за різання "проходу". Також виріжте "Правда".

Редагувати 2 - деякі функції, визначені лямбда. Зменшена кількість віддачі. Повертає "True" для закінчення та "False" для не закінчення. Використовуючи існуючий клас O як об’єкт відстеження, виключаючи потребу в N класі.


Заміна class N():passна class N():0і def t():passз, def 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 [а] *
  • розмір = 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

Інші глобальні змінні

  • x, y: Положення 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.