Theo tinh thần Giải quyết vấn đề tạm dừng cho Befinge , hãy xác định một ngôn ngữ 2D khác có tên Modilar SNISP . Modilar SNISP có sáu hướng dẫn sau:
\
chỉ dẫn con trỏ lệnh như sau:- nếu tiếp cận từ trên xuống, đi bên phải;
- nếu tiếp cận từ bên phải, đi lên;
- nếu tiếp cận từ dưới lên, đi bên trái;
- nếu tiếp cận từ bên trái, đi xuống.
/
chỉ dẫn con trỏ lệnh như sau:- nếu tiếp cận từ trên xuống, đi bên trái;
- nếu tiếp cận từ bên trái, đi lên;
- nếu tiếp cận từ dưới lên, đi bên phải;
- nếu tiếp cận từ bên phải, đi xuống.
!
bỏ qua hướng dẫn tiếp theo@
đẩy vị trí và hướng IP lên ngăn xếp cuộc gọi.#
bật một vị trí và hướng IP từ ngăn xếp cuộc gọi và khôi phục chúng, sau đó bỏ qua hướng dẫn tiếp theo. Nếu ngăn xếp cuộc gọi trống, thực thi dừng lại..
Không lam gi cả.
Con trỏ lệnh bắt đầu ở góc trên bên trái đi bên phải. Nếu nó rời khỏi sân chơi, hãy dừng lại.
Modilar SNISP không thể mạnh hơn một thiết bị PDA , vì nguồn lưu trữ không giới hạn duy nhất của nó là một ngăn xếp (ngăn xếp cuộc gọi) với một bảng chữ cái hữu hạn (tập hợp tất cả các cặp IP (vị trí, hướng)). Vấn đề tạm dừng là có thể quyết định đối với các thiết bị PDA , vì vậy thách thức này luôn luôn có thể xảy ra.
Các thách thức
Mục tiêu của bạn là viết một chương trình lấy một ma trận các ký tự đại diện cho chương trình SNISP Modilar và trả về một trong hai đầu ra riêng biệt tùy thuộc vào việc nó có dừng hay không.
Đây là môn đánh gôn , vì vậy chương trình hợp lệ ngắn nhất (tính bằng byte ) sẽ thắng.
Thông số kỹ thuật
- Cách bạn lấy một ma trận ký tự là linh hoạt: một chuỗi phân tách dòng mới, mảng chuỗi, mảng mảng ký tự, mảng ký tự 2d, mảng ký tự phẳng có số nguyên biểu thị chiều rộng, v.v ... đều được chấp nhận. Các trường hợp thử nghiệm lựa chọn cho sự lựa chọn đầu tiên.
- Bạn có thể giả sử rằng ma trận đầu vào sẽ là hình chữ nhật (vì vậy bạn không phải đệm các hàng ngắn) và sẽ có chiều dài và chiều rộng khác không.
- Bạn có thể chọn bất kỳ hai kết quả đầu ra khác biệt, không chỉ là sự thật / giả.
- Bạn có thể giả định rằng ma trận đầu vào sẽ chỉ gồm các lệnh hợp lệ (
\
,/
,!
,@
,#
, và.
). - Khi một lệnh được cho là "bỏ qua hướng dẫn tiếp theo", bạn có thể cho rằng sẽ có một lệnh tiếp theo để bỏ qua. Đặc biệt, nó sẽ không bao giờ gặp phải trong trường hợp (1) nó nằm ở rìa của sân chơi và (2) IP di chuyển vuông góc với cạnh đó, sao cho "hướng dẫn tiếp theo" sau khi nó nằm bên ngoài sân chơi.
Các trường hợp thử nghiệm
Đoạn mã sau có thể được sử dụng để kiểm tra các chương trình bằng ngôn ngữ. Lưu ý rằng nó được cho phép hơn một chút so với thông số kỹ thuật thực tế được đưa ra ở đây (ví dụ: nó cho phép các ký tự không phải .
là không có op).
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>
Các hình thức vô danh có thể được tìm thấy ở đây .
Ngừng
.
Chương trình nhỏ nhất có thể. Đi ra bên phải.
\\
\/
Gió quanh chương trình và đi ra khỏi đầu.
.\./.\
.\!/./
Đi trong một vòng lặp. Gió qua một phần của đường đua theo hai hướng khác nhau.
@\!/#
.\@/#
Sử dụng tất cả sáu lệnh.
@.@.@.@.@.@.@.@.@.#
Thời gian thực hiện của chương trình này là theo cấp số nhân của số lần lặp lại @.
, nhưng nó vẫn dừng lại.
Không dừng lại
!/\
.\/
Tôi tin rằng đây là vòng lặp vô hạn ngắn nhất.
@!\\#/@\!\
//@//.#./.
.\#.!\./\.
#.\!@!\@//
/..@.@\/#!
\.@.#.\/@.
Thỉnh thoảng điều này xoay quanh đường đua, sinh ra các khung stack, trước khi cuối cùng bị cuốn vào một chu kỳ tạo ra các khung stack. Không phải tất cả các lệnh thực sự được sử dụng.
.!/@.@.@.@.@.\
/.@.@.@.@.@.@/
\@.@.@.@.@.@.\
/.@.@.@.@.@.@/
.@\@.@.@.@.@.\
\.@.@.@.@.@.@/
Tiếp tục tạo khung stack, nhưng không ai trong số họ từng quay lại.