Challenges source_x vs source_y under time/memory constraints

    memlimit = memory limit in bytes (for Lua interpreter and everything under)
    cgroup = existing cgroup where to put this contest

def run_interactive(
        source_x, source_o, timeout=None, memlimit=None, cgroup=None,
        cgroup_path=None):
    """Challenges source_x vs source_y under time/memory constraints

    memlimit = memory limit in bytes (for Lua interpreter and everything under)
    cgroup = existing cgroup where to put this contest
    """

    msg = msgpack.packb([source_x, source_o])
    run_lua = os.path.join(os.path.dirname(__file__), 'run.lua')
    server = [os.getenv("LUA", "lua"), run_lua, '--server']
    args = dict(bufsize=0xffff, stdin=subprocess.PIPE, stdout=subprocess.PIPE)

    if timeout is not None:
        server = ['timeout', '%.3f' % timeout] + server

    with limit_ram(memlimit, cgroup, cgroup_path) as cg:
        if cg is not None:
            server = ['cgexec', '-g', cg] + server

        with subprocess.Popen(server, **args) as f:
            send_payload(f.stdin, msg)
            xo, stop = 'o', False
            while not stop:
                msg = get_payload(f.stdout)
                if msg == b'':
                    xo = flip(xo)  # because bad thing happened during next exec
                    f.wait()  # it has to shutdown properly first
                    if f.returncode == 124:
                        yield xo, ('error', "timeout"), ""
                    else:
                        errmsg = "probably OOM (%d)" % f.returncode
                        yield xo, ('error', errmsg), ""
                    stop = True
                else:
                    [xo, moveresult, log] = msgpack.unpackb(msg)
                    xo, log = xo.decode('utf8'), log.decode('utf8')
                    if moveresult[0] == b'error':
                        yield xo, ('error', moveresult[1].decode('utf8')), ""
                        stop = True
                    elif moveresult[0] == b'state_coords':
                        state = moveresult[1][0].decode('utf8')
                        coords = moveresult[1][1]
                        if state == 'draw' or state == 'x' or state == 'o':
                            stop = True
                        yield xo, ['state_coords', [state, coords]], ""