codewars befunge interpreter (v3)

Revision 3 of this benchmark created on


Setup

code = '91+:*:*:*:>1-# _@'
    
    
    var constablebrew = (function(sourceCode) {
    
      var instructions = {}, compiledIPs = {},
        a = 0,
        b = 0,
        data = [], // Registers and stack for the program
        ip = {
          x: 0,
          y: 0,
          dx: 1,
          dy: 0
        }, // Instruction pointer position and direction
        output = '',
        code;
    
      function copyIP(ip) {
        return {
          'x': ip.x,
          'y': ip.y,
          'dx': ip.dx,
          'dy': ip.dy
        };
      }
    
      function moveIP(ip) {
        ip.x += ip.dx;
        ip.y += ip.dy;
        if (ip.y >= code.length) {
          ip.y = 0;
        } else
        if (ip.y < 0) {
          ip.y = code.length - 1;
        }
        if (ip.x >= code[ip.y].length) {
          ip.x = 0;
        } else
        if (ip.x < 0) {
          ip.x = code[ip.y].length - 1;
        }
        return ip;
      }
    
      function getIPKey(ip) {
        return 'x' + ip.x + '_' + (ip.dx + 2) + '_y' + ip.y + '_' + (ip.dy + 2);
      }
    
      function compileCode(ipp) {
        var js = {
          '0': 'data.push(0);\n',
          '1': 'data.push(1);\n',
          '2': 'data.push(2);\n',
          '3': 'data.push(3);\n',
          '4': 'data.push(4);\n',
          '5': 'data.push(5);\n',
          '6': 'data.push(6);\n',
          '7': 'data.push(7);\n',
          '8': 'data.push(8);\n',
          '9': 'data.push(9);\n',
          '+': 'a=data.pop()||0;b=data.pop()||0;data.push(b+a);\n',
          '-': 'a=data.pop()||0;b=data.pop()||0;data.push(b-a);\n',
          '*': 'a=data.pop()||0;b=data.pop()||0;data.push(b*a);\n',
          '/': 'a=data.pop()||0;b=data.pop()||0;data.push((a)?~~(b/a):0);\n',
          '!': 'a=data.pop()||0;data.push(!a);\n',
          '`': 'a=data.pop()||0;b=data.pop()||0;data.push((b>a)?1:0);\n',
          ':': 'if(data.length){a=data.pop()||0;data.push(a);data.push(a);}else{data.push(0);}\n',
          '\\': 'a=data.pop()||0;b=data.pop()||0;data.push(a);data.push(b);\n',
          '$': 'data.pop();\n',
          '.': 'output += data.pop();\n',
          ',': 'output += String.fromCharCode(data.pop());\n',
          'g': 'a=data.pop()||0;b=data.pop()||0;data.push(code[a][b].charCodeAt(0));\n',
          ' ': ''
        },
          startingIP = getIPKey(ipp),
          ipx, chr, funcBody = '',
          stringifying = false;
        compiledIPs[getIPKey(ipp)] = compiledIPs[getIPKey(ipp)] || {};
    
        while (!compiledIPs[getIPKey(ipp)][startingIP]) {
    
          // Mark location as visited
          compiledIPs[getIPKey(ipp)] = compiledIPs[getIPKey(ipp)] || {};
          compiledIPs[getIPKey(ipp)][startingIP] = true;
    
          chr = code[ipp.y][ipp.x];
    
          if (stringifying && chr !== '"') {
            funcBody += 'data.push(' + chr.charCodeAt(0) + ');\n';
          } else {
            switch (chr) {
              case '"':
                stringifying = !stringifying;
                break;
              case '#':
                moveIP(ipp);
                break;
              case '>':
                ipp.dx = 1;
                ipp.dy = 0;
                break;
              case '<':
                ipp.dx = -1;
                ipp.dy = 0;
                break;
              case '^':
                ipp.dx = 0;
                ipp.dy = -1;
                break;
              case 'v':
                ipp.dx = 0;
                ipp.dy = 1;
                break;
              case '_':
                funcBody += '\n' +
                  'ip.x = ' + ipp.x + ';\n' +
                  'ip.y = ' + ipp.y + ';\n' +
                  'if(~~(data.pop()||0) ){\n' +
                  '  ip.dx = -1; ip.dy = 0;\n' +
                  '}else{\n' +
                  '  ip.dx = 1; ip.dy =  0;\n' +
                  '}\n' +
                  'return true;';
    
                instructions[startingIP] = eval(startingIP + ' = function(){' + funcBody + '};');
                ipx = copyIP(ipp);
                ipx.dx = -1;
                ipx.dy = 0;
                moveIP(ipx);
                compileCode(ipx);
    
                ipx = copyIP(ipp);
                ipx.dx = 1;
                ipx.dy = 0;
                moveIP(ipx);
                compileCode(ipx);
                return;
              case '|':
                funcBody += '\n' +
                  'ip.x = ' + ipp.x + ';\n' +
                  'ip.y = ' + ipp.y + ';\n' +
                  'if(~~(data.pop()||0) ){\n' +
                  '  ip.dx = 0; ip.dy = -1;\n' +
                  '}else{\n' +
                  '  ip.dx = 0; ip.dy =  1;\n' +
                  '}\n' +
                  'return true;';
                instructions[startingIP] = eval(startingIP + ' = function(){' + funcBody + '};');
                ipx = copyIP(ipp);
                ipx.dx = 0;
                ipx.dy = -1;
                moveIP(ipx);
                compileCode(ipx);
    
                ipx = copyIP(ipp);
                ipx.dx = 0;
                ipx.dy = 1;
                moveIP(ipx);
                compileCode(ipx);
                return;
              case '?':
                funcBody += '\n' +
                  'ip.x = ' + ipp.x + ';\n' +
                  'ip.y = ' + ipp.y + ';\n' +
                  'switch( ~~(Math.random() * 4) ){\n' +
                  '  case 0: ip.dx =  0; ip.dy = -1; break;\n' +
                  '  case 1: ip.dx =  0; ip.dy =  1; break;\n' +
                  '  case 2: ip.dx =  1; ip.dy =  0; break;\n' +
                  '  case 3: ip.dx = -1; ip.dy =  0;\n' +
                  '}\n' +
                  'return true;';
                instructions[startingIP] = eval(startingIP + ' = function(){' + funcBody + '};');
                ipx = copyIP(ipp);
                ipx.dx = 0;
                ipx.dy = -1;
                moveIP(ipx);
                compileCode(ipx);
    
                ipx = copyIP(ipp);
                ipx.dx = 0;
                ipx.dy = 1;
                moveIP(ipx);
                compileCode(ipx);
    
                ipx = copyIP(ipp);
                ipx.dx = -1;
                ipx.dy = 0;
                moveIP(ipx);
                compileCode(ipx);
    
                ipx = copyIP(ipp);
                ipx.dx = 1;
                ipx.dy = 0;
                moveIP(ipx);
                compileCode(ipx);
                return;
              case 'p':
                funcBody += '\n' +
                  'a=data.pop()||0;\n' +
                  'b=data.pop()||0;\n' +
                  'code[a][b]=String.fromCharCode(data.pop());\n' +
                  'compileCode(' + JSON.stringify(moveIP(copyIP(ipp))) + ');\n' +
                  'ip.x = ' + ipp.x + ';\n' +
                  'ip.y = ' + ipp.y + ';\n' +
                  'ip.dx = ' + ipp.dx + ';\n' +
                  'ip.dy = ' + ipp.dy + ';\n' +
                  'return true;';
                instructions[startingIP] = eval(startingIP + ' = function(){' + funcBody + '};');
                return;
              case '@':
                funcBody = 'if (new Date().getTime() - t > 2000) return false;\n' + funcBody;
                funcBody += '\n' +
                  'return false;';
                instructions[startingIP] = eval(startingIP + ' = function(){' + funcBody + '};');
                return;
              default:
                funcBody += js[chr];
            } // switch
          } // if stringifying
          moveIP(ipp);
          compiledIPs[getIPKey(ipp)] = compiledIPs[getIPKey(ipp)] || {};
        }
    
        if (funcBody !== '') {
          instructions[startingIP] = eval(startingIP + ' = function(){' + funcBody + '};');
        }
      }
    
      function run() {
        try {
          while (instructions[getIPKey(ip)]()) {
            moveIP(ip);
            //if(printCounter++ < 100) print('<pre>ip:'+getIPKey(ip)+'\n  a:'+a+'\n  b:'+b+'\n  data:'+JSON.stringify(data)+'\n  out:'+output+'</pre>');
          }
        } catch (e) {
          //print('ERROR! ' + e.message);
        }
      }
    
      code = sourceCode.split('\n').map(function(e) {
        return e.split('');
      });
      compileCode({
        x: 0,
        y: 0,
        dx: 1,
        dy: 0
      });
    
      //for(var o in instructions) print('function ' + o + '<pre>' + instructions[o] + '</pre>');
    
      return function() {
        a = 0;
        b = 0;
        data = [];
        ip = {x:0, y:0, dx:1, dy:0};
        output = '';
        run();
        return output;
      }
    })(code);
    
    
    
    
    function xDranik(code) {
    
      code = code.split('\n').map(function(e) {
        return e.split("")
      });
      var output = '';
      var stack = [];
      var rPointer = 0;
      var cPointer = 0;
      var a, b, c;
      var command;
      var right = true;
      var up = down = left = false;
      var trampoline = false;
    
      while (code[rPointer][cPointer] != '@') {
        command = code[rPointer][cPointer];
        if (command === ' ');
        else if (command >= '0' && command <= '9') {
          stack.push(parseInt(command));
        } else if (command === '+') {
          a = stack.pop();
          b = stack.pop();
          c = a + b;
          stack.push(c);
        } else if (command === '-') {
          a = stack.pop();
          b = stack.pop();
          c = b - a;
          stack.push(c);
        } else if (command === '*') {
          a = stack.pop();
          b = stack.pop();
          c = a * b;
          stack.push(c);
        } else if (command === '/') {
          a = stack.pop();
          b = stack.pop();
          if (a == 0) stack.push(0);
          else {
            c = Math.floor(b / a);
            stack.push(c);
          }
        } else if (command === '%') {
          a = stack.pop();
          b = stack.pop();
          if (a == 0) stack.push(0);
          else {
            c = b % a;
            stack.push(c);
          }
        } else if (command === '!') {
          a = stack.pop();
          if (a == 0) stack.push(1);
          else stack.push(0);
        } else if (command === '`') {
          a = stack.pop();
          b = stack.pop();
          if (b > a) stack.push(1);
          else stack.push(0);
        } else if (command === '>') {
          right = true;
          up = down = left = false;
        } else if (command === '<') {
          left = true;
          up = down = right = false;
        } else if (command === '^') {
          up = true;
          down = left = right = false;
        } else if (command === 'v') {
          down = true;
          up = left = right = false;
        } else if (command === '?') {
          var ran = Math.floor(Math.random() * 4) + 1;
          if (ran === 1) {
            up = true;
            down = left = right = false;
          } else if (ran === 2) {
            down = true;
            up = left = right = false;
          } else if (ran === 3) {
            left = true;
            up = down = right = false;
          } else {
            right = true;
            up = down = left = false;
          }
        } else if (command === "_") {
          a = stack.pop();
          if (a == 0) {
            right = true;
            up = down = left = false;
          } else {
            left = true;
            up = down = right = false;
          }
        } else if (command === '|') {
          a = stack.pop();
          if (a == 0) {
            down = true;
            up = left = right = false;
          } else {
            up = true;
            down = left = right = false;
          }
        } else if (command === '"') {
          //TODO
          if (up) rPointer--;
          if (down) rPointer++;
          if (left) cPointer--;
          if (right) cPointer++;
          command = code[rPointer][cPointer];
          while (command != '"') {
            stack.push(command.charCodeAt(0));
            if (up) rPointer--;
            if (down) rPointer++;
            if (left) cPointer--;
            if (right) cPointer++;
            command = code[rPointer][cPointer];
          }
        } else if (command === ':') {
          if (stack.length == 0) stack.push(0);
          else {
            a = stack.pop();
            stack.push(a);
            stack.push(a);
          }
        } else if (command === '\\') {
          a = stack.pop();
          b = stack.pop();
          stack.push(a);
          stack.push(b);
        } else if (command === '$') {
          stack.pop();
        } else if (command === '.') {
          a = stack.pop();
          output += a.toString();
        } else if (command === ',') {
          a = stack.pop();
          output += String.fromCharCode(a);
        } else if (command === '#') {
          trampoline = true;
        } else if (command === 'p') {
          a = stack.pop(); //y
          b = stack.pop(); //x
          c = stack.pop(); //v
          //console.log(a + ":" + b + ":" + c);
          //todo
          code[a][b] = String.fromCharCode(c);
        } else if (command === 'g') {
          a = stack.pop(); //y
          b = stack.pop(); //x
          stack.push(code[a][b].charCodeAt(0));
        }
        if (up) rPointer--;
        if (down) rPointer++;
        if (left) cPointer--;
        if (right) cPointer++;
        if (trampoline) {
          if (up) rPointer--;
          if (down) rPointer++;
          if (left) cPointer--;
          if (right) cPointer++;
          trampoline = false;
        }
      }
      return output;
    }
    
    
    
    
    
    
    
    
    function untrue(code) {
    
      // Break code string into array
      code = code.split('\n');
      for (var i = 0; i < code.length; i++) {
        code[i] = code[i].split('');
      }
      //console.log(code);
    
      var stack = [];
      var op = null;
    
      var ptr = new Pointer();
    
      var output = '';
      var put = [];
      var capture = false;
    
      var runCommand = function() {
        if (!code[ptr.y][ptr.x]) op = ' ';
        else op = code[ptr.y][ptr.x];
    
        if (op === '"') {
          if (capture == false) {
            capture = true;
            ptr.moveNext();
            return;
          } else {
            capture = false;
          }
        }
        if (capture) {
          stack.push(op.charCodeAt(0));
          ptr.moveNext();
          return;
        }
    
        if (op === ' ') {
          op = 'nop';
        }
        if (op === '<') {
          ptr.moveLeft();
        }
        if (op === '>') {
          ptr.moveRight();
        }
        if (op === '^') {
          ptr.moveUp();
        }
        if (op === 'v') {
          ptr.moveDown();
        }
        if (op === '?') {
          ptr.moveRandom();
        }
        if (op === '#') {
          ptr.moveNext();
        }
    
        if (op === '+') {
          var a = ~~stack.pop();
          var b = ~~stack.pop();
          stack.push(a + b);
        }
        if (op === '-') {
          var a = ~~stack.pop();
          var b = ~~stack.pop();
          stack.push(b - a);
        }
        if (op === '*') {
          var a = ~~stack.pop();
          var b = ~~stack.pop();
          stack.push(a * b);
        }
        if (op === '/') {
          var a = ~~stack.pop();
          var b = ~~stack.pop();
          stack.push(b / a);
        }
        if (op === '%') {
          var a = ~~stack.pop();
          var b = ~~stack.pop();
          stack.push(b % a ? b % a : 0);
        }
    
        if (op.match(/[0-9]/)) {
          stack.push(op);
        }
        if (op === '!') {
          var a = stack.pop();
          stack.push(a == 0 ? 1 : 0);
        }
        if (op === '`') {
          var a = stack.pop();
          var b = stack.pop();
          stack.push(b > a ? 1 : 0);
        }
        if (op === '_') {
          var a = stack.pop();
          a == 0 || !a ? ptr.moveRight() : ptr.moveLeft();
        }
        if (op === '|') {
          var a = stack.pop();
          a == 0 || !a ? ptr.moveDown() : ptr.moveUp();
        }
    
        if (op === ':') {
          var a = stack.pop();
          if ( !! a) {
            stack.push(a);
            stack.push(a);
          } else {
            stack.push(0);
          }
        }
        if (op === '\\') {
          if (stack.length == 1) {
            var a = stack.pop();
            stack.push(0);
            stack.push(a);
          } else {
            var a = stack.pop();
            var b = stack.pop();
            stack.push(a);
            stack.push(b);
          }
        }
    
        if (op === '$') {
          stack.pop();
        }
    
        if (op === '.') {
          output += ~~stack.pop();
        }
        if (op === ',') {
          var a = stack.pop();
          output += String.fromCharCode(a);
        }
        if (op === 'p') {
          var y = stack.pop();
          var x = stack.pop();
          var v = stack.pop();
          v = String.fromCharCode(v);
          code[y][x] = v;
        }
        if (op === 'g') {
          var y = stack.pop();
          var x = stack.pop();
          if (code[y][x]) {
            var v = code[y][x].charCodeAt(0);
            stack.push(v);
          }
        }
    
    
        ptr.moveNext();
    
        //console.log("Ptr: " + ptr.x + "," + ptr.y + "(" + ptr.xv + "," + ptr.yv + ") op: " + op + " Output: \"" + output + "\" Stack size: " + stack.length + " item on top: " + stack[stack.length-1]);
      }
    
    
      var loopCount = 0; // prevent infinite loop
    
      while (op !== '@' && loopCount < 1050) {
        //loopCount++;
        runCommand();
      }
    
      return output;
    }
    
    
    var Pointer = function() {
      this.x = 0;
      this.y = 0;
      this.xv = 0;
      this.yv = 0;
      this.stop = function() {
        this.xv = 0;
        this.yv = 0;
      }
      this.moveUp = function() {
        this.xv = 0;
        this.yv = -1;
      }
      this.moveDown = function() {
        this.xv = 0;
        this.yv = 1;
      }
      this.moveLeft = function() {
        this.xv = -1;
        this.yv = 0;
      }
      this.moveRight = function() {
        this.xv = 1;
        this.yv = 0;
      }
    
      this.moveRandom = function() {
        var dir = Math.floor(Math.random() * 4);
        if (dir == 0) {
          this.moveLeft();
        }
        if (dir == 1) {
          this.moveRight();
        }
        if (dir == 2) {
          this.moveUp();
        }
        if (dir == 3) {
          this.moveDown();
        }
      }
    
      this.moveNext = function() {
        if (this.yv + this.xv == 0) {
          this.moveRight();
        }
        this.x += this.xv;
        this.y += this.yv;
      }
    }
    
    
    
    
    
    
      function ProgramCounter() {
        this.x = 0;
        this.y = 0;
    
        this.directions = {
          'up': function() {
            this.y -= 1;
          },
          'down': function() {
            this.y += 1
          },
          'left': function() {
            this.x -= 1
          },
          'right': function() {
            this.x += 1
          },
        };
    
        this.goRight();
      }
    
    
    ProgramCounter.prototype.goRight = function() {
      this.direction = 'right'
    };
    ProgramCounter.prototype.goLeft = function() {
      this.direction = 'left'
    };
    ProgramCounter.prototype.goUp = function() {
      this.direction = 'up'
    };
    ProgramCounter.prototype.goDown = function() {
      this.direction = 'down'
    };
    ProgramCounter.prototype.goRandom = function() {
      this.direction = Object.keys(this.directions)[Math.floor(Math.random() * 4)]
    };
    
    
    
    ProgramCounter.prototype.tick = function() {
      this.directions[this.direction].call(this);
    };
    
    
    function Machine(s) {
      var code = s.split("\n").map(function(row) {
        return row.split("")
      });
    
      this.code = code;
      this.pc = new ProgramCounter();
      this.stack = [];
      this.output_buffer = []
    }
    
    
    Machine.prototype.output = function(value) {
      if (value === undefined) {
        return this.output_buffer;
      } else {
        this.output_buffer.push(value);
      }
    }
    
    
    Machine.prototype.nextInstruction = function() {
      return this.getProgramCell(this.pc.x, this.pc.y);
    };
    
    
    Machine.prototype.isEmpty = function() {
      return (this.stack.length === 0);
    };
    
    Machine.prototype.push = function(value) {
      this.stack.push(value);
    };
    
    Machine.prototype.pop = function() {
      return this.stack.pop();
    };
    
    Machine.prototype.setProgramCell = function(x, y, v) {
      this.code[y][x] = v;
    };
    
    Machine.prototype.getProgramCell = function(x, y) {
      return this.code[y][x];
    };
    
    
    Machine.prototype.run = function() {
      var digits = "0123456789";
    
      while (true) {
        var instruction = this.nextInstruction();
    
        if (digits.indexOf(instruction) >= 0) {
          this.push(digits.indexOf(instruction));
        } else if (instruction === '+') {
          this.push(this.pop() + this.pop());
        } else if (instruction === '-') {
          var a = this.pop();
          var b = this.pop();
    
          this.push(b - a);
        } else if (instruction === '*') {
          this.push(this.pop() * this.pop());
        } else if (instruction === '/') {
          var a = this.pop();
          var b = this.pop();
    
          this.push((a === 0) ? 0 : (b / a));
        } else if (instruction === '%') {
          var a = this.pop();
          var b = this.pop();
    
          this.push((a === 0) ? 0 : (b % a));
        } else if (instruction === '!') {
          this.push(this.pop() === 0 ? 1 : 0);
        } else if (instruction === '`') {
          var a = this.pop();
          var b = this.pop();
    
          this.push((b > a) ? 1 : 0);
        } else if (instruction === '>') {
          this.pc.goRight();
        } else if (instruction === '<') {
          this.pc.goLeft();
        } else if (instruction === '^') {
          this.pc.goUp();
        } else if (instruction === 'v') {
          this.pc.goDown();
        } else if (instruction === '?') {
          this.pc.goRandom();
        } else if (instruction === '_') {
          if (this.pop() === 0) {
            this.pc.goRight();
          } else {
            this.pc.goLeft();
          }
        } else if (instruction === '|') {
          if (this.pop() === 0) {
            this.pc.goDown();
          } else {
            this.pc.goUp();
          }
        } else if (instruction === '"') {
          while (true) {
            this.pc.tick();
            var next = this.nextInstruction();
            if (next === '"') {
              break;
            }
    
            this.push(next.charCodeAt(0));
          }
        } else if (instruction === ':') {
          if (this.isEmpty()) {
            this.push(0);
          } else {
            var value = this.pop();
            this.push(value);
            this.push(value);
          }
        } else if (instruction === '\\') {
          var a = this.pop();
          var b = this.pop();
    
          if (b === undefined) {
            b = 0;
          }
    
          this.push(a);
          this.push(b);
        } else if (instruction === '$') {
          this.pop();
        } else if (instruction === '.') {
          this.output(this.pop());
        } else if (instruction === ',') {
          this.output(String.fromCharCode(this.pop()));
        } else if (instruction === '#') {
          this.pc.tick();
        } else if (instruction === 'p') {
          var y = this.pop();
          var x = this.pop();
          var v = this.pop();
    
          this.setProgramCell(x, y, String.fromCharCode(v));
        } else if (instruction === 'g') {
          var y = this.pop();
          var x = this.pop();
    
          this.push(this.getProgramCell(x, y).charCodeAt(0));
        } else if (instruction === '@') {
          break;
        } else if (instruction === ' ') {} else {
          //console.log("Unknown instruction: " + instruction);
          break;
        }
    
        this.pc.tick();
      }
    };
    
    
    function marktriggs(code_string) {
      //console.log(code_string);
      var machine = new Machine(code_string);
    
      machine.run();
      return machine.output().join("");
    }
    
    
    
    
    
    
    
    
    
    function eugenebulkin(code) {
      var a, addDir, b, c, dir, dirs, getValue, l, output, ptr, stack, string_mode, v;
      output = "";
      code = (function() {
        var _i, _len, _ref, _results;
        _ref = code.split("\n");
        _results = [];
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          l = _ref[_i];
          _results.push((l + new Array(81 - l.length).join(' ')).split(''));
        }
        return _results;
      })();
      ptr = [0, 0];
      dir = [1, 0];
      stack = [];
      getValue = function(ptr) {
        return code[ptr[1]][ptr[0]];
      };
      addDir = function(ptr) {
        ptr[0] = dir[0] + ptr[0] % 79;
        ptr[1] = code.length === 1 ? ptr[1] : dir[1] + ptr[1] % code.length;
        if (ptr[0] < 0) {
          ptr[0] = ptr[0] + 80;
        }
        if (ptr[1] < 0) {
          ptr[1] = ptr[1] + code.length;
        }
        return ptr;
      };
      v = null;
      a = 0;
      b = 0;
      c = 0;
      string_mode = false;
      dirs = [
        [0, 1],
        [0, -1],
        [-1, 0],
        [1, 0]
      ];
      while (getValue(ptr) !== '@') {
        v = getValue(ptr);
        if (string_mode && v !== '"') {
          stack.push(v.charCodeAt(0));
          ptr = addDir(ptr);
          continue;
        }
        switch (false) {
          case v !== '"':
            string_mode = !string_mode;
            break;
          case !v.match(/\d/):
            stack.push(parseInt(v, 10));
            break;
          case !v.match(/[v\^<>\?]/):
            a = (function() {
              switch (v) {
                case "v":
                  return 0;
                case "^":
                  return 1;
                case "<":
                  return 2;
                case ">":
                  return 3;
                case "?":
                  return~~ (Math.random() * 4);
              }
            })();
            dir = dirs[a];
            break;
          case v !== ":":
            stack.push(stack.length > 0 ? stack[stack.length - 1] : 0);
            break;
          case v !== "p":
            a = stack.pop();
            b = stack.pop();
            c = stack.pop();
            code[a][b] = String.fromCharCode(c);
            break;
          case !v.match(/[\+\-\*\/\%\`\\g]/):
            a = stack.pop();
            b = stack.pop();
            switch (v) {
              case "+":
                stack.push(!b ? a : b + a);
                break;
              case "-":
                stack.push(!b ? -a : b - a);
                break;
              case "*":
                stack.push(!b ? 0 : b * a);
                break;
              case "%":
                stack.push(a === 0 || !a ? 0 : b % a);
                break;
              case "/":
                stack.push(a === 0 || !a ? 0 : ~~(b / a));
                break;
              case "`":
                stack.push(~~(b > a));
                break;
              case "\\":
                stack.push(a);
                stack.push(b || 0);
                break;
              case "g":
                stack.push(getValue([b, a]).charCodeAt(0));
                break;
            }
            break;
          case !v.match(/[\_\!\$\.\,\|]/):
            a = stack.pop();
            switch (v) {
              case "_":
                dir = (a === 0 || stack.length === 0 ? [1, 0] : [-1, 0]);
                break;
              case "|":
                dir = (a === 0 || stack.length === 0 ? [0, 1] : [0, -1]);
                break;
              case "!":
                stack.push(a === 0 ? 1 : 0);
                break;
              case ".":
                output += a;
                break;
              case ",":
                output += String.fromCharCode(a);
                break;
            }
            break;
          case v !== "#":
            ptr = addDir(ptr);
            break;
        }
        ptr = addDir(ptr);
      }
      return output;
    };
    
    
    
    
    
    
    
    
    function OverZealous(code) {
      var output = "",
    
        stack = [],
        pop = function() {
          return stack.length ? stack.pop() : 0;
        },
        push = function() {
          stack.push.apply(stack, arguments);
        },
    
        dir = {
          x: 1,
          y: 0
        },
        pos = {
          x: 0,
          y: 0
        },
        move = function(d) {
          d = d || dir;
          pos.x = (pos.x + dir.x) % w;
          pos.y = (pos.y + dir.y) % h;
        },
        getIns = function() {
          return code[pos.y][pos.x];
        },
    
        // direction changes
        cDir = {
          '>': {
            x: 1,
            y: 0
          },
          '<': {
            x: -1,
            y: 0
          },
          '^': {
            x: 0,
            y: -1
          },
          'v': {
            x: 0,
            y: 1
          }
        },
    
        // stack operations
        op = {
          '+': function() {
            return pop() + pop();
          },
          '-': function() {
            return -(pop()) + pop();
          },
          '*': function() {
            return pop() * pop();
          },
          '/': function() {
            var a = pop(),
              b = pop();
            return a === 0 ? 0 : Math.floor(b / a);
          },
          '%': function() {
            var a = pop(),
              b = pop();
            return a === 0 ? 0 : (b % a);
          },
          '!': function() {
            return pop() === 0 ? 1 : 0;
          },
          '`': function() {
            return (pop() <= pop()) ? 1 : 0;
          }
        },
    
        cont = true,
        ins, w, h, a, b, x, y, v;
    
      code = code.split(/\n/);
      h = code.length;
      w = code.reduce(function(max, row) {
        return Math.max(max, row.length);
      }, 0);
      code = code.map(function(line) {
        line = line.split('');
        while (line.length < w) line.push(' ');
        return line;
      });
    
      while (cont) {
        ins = getIns();
    
        if (/\d/.test(ins)) {
          // decimal 0-9
          stack.push(ins | 0);
    
        } else if (cDir[ins]) {
          // change direction
          dir = cDir[ins];
    
        } else if (op[ins]) {
          // perform stack operation
          push(op[ins]());
    
        } else switch (ins) {
          // miscellaneous instructions
          case '?':
            if (Math.random() < 0.5) {
              dir = {
                x: Math.floor(Math.random() * 3) - 1,
                y: 0
              };
            } else {
              dir = {
                x: 0,
                y: Math.floor(Math.random() * 3) - 1
              };
            }
            break;
          case '_':
            dir = {
              x: (pop() === 0 ? 1 : -1),
              y: 0
            };
            break;
          case '|':
            dir = {
              x: 0,
              y: (pop() === 0 ? 1 : -1)
            };
            break;
          case '"':
            while (true) {
              move();
              a = getIns();
              if (a === '"') {
                break;
              }
              push(a.charCodeAt(0));
            }
            break;
          case ':':
            v = pop();
            push(v, v);
            break;
          case '\\':
            push(pop(), pop());
            break;
          case '$':
            pop();
            break;
          case '.':
            output += pop();
            break;
          case ',':
            output += String.fromCharCode(pop());
            break;
          case '#':
            move();
            break;
          case 'p':
            x = pop();
            y = pop();
            v = pop();
            code[x][y] = String.fromCharCode(v);
            break;
          case 'g':
            x = pop();
            y = pop();
            push(code[x][y].charCodeAt(0));
            break;
          case '@':
            cont = false;
            break;
        }
        if (cont) {
          move();
        }
      }
    
      return output;
    }
    
    
    
    
    
    
    
    
    
    
    function cadetstar(sCode) {
      var tCode = sCode.split(/\n/);
      var code = [],
        iCode;
      var i;
      for (i = 0; i < tCode.length; i++) {
        iCode = tCode[i].split('');
        for (j = 0; j < iCode.length; j++) {
          if (code[j] === undefined) {
            code[j] = [];
          }
          code[j][i] = iCode[j];
        }
      }
      var output = "";
      var continuing = true;
      var curX = 0;
      var curY = 0;
      var dirX = 1;
      var dirY = 0;
      var stack = [];
      var operA, operB, operC;
      var directions = {
        '>': [1, 0],
        '<': [-1, 0],
        '^': [0, -1],
        'v': [0, 1]
      };
      var isStringMode = false;
      var skipper;
    
      while (continuing) {
        skipper = 1;
        if (isStringMode) {
          if (code[curX][curY] === '"') {
            isStringMode = false;
          } else {
            stack.push(code[curX][curY].charCodeAt(0));
          }
        } else {
          switch (code[curX][curY]) {
            case '+':
              stack.push(stack.pop() + stack.pop());
              break;
            case '-':
              stack.push(-1 * stack.pop() + stack.pop());
              break;
            case '*':
              stack.push(stack.pop() * stack.pop());
              break;
            case '/':
              operA = stack.pop();
              operB = stack.pop();
              if (operA === 0) {
                stack.push(0);
              } else {
                stack.push(Math.floor(operB / operA))
              }
              break;
            case '%':
              operA = stack.pop();
              operB = stack.pop();
              if (operA === 0) {
                stack.push(0);
              } else {
                stack.push(operB % operA);
              }
              break;
            case '!':
              stack.push(!stack.pop() ? 1 : 0)
              break;
            case '`':
              stack.push(stack.pop() < stack.pop() ? 1 : 0);
              break;
            case '<':
            case '>':
            case '^':
            case 'v':
              var tester = directions[code[curX][curY]];
              //          console.log(tester);
              dirX = directions[code[curX][curY]][0];
              dirY = directions[code[curX][curY]][1];
              break;
            case '?':
              var dir = ['<', '>', '^', 'v'][Math.floor(Math.random() * 4)];
              dirX = directions[dir][0];
              dirY = directions[dir][1];
              break;
            case '_':
              dirX = ((stack.pop() === 0) ? 1 : -1);
              dirY = 0;
              break;
            case '|':
              dirY = ((stack.pop() === 0) ? 1 : -1);
              dirX = 0;
              break;
            case '"':
              isStringMode = true;
              break;
            case ':':
              if ( !! stack[stack.length - 1]) {
                stack.push(stack[stack.length - 1])
              } else {
                stack.push(0);
              }
              break;
            case '\\':
              operA = stack.pop();
              operB = stack.pop() || 0;
              stack.push(operA);
              stack.push(operB);
              break;
            case '$':
              stack.pop();
              break;
            case '.':
              output += stack.pop();
              break;
            case ',':
              output += String.fromCharCode(stack.pop());
              break;
            case '#':
              skipper = 2;
              break;
            case 'p':
              operA = stack.pop();
              operB = stack.pop();
              operC = stack.pop();
              code[operB][operA] = String.fromCharCode(operC);
              break;
            case 'g':
              operA = stack.pop();
              operB = stack.pop();
              stack.push(code[operB][operA].charCodeAt(0));
              break;
            case '@':
              continuing = false;
              break;
            case ' ':
              break;
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '0':
              stack.push(parseInt(code[curX][curY], 10));
              break;
          }
        }
        curX += dirX * skipper;
        curY += dirY * skipper;
      }
      return output;
    }
    
    
    
    
    
    
    
    
    var mholtfoo = function(input) {
      var getDir = function(d) {
        switch (d) {
          case 0:
          case 'north':
            return [0, -1];
          case 1:
          case 'east':
            return [1, 0];
          case 2:
          case 'south':
            return [0, 1];
          case 3:
          case 'west':
            return [-1, 0];
        }
      }
    
      var getPos = function(pos, dir) {
        return [pos[0] + dir[0], pos[1] + dir[1]];
      }
      var output = "";
      var pos = [0, 0];
      var dir = [1, 0];
      var stack = [];
      var doBreak = false;
      var ascii = false;
    
      var code = input.split("\n").map(function(e) {
        return e.split("")
      });
      do {
        if (pos[1] >= code.length || pos[0] >= code[pos[1]].length) throw "Index out of bounds (" + pos[0] + ", " + pos[1] + ")";
        cCode = code[pos[1]][pos[0]];
        if (ascii && cCode == '"') ascii = false;
        else if (ascii) stack.unshift(cCode.charCodeAt(0));
        else {
          switch (cCode = code[pos[1]][pos[0]]) {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
              stack.unshift(parseInt(cCode, 10));
              break;
            case '+':
              stack.unshift(stack.shift() + stack.shift());
              break;
            case '-':
              var x = stack.shift();
              stack.unshift(stack.shift() - x);
              break;
            case '*':
              stack.unshift(stack.shift() * stack.shift());
              break;
            case '/':
              stack.unshift((a = stack.shift()) === 0 ? 0 : Math.floor(stack.shift() / a));
              break;
            case '%':
              stack.unshift((a = stack.shift()) === 0 ? 0 : stack.shift() % a);
              break;
            case '!':
              stack.unshift(stack.shift() === 0 ? 1 : 0);
              break;
            case '`':
              stack.unshift(stack.shift() < stack.shift() ? 1 : 0);
              break;
            case '>':
              dir = getDir('east');
              break;
            case '<':
              dir = getDir('west');
              break;
            case '^':
              dir = getDir('north');
              break;
            case 'v':
              dir = getDir('south');
              break;
            case '?':
              dir = getDir(Math.floor(Math.random() * 4));
              break;
            case '_':
              dir = getDir(stack.shift() == 0 ? 'east' : 'west');
              break;
            case '|':
              dir = getDir(stack.shift() == 0 ? 'south' : 'north');
              break;
            case '"':
              ascii = true;
              break;
            case ':':
              stack.unshift(stack[0] || 0);
              break;
            case '\\':
              var a = stack[0];
              stack[0] = stack[1] || 0;
              stack[1] = a;
              break;
            case '$':
              stack.shift();
              break;
            case '.':
              output += stack.shift();
              break;
            case ',':
              output += String.fromCharCode(stack.shift());
              break;
            case '#':
              pos = getPos(pos, dir);
              break;
            case 'p':
              code[stack.shift()][stack.shift()] = String.fromCharCode(stack.shift());
              break;
            case 'g':
              stack.unshift(code[stack.shift()][stack.shift()].charCodeAt(0));
              break;
            case '@':
              doBreak = true;
              break;
            case ' ':
              break;
          }
        }
        pos = getPos(pos, dir);
      } while (!doBreak);
    
      return output;
    }
    
    var pygy = (function() {
      var binops, directions, interpret;
    
      binops = {
        "+": function(a, b) {
          return a + b;
        },
        "-": function(a, b) {
          return b - a;
        },
        "*": function(a, b) {
          return a * b;
        },
        "/": function(a, b) {
          return 0 | b / a;
        },
        "%": function(a, b) {
          return 0 | b % a;
        },
        "`": function(a, b) {
          if (a < b) {
            return 1;
          } else {
            return 0;
          }
        }
      };
    
      directions = {
        ">": {
          dx: 1,
          dy: 0
        },
        "<": {
          dx: -1,
          dy: 0
        },
        "^": {
          dx: 0,
          dy: -1
        },
        "v": {
          dx: 0,
          dy: 1
        }
      };
    
      interpret = function(code) {
        var done, dx, dy, len, op, output, stack, x, y, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7;
        code = (code.split("\n")).map(function(s) {
          return s.split("");
        });
        output = "";
        stack = [];
        _ref = [-1, 0], x = _ref[0], y = _ref[1];
        _ref1 = directions[">"], dx = _ref1.dx, dy = _ref1.dy;
        done = false;
        while (!done) {
          op = code[y += dy][x += dx];
          switch (op) {
            case "0":
            case "1":
            case "2":
            case "3":
            case "4":
            case "5":
            case "6":
            case "7":
            case "8":
            case "9":
              stack.push(0 | op);
              break;
            case "+":
            case "-":
            case "*":
            case "/":
            case "%":
            case "`":
              stack.push(binops[op](stack.pop(), stack.pop()));
              break;
            case "!":
              stack.push(stack.pop() === 0 ? 1 : 0);
              break;
            case ">":
            case "<":
            case "^":
            case "v":
              _ref2 = directions[op], dx = _ref2.dx, dy = _ref2.dy;
              break;
            case "?":
              _ref3 = directions[[">", "<", "^", "v"][0 | Math.random() * 4]], dx = _ref3.dx, dy = _ref3.dy;
              break;
            case "_":
              _ref4 = directions[stack.pop() === 0 ? ">" : "<"], dx = _ref4.dx, dy = _ref4.dy;
              break;
            case "|":
              _ref5 = directions[stack.pop() === 0 ? "v" : "^"], dx = _ref5.dx, dy = _ref5.dy;
              break;
            case '"':
              while ('"' !== (op = code[y += dy][x += dx])) {
                stack.push(op.charCodeAt(0));
              }
              break;
            case ":":
              stack.push((len = stack.length) ? stack[len - 1] : 0);
              break;
            case "\\":
              if ((len = stack.length) === 1) {
                stack.push(0);
              } else {
                _ref6 = [stack[len - 2], stack[len - 1]], stack[len - 1] = _ref6[0], stack[len - 2] = _ref6[1];
              }
              break;
            case "$":
              stack.pop();
              break;
            case ".":
              output += stack.pop();
              break;
            case ",":
              output += String.fromCharCode(stack.pop());
              break;
            case "#":
              _ref7 = [x + dx, y + dy], x = _ref7[0], y = _ref7[1];
              break;
            case "p":
              code[stack.pop()][stack.pop()] = String.fromCharCode(stack.pop());
              break;
            case "g":
              stack.push(code[stack.pop()][stack.pop()].charCodeAt(0));
              break;
            case " ":
              "";
              break;
            case "@":
              done = true;
              break;
            default:
              throw "Invalid operation: " + op;
          }
        }
        return output;
      };
      return interpret;
    })();

Test runner

Ready to run.

Testing in
TestOps/sec
constablebrew precompiled
constablebrew(code)
ready
xDranik
xDranik(code)
ready
untrue
untrue(code)
ready
marktriggs
marktriggs(code)
ready
eugenebulkin
eugenebulkin(code)
ready
OverZealous
OverZealous(code)
ready
cadetstar
cadetstar(code)
ready
mholtfoo
mholtfoo(code)
ready
pygy
pygy(code)
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.