codewars befunge interpreter (v2)

Revision 2 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>');
      
      run();
      return output;
    };
    
    
    
    
    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;
    }

Test runner

Ready to run.

Testing in
TestOps/sec
constablebrew
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

Revisions

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