Skip to content

Instantly share code, notes, and snippets.

@Reconcyl
Last active July 19, 2018 19:35
Show Gist options
  • Save Reconcyl/e2cbe9cc55a7cd075f2a1f00b0ae2cba to your computer and use it in GitHub Desktop.
Save Reconcyl/e2cbe9cc55a7cd075f2a1f00b0ae2cba to your computer and use it in GitHub Desktop.
<html>
<head>
<link href="main.css" rel="stylesheet" type="text/css">
</head>
<body>
<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>
<script src="main.js"></script>
</body>
</html>
#code {
font-family: monospace;
}
#execution-display {
font-family: monospace;
white-space: pre;
}
.highlight {
background-color: yellow;
}
let TICKS_PER_SECOND = 5;
let INTERVAL_MS = 1000 / TICKS_PER_SECOND;
let runButton = document.getElementById("run-button");
let stopButton = document.getElementById("stop-button");
let code = document.getElementById("code");
let executionArea = document.getElementById("execution-display");
let intervalId;
function htmlEscape(string) {
let node = document.createElement("span");
node.innerText = string;
return node.innerHTML;
}
let snisp = {
x: null,
y: null,
direction: null,
callStack: null,
stopped: null,
playfield: null,
padRows: function() {
let maxLength = Math.max(...this.playfield.map(e => e.length));
for (let i = 0; i < this.playfield.length; i++) {
this.playfield[i] = this.playfield[i].padEnd(maxLength, ".");
}
},
initialize: function() {
this.x = 0;
this.y = 0;
this.direction = "right";
this.callStack = [];
this.stopped = false;
this.playfield = code.value.split("\n");
this.padRows();
this.update();
},
getCurrentChar: function() {
let row = this.playfield[this.y];
if (row == undefined) return;
return row[this.x];
},
backslashMirror: function() {
let table = {
"up": "left",
"right": "down",
"down": "right",
"left": "up",
};
this.direction = table[this.direction];
},
slashMirror: function() {
let table = {
"up": "right",
"right": "up",
"down": "left",
"left": "down",
};
this.direction = table[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 state = this.callStack.pop();
if (state == undefined) {
this.stopped = true;
return;
}
this.x = state.x;
this.y = state.y;
this.direction = state.direction;
},
tick: function() {
if (this.stopped) return;
let currentChar = this.getCurrentChar();
if (currentChar == undefined) {
this.stopped = true;
return;
}
switch (currentChar) {
case "\\":
this.backslashMirror();
break;
case "/":
this.slashMirror();
break;
case "!":
this.forward();
break;
case "@":
this.pushState();
break;
case "#":
this.restoreState();
this.forward();
break;
default:
break;
}
this.forward();
},
generatePlayfieldHTML: function(stateX, stateY) {
let playfieldLines = [];
for (let y = 0; y < this.playfield.length; y++) {
let chars = [];
let row = this.playfield[y];
for (let x = 0; x < row.length; x++) {
let c = htmlEscape(row[x]);
if (x == stateX && y == stateY) {
c = '<span class="highlight">' + c + '</span>';
}
chars.push(c);
}
playfieldLines.push(chars.join(""));
}
return playfieldLines.join("<br>");
},
update: function() {
let playfields = [];
for (let i = 0; i < this.callStack.length; i++) {
let state = this.callStack[i];
playfields.push(this.generatePlayfieldHTML(state.x, state.y));
}
playfields.push(this.generatePlayfieldHTML(this.x, this.y));
let playfieldHTML = playfields.join("<br><br>");
executionArea.innerHTML = playfieldHTML;
},
};
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);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment