Skip to content

Instantly share code, notes, and snippets.

@mchccn
Last active February 13, 2022 16:31
Show Gist options
  • Save mchccn/21020dfc152d405e06debf42e0e15523 to your computer and use it in GitHub Desktop.
Save mchccn/21020dfc152d405e06debf42e0e15523 to your computer and use it in GitHub Desktop.
CodeWars RoboScript Kata Solutions

These are my solutions to the RoboScript series of katas on CodeWars.

This Kata Series is based on a fictional story about a computer scientist and engineer who owns a firm that sells a toy robot called MyRobot which can interpret its own (esoteric) programming language called RoboScript. Naturally, this Kata Series deals with the software side of things (I'm afraid Codewars cannot test your ability to build a physical robot!).

Authored by donaldsebleung

const span = (color: string, text: string) => `<span style="color: ${color}">${text}</span>`;
function highlight(code: string) {
return code.replace(/(F+|L+|R+|[0-9]+)/, (match) => {
if (match[0] === "F") return span("pink", match);
if (match[0] === "L") return span("red", match);
if (match[0] === "R") return span("green", match);
return span("orange", match);
});
}
function execute(code: string) {
const visited = [[0, 0]] as [number, number][];
const cmds = code.split(/(?=[FLR])/);
const pos = [0, 0] as [number, number];
const dir = [1, 0] as [number, number];
cmds.forEach((cmd) => {
const c = cmd[0];
const n = cmd.slice(1) || undefined;
for (let i = 0; i < Number(typeof n !== "undefined" ? n : 1); i++) {
if (c === "F") {
pos[0] += dir[0];
pos[1] += dir[1];
visited.push([...pos]);
}
if (c === "L") {
dir.reverse();
dir[1] *= dir[0] ? 1 : -1;
}
if (c === "R") {
dir.reverse();
dir[0] *= dir[1] ? 1 : -1;
}
}
});
const x = Math.min(...visited.map(([x]) => x));
const y = Math.min(...visited.map(([, y]) => y));
visited.forEach((p) => {
if (x < 0) p[0] += Math.abs(x);
if (y < 0) p[1] += Math.abs(y);
});
const w = Math.max(...visited.map(([x]) => x));
const h = Math.max(...visited.map(([, y]) => y));
return visited.reduce((b, [x, y]) => (b[y][x] = "*", b), Array.from({ length: h + 1 }, () => Array.from({ length: w + 1 }, () => " "))).map((l) => l.join("")).join("\r\n");
}
type Nested<T = unknown> = (T | Nested<T>)[];
function extract(tokens: string[]) {
let i = 0;
return (function main(): Nested<string> {
const a = [] as Nested<string>;
while (i < tokens.length) {
const token = tokens[i++];
if (token === ")") return a;
if (token === "(") {
a.push(main());
continue;
}
if (["F", "L", "R"].includes(token) || /^[0-9]+$/.test(token)) {
a.push(token);
continue;
}
}
return a;
})();
}
function expand(code: string) {
const tokens = [];
while (code.length) {
if (["(", ")", "F", "L", "R"].includes(code[0])) {
tokens.push(code[0]);
code = code.slice(1);
}
if (/^[0-9]+/.test(code)) {
tokens.push(code.match(/^([0-9]+)/)![0]);
code = code.slice(tokens[tokens.length - 1].length);
}
}
const tree = extract(tokens);
const resolve = (s: string | Nested<string>, i: number, a: Nested<string>): string => {
if (typeof s === "string") {
if (["F", "L", "R"].includes(s)) {
if (a[i + 1] && typeof a[i + 1] === "string" && /^[0-9]+$/.test(a[i + 1] as string)) return s.repeat(+a[i + 1]);
return s;
}
return "";
} else {
if (a[i + 1] && typeof a[i + 1] === "string" && /^[0-9]+$/.test(a[i + 1] as string)) return s.map(resolve).join("").repeat(+a[i + 1]);
return s.map(resolve).join("");
}
};
return tree.map(resolve).join("");
}
function execute(code: string) {
code = expand(code);
console.log(code);
const visited = [[0, 0]] as [number, number][];
const cmds = code.split("");
const pos = [0, 0] as [number, number];
const dir = [1, 0] as [number, number];
cmds.forEach((c) => {
if (c === "F") {
pos[0] += dir[0];
pos[1] += dir[1];
visited.push([...pos]);
}
if (c === "L") {
dir.reverse();
dir[1] *= dir[0] ? 1 : -1;
}
if (c === "R") {
dir.reverse();
dir[0] *= dir[1] ? 1 : -1;
}
});
const x = Math.min(...visited.map(([x]) => x));
const y = Math.min(...visited.map(([, y]) => y));
visited.forEach((p) => {
if (x < 0) p[0] += Math.abs(x);
if (y < 0) p[1] += Math.abs(y);
});
const w = Math.max(...visited.map(([x]) => x));
const h = Math.max(...visited.map(([, y]) => y));
return visited.reduce((b, [x, y]) => (b[y][x] = "*", b), Array.from({ length: h + 1 }, () => Array.from({ length: w + 1 }, () => " "))).map((l) => l.join("")).join("\r\n");
}
type Nested<T = unknown> = (T | Nested<T>)[];
function extract(tokens: string[]) {
let i = 0;
return (function main(): Nested<string> {
const a = [] as Nested<string>;
while (i < tokens.length) {
const token = tokens[i++];
if (token.startsWith(")")) return token.length !== 1 ? a.concat(token.slice(1)) : a;
if (token === "(") a.push(main());
if (["F", "L", "R", "p", "q", "P"].some((c) => token.startsWith(c)) || /^[0-9]+$/.test(token)) a.push(token);
}
return a;
})();
}
function expand(code: string) {
const tokens = [];
const regex = [
/^(F(?:[1-9][0-9]*)?)/,
/^(L(?:[1-9][0-9]*)?)/,
/^(R(?:[1-9][0-9]*)?)/,
/(^\()/,
/(^\)(?:[1-9][0-9]*)?)/,
/^(p[0-9]+)/,
/^(q)/,
/^(P[0-9]+)/,
];
while (code.length) {
code = code.trim();
const match = regex.reduce((m, p) => m || code.match(p), undefined! as RegExpMatchArray);
tokens.push(match[0]);
code = code.slice(match[0].length);
}
const patterns = new Map<string, Nested<string>>();
let depth = 0;
const resolve = (s: string | Nested<string>, i: number, a: Nested<string> & { skip?: number }): string | Nested<string> => {
if (a.skip) return (a.skip--, undefined!);
if (typeof s === "string") {
if (s.startsWith("p")) {
if (depth) throw "";
if (a.indexOf("q", i) < 0) throw "";
const id = s.slice(1);
const p = a.slice(i, a.indexOf("q", i));
let del = p.length;
if (typeof p[0] === "string" && p[0].startsWith("p")) (p.shift(), del++);
if (typeof p[p.length - 1] === "string" && p[p.length - 1] === "q") (p.pop(), del--);
a.skip = del - 1;
if (patterns.has(id)) throw "";
patterns.set(id, p);
return undefined!;
}
if (s === "q") throw "";
return s;
} else {
depth++;
const _ = s.map(resolve).filter((n) => n);
depth--;
return _;
}
};
const tree = extract(tokens).map(resolve).filter((n) => n);
const stack = [] as string[];
const compile = (s: string | Nested<string>, i: number, a: Nested<string>): string => {
if (stack.length > 128) throw "";
if (typeof s === "string") {
if (s.startsWith("P")) {
if (!s.slice(1)) throw "";
const p = patterns.get(s.slice(1));
if (!p) throw "";
stack.push(s.slice(1));
const _ = p.map(compile).join("");
stack.pop();
return _;
}
if (["F", "L", "R"].some((c) => s.startsWith(c))) return s[0].repeat(Number(s.slice(1) === "" ? 1 : s.slice(1)));
return "";
} else {
if (/^[0-9]+$/.test(s[s.length - 1].toString())) return s.slice(0, -1).map(compile).join("").repeat(Number(s[s.length - 1]));
return s.map(compile).join("");
}
};
return tree.map(compile).join("");
}
function execute(code: string) {
code = expand(code);
const visited = [[0, 0]] as [number, number][];
const cmds = code.split("");
const pos = [0, 0] as [number, number];
const dir = [1, 0] as [number, number];
cmds.forEach((c) => {
if (c === "F") {
pos[0] += dir[0];
pos[1] += dir[1];
visited.push([...pos]);
}
if (c === "L") {
dir.reverse();
dir[1] *= dir[0] ? 1 : -1;
}
if (c === "R") {
dir.reverse();
dir[0] *= dir[1] ? 1 : -1;
}
});
const x = Math.min(...visited.map(([x]) => x));
const y = Math.min(...visited.map(([, y]) => y));
visited.forEach((p) => {
if (x < 0) p[0] += Math.abs(x);
if (y < 0) p[1] += Math.abs(y);
});
const w = Math.max(...visited.map(([x]) => x));
const h = Math.max(...visited.map(([, y]) => y));
return visited.reduce((b, [x, y]) => (b[y][x] = "*", b), Array.from({ length: h + 1 }, () => Array.from({ length: w + 1 }, () => " "))).map((l) => l.join("")).join("\r\n");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment