Ouroboros - це езоланг, який я розробив цього тижня. Час взяти це за спину!
i.1+!57*(\m1(M\1).96>.@32*-.80=\.78=3*\.66=3*\.82=5*\81=9*++++\2*1\-*+
)L!4*(4Sn1(
Кожен рядок однозначних команд 1 являє собою вуроборову змію, в якій виконання триває від голови (початку) до хвоста (кінця) і петлі назад до голови. Команди (і )дозволяють з'їсти частину хвоста або відкотити його, тим самим змінюючи, які команди виконуються. Якщо вказівник інструкції колись проковтнеться, змія гине (припиняє виконання). Програма Ouroboros складається з однієї або декількох змій, які виконуються паралельно. У кожної змії є своя стопка, а також є спільний стек.
1 Один виняток, який відрізняє Уробороса від багатьох двовимірних мов: багатоцифрові числа можна писати прямо, без того, щоб робити математику чи натискати 0 спочатку.
Змія 1
Перша змія зчитує символ ( i) і перевіряє, чи -1 / EOF ( .1+!). Якщо так, він з'їдає більшу частину хвоста, аж до M( 57*().
Потім змія поміняє код символу на підрахунок, який знаходиться над ним на стеці ( \), перемістить таблицю на спільний стек ( m) і проковтує інший символ ( 1(). Якщо він уже проковтнув купу, це означає, що він проковтне той (факт, що IP зараз увімкнено, і гине. В іншому випадку виконання відбувається шляхом переміщення талію назад до стека змії 1, замінивши його char-кодом та повторно регулюючи характер, який раніше був проковтнутий ( M\1)).
Потім ми використовуємо математичні та стекові операції, щоб генерувати відповідний бал для персонажа. .96>тестує, чи це малі чи ні; наступні 32*-перетворюються на великі регістри. Потім довга тяганина .80=до 81=9*++++карт P-> 1, N-> 3і т. Д. Нарешті, \2*1\-*заперечує рахунок, якщо літера була малою літерою, і +додає її до запущеної таблиці. Потім змія замикається і читає іншого символу.
Змія 2
Друга змія починається з відрижкової операції ( )), яка нічого не робить з першого разу (оскільки нічого ще не проковтнулося, а також з моменту появи порожнього стека 0). Далі він висуває довжину спільного стека до власної стеки та логічно заперечує ( L!). Це дає, 1якщо стек порожній, 0інакше. Змія множиться на 4 і з'їдає багато символів ( 4*().
Якщо загальний стек був порожнім, це означає, що змія зараз закінчується перед S. Він штовхається 4і петлі повертається назад до місця ), де він відриває символів, які він щойно проковтнув, і починається заново.
Якщо в спільному стеку було значення, проте жодні символи не проковтуються і виконання продовжується. Змія перемикається на загальний стек і видає там номер ( Sn); потім він ковтає свого останнього персонажа і вмирає ( 1().
Синхронізація
Дві змії повинні бути ретельно синхронізовані, щоб ніколи не було значення на спільному стеку, коли змія 2 робить свою перевірку, до кінця введення. Snake 1 кладе значення на спільний стек на короткий час кожного проходу через його цикл. Таким чином, багатожильний 2 в Lкоманді ніколи не повинна виконуватися між mі Mкомандами в змії 1. До щастя, змій вибудовується дуже добре. Найважливіше, що довжина циклу змії 1 (70 інструкцій) є кратним циклом змії 2 (7 інструкцій), тому вони ніколи не вийдуть із синхронізації:
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
Якби цифри не спрацювали так ідеально, я б проклав одну або обидві змії пробілами, щоб вони вирівнялися за потребою.
Все це дуже добре, але я хочу бачити це в дії!
Ось наведена вище програма через Stack Snippet. Навіть на 1000 операцій в секунду потрібно 10 хвилин, щоб виплюнути відповідь на зразок введення - але він дійсно туди потрапляє!
// 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>