JSFiddle - React, Tailwind, and code Playground

HTML

<div id="controls">
    <div id="blocks">
        <div class="block b-forward" data-action="forward"></div>
        <div class="block b-left" data-action="left"></div>
        <div class="block b-right" data-action="right"></div>
        <div class="block b-function" data-action="function"></div>
    </div>
    <div id="board">
        <div id="queue" class="func" data-name="main" data-entry="true"></div>
        <div id="func" class="func" data-name="f"></div>
        <button type="button" onclick="run()" id="run"></button>
        <p>inspired by <a href="http://primo.io/" target="_blank">primo.io</a></p>
    </div>
</div>

<div id="robot" style="display: none"></div>

CSS

* {
    box-sizing: border-box;
}

body {
    font-family: Helvetica, sans-serif;
}

#board {
    background-color: hsl(37, 50%, 90%);
    border-radius: 5px;
    padding: 20px 50px;
}

#queue {
    border: 5px solid transparent;
}

#func {
    border: 5px solid hsl(160, 51%, 37%);
    border-radius: 5px;
}

#robot {
    width: 100px;
    height: 100px;
    border: 5px solid hsl(0, 0%, 80%);
    border-radius: 5px;
    position: absolute;
    left: 500px;
    top: 200px;
    border-top-right-radius: 30px;
    border-bottom-right-radius: 30px;
    transition: all 0.95s;
}

#controls {
    text-align: center;
    width: 300px;
}

.slot {
    border: 10px solid hsl(21, 80%, 70%);
    border-radius: 13px;
    margin: 13px;
    display: inline-block;
}

.block {
    border: 1px solid transparent;
    border-radius: 30px;
    margin: 8px;
    display: inline-block;
    height: 30px;
    width: 30px;
    cursor: pointer;
}

.b-forward { background-color: hsl(354, 80%, 60%); }
.b-function { background-color: hsl(160, 51%, 37%); }
.b-left { background-color: hsl(200, 90%, 60%); }
.b-right { background-color: hsl(49, 100%, 70%); }

#run {
    background-color: hsl(354, 80%, 60%);
    border-radius: 10px;
    border: 0px none;
    margin-top: 20px;
    width: 100%;
    height: 30px;
    outline: none;
    cursor: pointer;
}

#run:active {
    background-color: hsl(354, 80%, 50%);
}

JavaScript

Math.TAU = 2 * Math.PI;

var ROBOT = {
    x: 500, y: 200,
    angle: 0, speed: 100,
    running: false
};

$(function () {
    var q = $('#queue');
    var f = $('#func');
    var blocks = $('#blocks .block');
    blocks.draggable({
        revert: true,
        revertDuration: 0,
        helper: 'clone',
        stop: function (ev) {
            dropBlock($(this), ev.clientX, ev.clientY);
        }
    });
    
    for (var i = 0; i < 12; ++i) {
        addSlot(q);
    }
    
    for (var i = 0; i < 4; ++i) {
        addSlot(f);
    }
    
    $('#board').on('click', '.slot', function () {
        clearSlot($(this));
    });
    
    drawRobot(ROBOT);
    $('#robot').show();
});

function dropBlock(block, x, y) {
    var e = document.elementFromPoint(x, y);
    
    if (!e) {
        return;
    }
    
    e = $(e);
    
    if (e.is('.slot')) {
        clearSlot(e);
        setSlot(e, block.data('action'));
    }
}

function clearSlot(slot) {
    slot.removeClass('b-forward b-left b-right b-function block');
    slot.data('action', '');
}

function setSlot(slot, action) {
    slot.addClass('block');
    slot.addClass('b-' + action);
    slot.data('action', action);
}

function addSlot(element) {
    var slot = $('<div/>');
    slot.addClass('slot');
    element.append(slot);
}

function run() {
    if (ROBOT.running) {
        ROBOT.running = false;
        return;
    }
    
    ROBOT.running = true;
    
    var code = {};
    
    $.each($('#board .func'), function (i, elm) {
        elm = $(elm);
        var name = elm.data('name');
        code[name] = getActions(elm);
    });
    
    var state = {
        data: { robot: ROBOT },
        code: code,
        frames: [{ func: 'main', ip: 0 }]
    };
    
    executeStep(state);
}

function executeStep(state) {
    if (!state.data.robot.running) {
        return;
    }
    
    var frame = state.frames[state.frames.length - 1];
    
    if (!frame) {
        state.data.robot.running = false;
        return...