Ouroboros bu hafta tasarladığım bir esolang. Sıkma zamanı!
i.1+!57*(\m1(M\1).96>.@32*-.80=\.78=3*\.66=3*\.82=5*\81=9*++++\2*1\-*+
)L!4*(4Sn1(
Tek karakterlik komutların ( 1) her satırı, yürütmenin baştan (başlangıç) kuyruğa (uç) ilerlediği ve başa geri döndüğü bir ouroboros yılanını temsil eder. (
ve)
komutlar böylece komutlar idam neyi değiştirerek, kuyruk kısmını yemek ya da kusar sağlar. Talimat işaretçisi yutulursa, yılan ölür (yürütmeyi durdurur). Bir Ouroboros programı, paralel olarak çalışan bir veya daha fazla yılandan oluşur. Her yılanın kendi yığını vardır ve ayrıca paylaşılan bir yığın vardır.
1 Ouroboros'u birçok 2B dilden ayıran bir istisna: çok basamaklı sayılar, matematik yapmadan veya 0'a ilk basmadan doğrudan yazılabilir.
Yılan 1
İlk yılan bir karakteri ( i
) okur ve -1 / EOF ( .1+!
) olup olmadığını kontrol eder . Eğer öyleyse, M
( 57*(
) dahil olmak üzere kuyruğunun çoğunu yer .
Yılan, karakter kodunu yığının ( \
) üzerindeki üstünde bulunan çetele ile değiştirir, çeteyi paylaşılan yığına ( m
) taşır ve başka bir karakteri ( 1(
) yutar . Zaten bir demet yutmuşsa, (
bu IP'nin açık olduğu ve öldüğü anlamına gelir . Aksi takdirde, işlem, çeteleyi yılan 1'in yığınına geri taşıyarak, char koduyla değiştirerek ve önceden yutulan karakterden ( M\1)
) yeniden işlem yaparak devam eder .
Daha sonra karakter için uygun puanı oluşturmak için matematik ve yığın işlemlerini kullanırız. .96>
küçük harf olup olmadığını test eder; sonraki 32*-
büyük harfe dönüşür. Sonra uzun streç .80=
için 81=9*++++
harita P
-> 1
, N
-> 3
vb Son olarak, \2*1\-*
mektup küçük harfli olsaydı skor ortadan kaldırır ve+
çalışan taksitli ekler. Yılan daha sonra döngüler ve başka bir karakter okur.
Yılan 2
İkinci yılan )
, ilk kez hiçbir şey yapmayan (henüz hiçbir şey yutulmadığından ve aynı zamanda boş bir yığını patlattığından beri) bir regürjitasyon işlemi ( ) ile başlar 0
. Daha sonra, paylaşılan yığının uzunluğunu kendi yığınına iter ve mantıksal olarak negatiftir ( L!
). Bu 1
, yığının boş olup olmadığını verir , 0
aksi takdirde. Yılan 4 ile çarpılır ve bu kadar çok karakter ( 4*(
) yer.
Paylaşılan yığın boşsa, bu yılan şimdi S
. Sadece yuttuğu karakterleri yeniden canlandırdığı ve yeniden başladığı yere 4
geri iter )
.
Bununla birlikte, paylaşılan yığın üzerinde bir değer varsa, hiçbir karakter yutulmaz ve yürütme devam eder. Yılan paylaşılan yığına geçer ve oradaki sayıyı çıkarır ( Sn
); sonra son karakterini yutar ve ölür (1(
).
Senkronizasyon
İki yılan dikkatlice senkronize edilmelidir, böylece yılan 2 kontrolünü yaptığında giriş sonuna ulaşılana kadar paylaşılan yığın üzerinde hiçbir zaman bir değer kalmaz. Yılan 1, döngüden her geçişte kısa bir süre paylaşılan yığın üzerine bir değer koyar. Bu nedenle, yılan 2'nin L
komutu asla yılan 1'deki m
ve M
komutları arasında yürütülmemelidir . Neyse ki, yılanlar çok iyi sıralanır. En önemlisi, yılan 1'in döngüsünün uzunluğu (70 talimat) yılan 2'nin döngüsünün katlarıdır (7 talimat), bu yüzden ikisi asla senkronizasyondan çıkmaz:
i.1+!57*(\m1(M\1).96>.@32*-.80=\.78=3*\.66=3*\.82=5*\81=9*++++\2*1\-*+
)L!5*(5)L!5*(5)L!5*(5)L!5*(5)L!5*(5)L!5*(5)L!5*(5)L!5*(5)L!5*(5)L!5*(5
|__|
Danger zone
Rakamlar bu kadar mükemmel bir şekilde çalışmasaydı, gerektiğinde hizalamak için bir veya iki yılanı boşluklarla doldururdum.
Bütün bunlar çok iyi, ama eylemde görmek istiyorum!
Stack Snippet üzerinden yukarıdaki program. Saniyede 1000 işlemde bile, örnek girişi için cevabı vermek yaklaşık 10 saniye sürer - ancak oraya ulaşır!
// Define Stack class
function Stack() {
this.stack = [];
this.length = 0;
}
Stack.prototype.push = function(item) {
this.stack.push(item);
this.length++;
}
Stack.prototype.pop = function() {
var result = 0;
if (this.length > 0) {
result = this.stack.pop();
this.length--;
}
return result;
}
Stack.prototype.top = function() {
var result = 0;
if (this.length > 0) {
result = this.stack[this.length - 1];
}
return result;
}
Stack.prototype.toString = function() {
return "" + this.stack;
}
// Define Snake class
function Snake(code) {
this.code = code;
this.length = this.code.length;
this.ip = 0;
this.ownStack = new Stack();
this.currStack = this.ownStack;
this.alive = true;
this.wait = 0;
this.partialString = this.partialNumber = null;
}
Snake.prototype.step = function() {
if (!this.alive) {
return null;
}
if (this.wait > 0) {
this.wait--;
return null;
}
var instruction = this.code.charAt(this.ip);
var output = null;
if (this.partialString !== null) {
// We're in the middle of a double-quoted string
if (instruction == '"') {
// Close the string and push its character codes in reverse order
for (var i = this.partialString.length - 1; i >= 0; i--) {
this.currStack.push(this.partialString.charCodeAt(i));
}
this.partialString = null;
} else {
this.partialString += instruction;
}
} else if (instruction == '"') {
this.partialString = "";
} else if ("0" <= instruction && instruction <= "9") {
if (this.partialNumber !== null) {
this.partialNumber = this.partialNumber + instruction; // NB: concatenation!
} else {
this.partialNumber = instruction;
}
next = this.code.charAt((this.ip + 1) % this.length);
if (next < "0" || "9" < next) {
// Next instruction is non-numeric, so end number and push it
this.currStack.push(+this.partialNumber);
this.partialNumber = null;
}
} else if ("a" <= instruction && instruction <= "f") {
// a-f push numbers 10 through 15
var value = instruction.charCodeAt(0) - 87;
this.currStack.push(value);
} else if (instruction == "$") {
// Toggle the current stack
if (this.currStack === this.ownStack) {
this.currStack = this.program.sharedStack;
} else {
this.currStack = this.ownStack;
}
} else if (instruction == "s") {
this.currStack = this.ownStack;
} else if (instruction == "S") {
this.currStack = this.program.sharedStack;
} else if (instruction == "l") {
this.currStack.push(this.ownStack.length);
} else if (instruction == "L") {
this.currStack.push(this.program.sharedStack.length);
} else if (instruction == ".") {
var item = this.currStack.pop();
this.currStack.push(item);
this.currStack.push(item);
} else if (instruction == "m") {
var item = this.ownStack.pop();
this.program.sharedStack.push(item);
} else if (instruction == "M") {
var item = this.program.sharedStack.pop();
this.ownStack.push(item);
} else if (instruction == "y") {
var item = this.ownStack.top();
this.program.sharedStack.push(item);
} else if (instruction == "Y") {
var item = this.program.sharedStack.top();
this.ownStack.push(item);
} else if (instruction == "\\") {
var top = this.currStack.pop();
var next = this.currStack.pop()
this.currStack.push(top);
this.currStack.push(next);
} else if (instruction == "@") {
var c = this.currStack.pop();
var b = this.currStack.pop();
var a = this.currStack.pop();
this.currStack.push(c);
this.currStack.push(a);
this.currStack.push(b);
} else if (instruction == ";") {
this.currStack.pop();
} else if (instruction == "+") {
var b = this.currStack.pop();
var a = this.currStack.pop();
this.currStack.push(a + b);
} else if (instruction == "-") {
var b = this.currStack.pop();
var a = this.currStack.pop();
this.currStack.push(a - b);
} else if (instruction == "*") {
var b = this.currStack.pop();
var a = this.currStack.pop();
this.currStack.push(a * b);
} else if (instruction == "/") {
var b = this.currStack.pop();
var a = this.currStack.pop();
this.currStack.push(a / b);
} else if (instruction == "%") {
var b = this.currStack.pop();
var a = this.currStack.pop();
this.currStack.push(a % b);
} else if (instruction == "_") {
this.currStack.push(-this.currStack.pop());
} else if (instruction == "I") {
var value = this.currStack.pop();
if (value < 0) {
this.currStack.push(Math.ceil(value));
} else {
this.currStack.push(Math.floor(value));
}
} else if (instruction == ">") {
var b = this.currStack.pop();
var a = this.currStack.pop();
this.currStack.push(+(a > b));
} else if (instruction == "<") {
var b = this.currStack.pop();
var a = this.currStack.pop();
this.currStack.push(+(a < b));
} else if (instruction == "=") {
var b = this.currStack.pop();
var a = this.currStack.pop();
this.currStack.push(+(a == b));
} else if (instruction == "!") {
this.currStack.push(+!this.currStack.pop());
} else if (instruction == "?") {
this.currStack.push(Math.random());
} else if (instruction == "n") {
output = "" + this.currStack.pop();
} else if (instruction == "o") {
output = String.fromCharCode(this.currStack.pop());
} else if (instruction == "r") {
var input = this.program.io.getNumber();
this.currStack.push(input);
} else if (instruction == "i") {
var input = this.program.io.getChar();
this.currStack.push(input);
} else if (instruction == "(") {
this.length -= Math.floor(this.currStack.pop());
this.length = Math.max(this.length, 0);
} else if (instruction == ")") {
this.length += Math.floor(this.currStack.pop());
this.length = Math.min(this.length, this.code.length);
} else if (instruction == "w") {
this.wait = this.currStack.pop();
}
// Any instruction not covered by the above cases is ignored
if (this.ip >= this.length) {
// We've swallowed the IP, so this snake dies
this.alive = false;
this.program.snakesLiving--;
} else {
// Increment IP and loop if appropriate
this.ip = (this.ip + 1) % this.length;
}
return output;
}
// Define Program class
function Program(source, speed, io) {
this.sharedStack = new Stack();
this.snakes = source.split(/\r?\n/).map(function(snakeCode) {
var snake = new Snake(snakeCode);
snake.program = this;
snake.sharedStack = this.sharedStack;
return snake;
}.bind(this));
this.snakesLiving = this.snakes.length;
this.io = io;
this.speed = speed || 10;
this.halting = false;
}
Program.prototype.run = function() {
if (this.snakesLiving) {
this.step();
this.timeout = window.setTimeout(this.run.bind(this), 1000 / this.speed);
}
}
Program.prototype.step = function() {
for (var s = 0; s < this.snakes.length; s++) {
var output = this.snakes[s].step();
if (output) {
this.io.print(output);
}
}
}
Program.prototype.halt = function() {
window.clearTimeout(this.timeout);
}
var ioFunctions = {
print: function(item) {
var stdout = document.getElementById('stdout');
stdout.value += "" + item;
},
getChar: function() {
if (inputData) {
var inputChar = inputData[0];
inputData = inputData.slice(1);
return inputChar.charCodeAt(0);
} else {
return -1;
}
},
getNumber: function() {
while (inputData && (inputData[0] < "0" || "9" < inputData[0])) {
inputData = inputData.slice(1);
}
if (inputData) {
var inputNumber = inputData.match(/\d+/)[0];
inputData = inputData.slice(inputNumber.length);
return +inputNumber;
} else {
return -1;
}
}
};
var program = null;
var inputData = null;
function resetProgram() {
var stdout = document.getElementById('stdout');
stdout.value = null;
if (program !== null) {
program.halt();
}
program = null;
inputData = null;
}
function initProgram() {
var source = document.getElementById('source'),
stepsPerSecond = document.getElementById('steps-per-second'),
stdin = document.getElementById('stdin');
program = new Program(source.value, +stepsPerSecond.innerHTML, ioFunctions);
inputData = stdin.value;
}
function runBtnClick() {
if (program === null || program.snakesLiving == 0) {
resetProgram();
initProgram();
} else {
program.halt();
var stepsPerSecond = document.getElementById('steps-per-second');
program.speed = +stepsPerSecond.innerHTML;
}
program.run();
}
function stepBtnClick() {
if (program === null) {
initProgram();
} else {
program.halt();
}
program.step();
}
.container {
width: 100%;
}
.so-box {
font-family: 'Helvetica Neue', Arial, sans-serif;
font-weight: bold;
color: #fff;
text-align: center;
padding: .3em .7em;
font-size: 1em;
line-height: 1.1;
border: 1px solid #c47b07;
-webkit-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.3), 0 2px 0 rgba(255, 255, 255, 0.15) inset;
text-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
background: #f88912;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.3), 0 2px 0 rgba(255, 255, 255, 0.15) inset;
}
.control {
display: inline-block;
border-radius: 6px;
float: left;
margin-right: 25px;
cursor: pointer;
}
.option {
padding: 10px 20px;
margin-right: 25px;
float: left;
}
h1 {
text-align: center;
font-family: Georgia, 'Times New Roman', serif;
}
a {
text-decoration: none;
}
input,
textarea {
box-sizing: border-box;
}
textarea {
display: block;
white-space: pre;
overflow: auto;
height: 40px;
width: 100%;
max-width: 100%;
min-height: 25px;
}
span[contenteditable] {
padding: 2px 6px;
background: #cc7801;
color: #fff;
}
#stdout-container,
#stdin-container {
height: auto;
padding: 6px 0;
}
#reset {
float: right;
}
#source-display-wrapper {
display: none;
width: 100%;
height: 100%;
overflow: auto;
border: 1px solid black;
box-sizing: border-box;
}
#source-display {
font-family: monospace;
white-space: pre;
padding: 2px;
}
.activeToken {
background: #f88912;
}
.clearfix:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.clearfix {
display: inline-block;
}
* html .clearfix {
height: 1%;
}
.clearfix {
display: block;
}
<!--
Designed and written 2015 by D. Loscutoff
Much of the HTML and CSS was taken from this Befunge interpreter by Ingo Bürk: http://codegolf.stackexchange.com/a/40331/16766
-->
<div class="container">
<textarea id="source" placeholder="Enter your program here" wrap="off">i.1+!57*(\m1(M\1).96>.@32*-.80=\.78=3*\.66=3*\.82=5*\81=9*++++\2*1\-*+
)L!4*(4Sn1(</textarea>
<div id="source-display-wrapper">
<div id="source-display"></div>
</div>
</div>
<div id="stdin-container" class="container">
<textarea id="stdin" placeholder="Input" wrap="off">5k2/ppp5/4P3/3R3p/6P1/1K2Nr2/PP3P2/8</textarea>
</div>
<div id="controls-container" class="container clearfix">
<input type="button" id="run" class="control so-box" value="Run" onclick="runBtnClick()" />
<input type="button" id="pause" class="control so-box" value="Pause" onclick="program.halt()" />
<input type="button" id="step" class="control so-box" value="Step" onclick="stepBtnClick()" />
<input type="button" id="reset" class="control so-box" value="Reset" onclick="resetProgram()" />
</div>
<div id="stdout-container" class="container">
<textarea id="stdout" placeholder="Output" wrap="off" readonly></textarea>
</div>
<div id="options-container" class="container">
<div class="option so-box">Steps per Second: <span id="steps-per-second" contenteditable>1000</span>
</div>
</div>