Recursively generating the look-and-say sequence
It seems like you tried too hard to avoid all loops by limiting yourself to .reduce() and recursion.  While JavaScript supports functional programming to some extent, sticking to pure FP everywhere will likely lead to clunky code.  A better strategy would be to define some functions that look pure from the outside, but might be implemented in an imperative style.  (Your code isn't pure FP anyway: the index += 1 statements perform mutation.)

I don't think that countStay and drill are well designed functions.  It's not clear to me what each of them is supposed to accomplish.  countStay seems to be a trivial wrapper around drill.  At first glance, all I can see is that drill contains a lot of code, and that it calls itself recursively.  In particular, it's hard to read because the variable names are cryptic.  map is obviously a map, and arr is obviously an array, but what are their purpose?

Suggested solution

I would define three functions, each of which has a clear purpose and is easily testable.  Instead of recursion, I've used iterateN, which is inspired by Haskell.  Then, it's easy to compose them to obtain the result.  This solution is simpler and more readable than yours.



/**
 * Split an array of items into runs of identical values;
 * yield [item, runLength] for each run.
 *
 * runLengthEncode('bookkeeper'.split('')) =&gt;
 * ['b', 1], ['o', 2], ['k', 2], ['e', 2], ['p', 1], ['e', 1], ['r', 1]
 */
const runLengthEncode = function*(items) {
    if (items.length) {
        let groupStart = 0, groupItem = items[0];
        for (let i = 1; i &lt; items.length; i++) {
            if (items[i] != groupItem) {
                yield [groupItem, i - groupStart];
                groupStart = i;
                groupItem = items[i];
            }
        }
        yield [groupItem, items.length - groupStart];
    }
};

/**
 * Given a number (as an integer or string), produce its successor
 * in the look-and-say sequence (as a string).
 *
 * nextLookAndSay('1211') =&gt; '111221'
 */
const nextLookAndSay = (n) =&gt; {
    let accum = [];
    for (let group of runLengthEncode(n.toString().split(''))) {
        accum.push(group[1], group[0]);
    }
    return accum.join('');
};

/**
 * The result of f(f(f ... f(init))), where the function f is applied n times
 * to the initial value.
 *
 * iterateN(2, (s) =&gt; s + 'o', 'b') =&gt; 'boo'
 */
const iterateN = (n, f, init) =&gt; {
    while (n--) { init = f(init); }
    return init;
};

console.log(iterateN(3, nextLookAndSay, '1'));