Add solution day 21
This commit is contained in:
parent
80547b5125
commit
881a530b8c
5
day_21/input.txt
Normal file
5
day_21/input.txt
Normal file
@ -0,0 +1,5 @@
|
||||
540A
|
||||
582A
|
||||
169A
|
||||
593A
|
||||
579A
|
78
day_21/solve2.md
Normal file
78
day_21/solve2.md
Normal file
@ -0,0 +1,78 @@
|
||||
|
||||
기본적인 정의를 합니다.
|
||||
|
||||
```my-ml-like-sudo-language
|
||||
// fg is shortest available pathes function that returns a path from pos to target.
|
||||
// type cmd :: enum {
|
||||
// up'^', down'v', right'<', left'>', accept'A'
|
||||
// }
|
||||
// type path_set = set of list of cmd;
|
||||
```
|
||||
|
||||
이제 헬퍼 함수를 정이합니다.
|
||||
|
||||
```
|
||||
// \product is a function that returns a list of all possible combinations of two lists.
|
||||
// \product :: path_set -> path_set -> path_set
|
||||
// \product [] ys = []
|
||||
// \product xs [] = []
|
||||
// \product (x:xs) y = (concat x y) ++ (\product xs y)
|
||||
// infix 5 \product // associativity is not important. because it commutative.
|
||||
|
||||
// ++ is union of two sets.
|
||||
// concat :: cmd -> path_set -> path_set
|
||||
// concat x [] = [x]
|
||||
// concat x (y:ys) = (x:y) ++ (concat x ys)
|
||||
|
||||
// product :: path_set -> path_set -> path_set
|
||||
// product a b = a \product b
|
||||
|
||||
```
|
||||
|
||||
```my-ml-like
|
||||
|
||||
type start_pos = cmd
|
||||
type target_pos = cmd
|
||||
type next_pos = cmd
|
||||
available_paths :: start_pos -> target_pos -> path_set
|
||||
type Handler 'T :: start_pos -> ('T, next_pos)
|
||||
|
||||
// define unit, mappend. these are monoid.
|
||||
|
||||
//
|
||||
|
||||
// unit :: Handler of path_set
|
||||
// unit = s -> t -> (t, s)
|
||||
// mappend :: Handler of path_set -> Handler of path_set -> Handler of path_set
|
||||
// mappend f g = s ->
|
||||
// let (t, s') = f s in
|
||||
// let (a ,s'') = g s'
|
||||
// in ( (product t a ) , s'')
|
||||
//
|
||||
// move :: Handler of (cmd -> set of list of cmd)
|
||||
|
||||
// move s cmds x = available_paths x \product
|
||||
// accept :: Handler of (list of cmd -> list of cmd)
|
||||
|
||||
// f = fg 'A'
|
||||
// f :: (list of cmd) -> set of (list of cmd)
|
||||
// statement execute 'A' (f (x)) = x
|
||||
// f x = move x; accept;
|
||||
// f x:tails = (move x; accept; f tails) 'A'
|
||||
// sl is get shortest path length
|
||||
// sl :: list of cmd -> Int
|
||||
// sl cmd = (f cmd) |> (foldr (x,y) -> min(x,y), INF)
|
||||
|
||||
// operator \product is a function that returns a list of all possible combinations of two lists.
|
||||
// \product :: list of cmd -> list of cmd -> list of cmd
|
||||
// \product [] ys = ys
|
||||
// \product xs [] = xs
|
||||
// \product (x:xs) (y:ys) = x \product (y:ys) ++ xs \product (y:ys)
|
||||
// infixr 5 \product
|
||||
|
||||
// sl (a \product b) = sl a + sl b
|
||||
// f /(.*)A/:rest = f '$1A' \product f rest
|
||||
// sl(f /(.*)A/:rest)) = sl (f '$1A' \product f rest)
|
||||
// = sl (f '$1A') + sl (f rest)
|
||||
//
|
||||
```
|
270
day_21/solve_1.test.ts
Normal file
270
day_21/solve_1.test.ts
Normal file
@ -0,0 +1,270 @@
|
||||
import { assertEquals, assertThrows } from "jsr:@std/assert";
|
||||
import {
|
||||
Command,
|
||||
DIRPAD_FIRST_POS,
|
||||
executeCommandsOnDirpadRobot,
|
||||
executeCommandsOnNumpadRobot,
|
||||
getDirpadNumber,
|
||||
getNextDirpadRobotState,
|
||||
getNextNumpadRobotState,
|
||||
getNumpadNumber,
|
||||
getPosFromDirpadNumber,
|
||||
getPosFromNumpadNumber,
|
||||
NUMPAD_FIRST_POS,
|
||||
Pos,
|
||||
shortestPathesOnDirpadRobot,
|
||||
shortestPathesOnNumpadRobot,
|
||||
solve_1,
|
||||
} from "./solve_1.ts";
|
||||
|
||||
Deno.test("getNumbericKeypadNumber", () => {
|
||||
assertEquals(getNumpadNumber([0, 0]), "7");
|
||||
assertEquals(getNumpadNumber([1, 0]), "8");
|
||||
assertEquals(getNumpadNumber([2, 0]), "9");
|
||||
assertEquals(getNumpadNumber([0, 1]), "4");
|
||||
assertEquals(getNumpadNumber([1, 1]), "5");
|
||||
assertEquals(getNumpadNumber([2, 1]), "6");
|
||||
assertEquals(getNumpadNumber([0, 2]), "1");
|
||||
assertEquals(getNumpadNumber([1, 2]), "2");
|
||||
assertEquals(getNumpadNumber([2, 2]), "3");
|
||||
assertEquals(getNumpadNumber([0, 3]), " ");
|
||||
assertEquals(getNumpadNumber([1, 3]), "0");
|
||||
assertEquals(getNumpadNumber([2, 3]), "A");
|
||||
});
|
||||
|
||||
Deno.test("getPosFromNumpadNumber", () => {
|
||||
assertEquals(getPosFromNumpadNumber("7"), [0, 0]);
|
||||
assertEquals(getPosFromNumpadNumber("8"), [1, 0]);
|
||||
assertEquals(getPosFromNumpadNumber("9"), [2, 0]);
|
||||
assertEquals(getPosFromNumpadNumber("4"), [0, 1]);
|
||||
assertEquals(getPosFromNumpadNumber("5"), [1, 1]);
|
||||
assertEquals(getPosFromNumpadNumber("6"), [2, 1]);
|
||||
assertEquals(getPosFromNumpadNumber("1"), [0, 2]);
|
||||
assertEquals(getPosFromNumpadNumber("2"), [1, 2]);
|
||||
assertEquals(getPosFromNumpadNumber("3"), [2, 2]);
|
||||
assertEquals(getPosFromNumpadNumber("0"), [1, 3]);
|
||||
assertEquals(getPosFromNumpadNumber("A"), [2, 3]);
|
||||
});
|
||||
|
||||
Deno.test("getNextKeypadRobotState", () => {
|
||||
assertThrows(() => getNextNumpadRobotState({ pos: [0, 0] }, "^"));
|
||||
assertEquals(getNextNumpadRobotState({ pos: [0, 0] }, "v"), {
|
||||
pos: [0, 1],
|
||||
});
|
||||
assertThrows(() => getNextNumpadRobotState({ pos: [0, 0] }, "<"));
|
||||
assertEquals(getNextNumpadRobotState({ pos: [0, 0] }, ">"), {
|
||||
pos: [1, 0],
|
||||
});
|
||||
assertEquals(getNextNumpadRobotState({ pos: [0, 0] }, "A"), {
|
||||
pos: [0, 0],
|
||||
});
|
||||
|
||||
assertEquals(getNextNumpadRobotState({ pos: [1, 1] }, "^"), {
|
||||
pos: [1, 0],
|
||||
});
|
||||
assertEquals(getNextNumpadRobotState({ pos: [1, 1] }, "v"), {
|
||||
pos: [1, 2],
|
||||
});
|
||||
assertEquals(getNextNumpadRobotState({ pos: [1, 1] }, "<"), {
|
||||
pos: [0, 1],
|
||||
});
|
||||
assertEquals(getNextNumpadRobotState({ pos: [1, 1] }, ">"), {
|
||||
pos: [2, 1],
|
||||
});
|
||||
assertEquals(getNextNumpadRobotState({ pos: [1, 1] }, "A"), {
|
||||
pos: [1, 1],
|
||||
});
|
||||
});
|
||||
|
||||
Deno.test("getDirKeypadNumber", () => {
|
||||
assertEquals(getDirpadNumber([0, 0]), " ");
|
||||
assertEquals(getDirpadNumber([1, 0]), "^");
|
||||
assertEquals(getDirpadNumber([2, 0]), "A");
|
||||
assertEquals(getDirpadNumber([0, 1]), "<");
|
||||
assertEquals(getDirpadNumber([1, 1]), "v");
|
||||
assertEquals(getDirpadNumber([2, 1]), ">");
|
||||
});
|
||||
|
||||
Deno.test("getPosFromDirKeypadNumber", () => {
|
||||
assertEquals(getPosFromDirpadNumber(" "), [0, 0]);
|
||||
assertEquals(getPosFromDirpadNumber("^"), [1, 0]);
|
||||
assertEquals(getPosFromDirpadNumber("A"), [2, 0]);
|
||||
assertEquals(getPosFromDirpadNumber("<"), [0, 1]);
|
||||
assertEquals(getPosFromDirpadNumber("v"), [1, 1]);
|
||||
assertEquals(getPosFromDirpadNumber(">"), [2, 1]);
|
||||
});
|
||||
|
||||
Deno.test("getNextDirKeypadRobotState", () => {
|
||||
assertEquals(getNextDirpadRobotState({ pos: [1, 1] }, "^"), {
|
||||
pos: [1, 0],
|
||||
});
|
||||
assertThrows(() => getNextDirpadRobotState({ pos: [1, 1] }, "v"));
|
||||
assertEquals(getNextDirpadRobotState({ pos: [1, 1] }, "<"), {
|
||||
pos: [0, 1],
|
||||
});
|
||||
assertEquals(getNextDirpadRobotState({ pos: [1, 1] }, ">"), {
|
||||
pos: [2, 1],
|
||||
});
|
||||
assertEquals(getNextDirpadRobotState({ pos: [1, 1] }, "A"), {
|
||||
pos: [1, 1],
|
||||
});
|
||||
});
|
||||
|
||||
Deno.test("executeCommandsOnNumpadRobot", () => {
|
||||
const commands = [
|
||||
"<A^A>^^AvvvA".split("") as Command[],
|
||||
"<A^A^>^AvvvA".split("") as Command[],
|
||||
"<A^A^^>AvvvA".split("") as Command[],
|
||||
];
|
||||
for (const command of commands) {
|
||||
const out: string[] = [];
|
||||
executeCommandsOnNumpadRobot(
|
||||
{
|
||||
pos: NUMPAD_FIRST_POS,
|
||||
},
|
||||
command,
|
||||
(o) => {
|
||||
out.push(o);
|
||||
},
|
||||
);
|
||||
assertEquals(out, ["0", "2", "9", "A"]);
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("executeCommandsOnDirKeypadRobot", () => {
|
||||
const input_result = [
|
||||
[
|
||||
"<vA<AA>>^AvAA<^A>A<v<A>>^AvA^A<vA>^A<v<A>^A>AAvA^A<v<A>A>^AAAvA<^A>A"
|
||||
.split("") as Command[],
|
||||
"v<<A>>^A<A>AvA<^AA>A<vAAA>^A",
|
||||
],
|
||||
["v<<A>>^A<A>AvA<^AA>A<vAAA>^A".split("") as Command[], "<A^A>^^AvvvA"],
|
||||
] as const;
|
||||
for (const [input, result] of input_result) {
|
||||
const out: string[] = [];
|
||||
executeCommandsOnDirpadRobot(
|
||||
{
|
||||
pos: DIRPAD_FIRST_POS,
|
||||
},
|
||||
input,
|
||||
(o) => {
|
||||
out.push(o);
|
||||
},
|
||||
);
|
||||
assertEquals(out.join(""), result);
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("shortestPathesOnNumpadRobot", () => {
|
||||
const testCase: {
|
||||
pos: Pos;
|
||||
target: string;
|
||||
expect: string[][];
|
||||
msg: string;
|
||||
}[] = [
|
||||
{
|
||||
pos: NUMPAD_FIRST_POS,
|
||||
target: "A",
|
||||
expect: [[]],
|
||||
msg: "should return empty array when target is same as pos",
|
||||
},
|
||||
{
|
||||
pos: [0, 0],
|
||||
target: "7",
|
||||
expect: [[]],
|
||||
msg: "should return empty array when target is same as pos",
|
||||
},
|
||||
{
|
||||
pos: NUMPAD_FIRST_POS,
|
||||
target: "9",
|
||||
expect: [["^", "^", "^"]],
|
||||
msg: "should return shortest path to target",
|
||||
},
|
||||
{
|
||||
pos: [0, 0],
|
||||
target: "5",
|
||||
expect: [["v", ">"], [">", "v"]],
|
||||
msg: "should return shortest path to target",
|
||||
},
|
||||
{
|
||||
pos: [1, 3],
|
||||
target: "1",
|
||||
expect: [["^", "<"]],
|
||||
msg: "should return shortest path to target",
|
||||
},
|
||||
];
|
||||
for (const { pos, target, expect, msg } of testCase) {
|
||||
assertEquals(shortestPathesOnNumpadRobot({ pos }, target), expect, msg);
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("shortestPathesOnDirpadRobot", () => {
|
||||
const testCase: {
|
||||
pos: Pos;
|
||||
target: string;
|
||||
expect: string[][];
|
||||
msg: string;
|
||||
}[] = [
|
||||
{
|
||||
pos: DIRPAD_FIRST_POS,
|
||||
target: "A",
|
||||
expect: [["A"]],
|
||||
msg: "should return empty array when target is same as pos",
|
||||
},
|
||||
{
|
||||
pos: [1, 0],
|
||||
target: "^",
|
||||
expect: [["A"]],
|
||||
msg: "should return empty array when target is same as pos",
|
||||
},
|
||||
{
|
||||
pos: DIRPAD_FIRST_POS,
|
||||
target: ">",
|
||||
expect: [["v", "A"]],
|
||||
msg: "should return shortest path to target",
|
||||
},
|
||||
{
|
||||
pos: [1, 0],
|
||||
target: ">",
|
||||
expect: [["v", ">", "A"], [">", "v", "A"]],
|
||||
msg: "should return shortest path to target",
|
||||
},
|
||||
{
|
||||
pos: [0, 1],
|
||||
target: "^",
|
||||
expect: [[">", "^", "A"]],
|
||||
msg: "should return shortest path to target",
|
||||
},
|
||||
];
|
||||
for (const { pos, target, expect, msg } of testCase) {
|
||||
assertEquals(shortestPathesOnDirpadRobot({ pos }, target), expect, msg);
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("example test", () => {
|
||||
const testCase: [string, string][] = [
|
||||
[
|
||||
"029A",
|
||||
"<vA<AA>>^AvAA<^A>A<v<A>>^AvA^A<vA>^A<v<A>^A>AAvA^A<v<A>A>^AAAvA<^A>A",
|
||||
],
|
||||
[
|
||||
"980A",
|
||||
"<v<A>>^AAAvA^A<vA<AA>>^AvAA<^A>A<v<A>A>^AAAvA<^A>A<vA>^A<A>A",
|
||||
],
|
||||
[
|
||||
"179A",
|
||||
"<v<A>>^A<vA<A>>^AAvAA<^A>A<v<A>>^AAvA^A<vA>^AA<A>A<v<A>A>^AAAvA<^A>A",
|
||||
],
|
||||
[
|
||||
"456A",
|
||||
"<v<A>>^AA<vA<A>>^AAvAA<^A>A<vA>^A<A>A<vA>^A<A>A<v<A>A>^AAvA<^A>A",
|
||||
],
|
||||
[
|
||||
"379A",
|
||||
"<v<A>>^AvA^A<vA<AA>>^AAvA<^A>AAvA^A<vA>^AA<A>A<v<A>A>^AAAvA<^A>A",
|
||||
],
|
||||
];
|
||||
for (const [input, expect] of testCase) {
|
||||
assertEquals(solve_1(input), expect.length);
|
||||
}
|
||||
});
|
254
day_21/solve_1.ts
Normal file
254
day_21/solve_1.ts
Normal file
@ -0,0 +1,254 @@
|
||||
export type Pos = [number, number];
|
||||
export type NumpadRobotState = {
|
||||
pos: Pos;
|
||||
};
|
||||
export type Command = "^" | "v" | "<" | ">" | "A";
|
||||
|
||||
export function getNextPos(pos: Pos, command: Command): Pos {
|
||||
const [x, y] = pos;
|
||||
switch (command) {
|
||||
case "^":
|
||||
return [x, y - 1];
|
||||
case "v":
|
||||
return [x, y + 1];
|
||||
case "<":
|
||||
return [x - 1, y];
|
||||
case ">":
|
||||
return [x + 1, y];
|
||||
default:
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
const NumbericKeypad = "789\n456\n123\n 0A";
|
||||
export const NUMPAD_FIRST_POS: Pos = [2, 3];
|
||||
export function getNumpadNumber([x, y]: Pos): string {
|
||||
return NumbericKeypad[y * 4 + x];
|
||||
}
|
||||
export function getPosFromNumpadNumber(num: string): Pos {
|
||||
const idx = NumbericKeypad.indexOf(num);
|
||||
return [idx % 4, Math.floor(idx / 4)];
|
||||
}
|
||||
|
||||
export function getNextNumpadRobotState(
|
||||
state: NumpadRobotState,
|
||||
command: Command,
|
||||
): NumpadRobotState {
|
||||
const nextPos = getNextPos(state.pos, command);
|
||||
const [nx, ny] = nextPos;
|
||||
if (
|
||||
nx < 0 || nx > 2 || ny < 0 || ny > 3 ||
|
||||
getNumpadNumber(nextPos) === " "
|
||||
) {
|
||||
throw new Error(
|
||||
`Invalid command: ${command} for pos: ${state.pos.join(",")} -> ${
|
||||
nextPos.join(",")
|
||||
} is out of bound`,
|
||||
);
|
||||
}
|
||||
return { pos: nextPos };
|
||||
}
|
||||
|
||||
const DirectionalKeypad = " ^A\n<v>";
|
||||
export const DIRPAD_FIRST_POS: Pos = [2, 0];
|
||||
export type DirKeypadRobotState = {
|
||||
pos: Pos;
|
||||
};
|
||||
export function getDirpadNumber([x, y]: Pos): string {
|
||||
return DirectionalKeypad[y * 4 + x];
|
||||
}
|
||||
export function getPosFromDirpadNumber(num: string): Pos {
|
||||
const idx = DirectionalKeypad.indexOf(num);
|
||||
return [idx % 4, Math.floor(idx / 4)];
|
||||
}
|
||||
|
||||
export function getNextDirpadRobotState(
|
||||
state: DirKeypadRobotState,
|
||||
command: Command,
|
||||
): DirKeypadRobotState {
|
||||
const nextPos = getNextPos(state.pos, command);
|
||||
const [nx, ny] = nextPos;
|
||||
if (
|
||||
nx < 0 || nx > 2 || ny < 0 || ny > 1 ||
|
||||
getDirpadNumber(nextPos) === " "
|
||||
) {
|
||||
throw new Error(
|
||||
`Invalid command: ${command} for pos: ${state.pos.join(",")} -> ${
|
||||
nextPos.join(",")
|
||||
} is out of bound`,
|
||||
);
|
||||
}
|
||||
return { pos: nextPos };
|
||||
}
|
||||
|
||||
export function executeCommandsOnNumpadRobot(
|
||||
state: NumpadRobotState,
|
||||
commands: Command[],
|
||||
output: (out: string) => void,
|
||||
): NumpadRobotState {
|
||||
return commands.reduce((state, command) => {
|
||||
if (command === "A") {
|
||||
output(getNumpadNumber(state.pos));
|
||||
}
|
||||
return getNextNumpadRobotState(state, command);
|
||||
}, state);
|
||||
}
|
||||
|
||||
export function executeCommandsOnDirpadRobot(
|
||||
state: DirKeypadRobotState,
|
||||
commands: Command[],
|
||||
output: (out: string) => void,
|
||||
): DirKeypadRobotState {
|
||||
return commands.reduce((state, command) => {
|
||||
if (command === "A") {
|
||||
output(getDirpadNumber(state.pos));
|
||||
}
|
||||
return getNextDirpadRobotState(state, command);
|
||||
}, state);
|
||||
}
|
||||
|
||||
export function shortestPathesOnNumpadRobot(
|
||||
state: NumpadRobotState,
|
||||
targetNum: string,
|
||||
): Command[][] {
|
||||
const targetPos = getPosFromNumpadNumber(targetNum);
|
||||
const len = Math.abs(targetPos[0] - state.pos[0]) +
|
||||
Math.abs(targetPos[1] - state.pos[1]);
|
||||
const queue: [Command[], NumpadRobotState][] = [[[], state]];
|
||||
const paths: Command[][] = [];
|
||||
while (queue.length > 0) {
|
||||
const [path, state] = queue.shift()!;
|
||||
if (path.length === len) {
|
||||
if (
|
||||
state.pos[0] === targetPos[0] && state.pos[1] === targetPos[1]
|
||||
) {
|
||||
paths.push(path);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (const command of ["^", "v", "<", ">"] as Command[]) {
|
||||
try {
|
||||
const nextState = getNextNumpadRobotState(state, command);
|
||||
queue.push([path.concat(command), nextState]);
|
||||
} catch (e) {
|
||||
// Ignore invalid command
|
||||
}
|
||||
}
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
export function shortestPathesOnDirpadRobot(
|
||||
state: DirKeypadRobotState,
|
||||
targetNum: string,
|
||||
): Command[][] {
|
||||
const targetPos = getPosFromDirpadNumber(targetNum);
|
||||
const len = Math.abs(targetPos[0] - state.pos[0]) +
|
||||
Math.abs(targetPos[1] - state.pos[1]);
|
||||
const queue: [Command[], DirKeypadRobotState][] = [[[], state]];
|
||||
const paths: Command[][] = [];
|
||||
while (queue.length > 0) {
|
||||
const [path, state] = queue.shift()!;
|
||||
if (path.length === len) {
|
||||
if (
|
||||
state.pos[0] === targetPos[0] && state.pos[1] === targetPos[1]
|
||||
) {
|
||||
paths.push(path);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (const command of ["^", "v", "<", ">"] as Command[]) {
|
||||
try {
|
||||
const nextState = getNextDirpadRobotState(state, command);
|
||||
queue.push([path.concat(command), nextState]);
|
||||
} catch (e) {
|
||||
// Ignore invalid command
|
||||
}
|
||||
}
|
||||
}
|
||||
return paths.map((p) => p.concat("A"));
|
||||
}
|
||||
|
||||
export function StepShortestPathesOnDirpadRobot(
|
||||
state: DirKeypadRobotState,
|
||||
targetNums: string[],
|
||||
) {
|
||||
return targetNums.reduce( (acc, targetNum) => {
|
||||
const prevPos = acc.prevPos;
|
||||
const paths = shortestPathesOnDirpadRobot({ pos: prevPos }, targetNum);
|
||||
if (paths.length === 0) {
|
||||
throw new Error(`No path found for ${targetNum} from ${prevPos}`);
|
||||
}
|
||||
return {
|
||||
prevPos: getPosFromDirpadNumber(targetNum),
|
||||
// product of all
|
||||
paths: acc.paths.flatMap((p) => paths.map((np) => p.concat(np))),
|
||||
}
|
||||
}, {
|
||||
prevPos: state.pos,
|
||||
paths: [[]] as Command[][],
|
||||
}).paths;
|
||||
}
|
||||
|
||||
function filterShortestPathes(paths: Command[][]) {
|
||||
let len = Number.MAX_SAFE_INTEGER;
|
||||
for (const path of paths) {
|
||||
len = Math.min(len, path.length);
|
||||
}
|
||||
// len = Math.min(...paths.map((p) => p.length)); // this is call stack overflow...
|
||||
// because the array is too big, the argument list is too long.
|
||||
// so we need to calculate the min value by ourself.
|
||||
return paths.filter((p) => p.length === len);
|
||||
}
|
||||
|
||||
export function solve_1(code: string) {
|
||||
const numbers = code.split("");
|
||||
let start = NUMPAD_FIRST_POS;
|
||||
|
||||
const finalOuts: string[] = [];
|
||||
|
||||
for (const num of numbers) {
|
||||
console.log("num", num);
|
||||
const numpadPaths = shortestPathesOnNumpadRobot({ pos: start }, num).map((p) => p.concat("A"));
|
||||
if (numpadPaths.length === 0) {
|
||||
throw new Error(`No path found for ${num} from ${start}`);
|
||||
}
|
||||
console.log("avail path", numpadPaths.map((p) => p.join("")));
|
||||
const pathes1 = filterShortestPathes(numpadPaths.flatMap((numpadPath) => {
|
||||
return StepShortestPathesOnDirpadRobot({ pos: DIRPAD_FIRST_POS }, numpadPath);
|
||||
}));
|
||||
|
||||
console.log("path1",pathes1.map((p) => p.join("")));
|
||||
const pathes2 = filterShortestPathes(pathes1.flatMap((path) => {
|
||||
return StepShortestPathesOnDirpadRobot({ pos: DIRPAD_FIRST_POS }, path);
|
||||
}));
|
||||
console.log("path2",pathes2.map((p) => p.join("")));
|
||||
const pathes3 = filterShortestPathes(pathes2.flatMap((path) => {
|
||||
return StepShortestPathesOnDirpadRobot({ pos: DIRPAD_FIRST_POS }, path);
|
||||
}));
|
||||
// console.log("path3",pathes3.map((p) => p.join("")));
|
||||
const executePath3 = (path: Command[]) => {
|
||||
const outs: string[] = [];
|
||||
executeCommandsOnDirpadRobot({ pos: DIRPAD_FIRST_POS }, path, (o) => { outs.push(o); });
|
||||
console.log("outs", outs.join(""));
|
||||
return outs;
|
||||
};
|
||||
finalOuts.push(...executePath3(pathes3[0]));
|
||||
start = getPosFromNumpadNumber(num);
|
||||
}
|
||||
console.log("finalOuts", finalOuts.join(""), finalOuts.length);
|
||||
return finalOuts.length;
|
||||
}
|
||||
|
||||
|
||||
if (import.meta.main) {
|
||||
const codes = Deno.readTextFileSync("input.txt").replaceAll("\r","").split("\n");
|
||||
let total = 0;
|
||||
for (const code of codes){
|
||||
const len = solve_1(code);
|
||||
const n = parseInt(code.slice(0,3));
|
||||
console.log("len", len, n, len * n);
|
||||
total += n * len;
|
||||
}
|
||||
console.log("total", total);
|
||||
}
|
124
day_21/solve_2.test.ts
Normal file
124
day_21/solve_2.test.ts
Normal file
@ -0,0 +1,124 @@
|
||||
import { assertEquals } from "jsr:@std/assert/equals";
|
||||
import {
|
||||
availableShortestPaths,
|
||||
Command,
|
||||
inputCommand,
|
||||
inputCommands,
|
||||
Path,
|
||||
PathSetHandler,
|
||||
shortestPathLength,
|
||||
} from "./solve_2.ts";
|
||||
|
||||
Deno.test("availableShortestPaths - blocked", () => {
|
||||
const paths = availableShortestPaths("<", "^");
|
||||
assertEquals(paths.map((x) => x.toString()), [">^"]);
|
||||
});
|
||||
|
||||
Deno.test("availableShortestPaths - common", () => {
|
||||
const paths = availableShortestPaths(">", "^");
|
||||
assertEquals(paths.map((x) => x.toString()).sort(), ["<^", "^<"].sort());
|
||||
});
|
||||
|
||||
Deno.test("Path - length", () => {
|
||||
const path = new Path(["^", "v", "<", ">", "A"]);
|
||||
assertEquals(path.length, 5);
|
||||
});
|
||||
|
||||
Deno.test("Path - execute", () => {
|
||||
const path = new Path(["<", "v", "A", ">", "A"]);
|
||||
const ret: Command[] = [];
|
||||
path.execute("A", (c) => {
|
||||
ret.push(c);
|
||||
});
|
||||
assertEquals(ret.join(""), "v>");
|
||||
});
|
||||
|
||||
Deno.test("Path - concat", () => {
|
||||
const path = new Path(["<", "v", "A", ">", "A"]);
|
||||
assertEquals(path.concat("^").toString(), "<vA>A^");
|
||||
});
|
||||
|
||||
Deno.test("PathHandler - coproduct", () => {
|
||||
const lhs = PathSetHandler.fromPathSet([Path.from(["A"])]);
|
||||
const rhs = PathSetHandler.fromPathSet([Path.from(["v"])]);
|
||||
const set = lhs.coproduct(rhs);
|
||||
const [m, _] = set.call(">");
|
||||
assertEquals(
|
||||
m.map((x) => x.toString()).sort(),
|
||||
[
|
||||
"A",
|
||||
"v",
|
||||
].sort(),
|
||||
);
|
||||
});
|
||||
Deno.test("PathHandler - product", () => {
|
||||
const lhs = PathSetHandler.fromPathSet([Path.from(["A"])]);
|
||||
const rhs = PathSetHandler.fromPathSet([Path.from(["v"])]);
|
||||
const set = lhs.product(rhs);
|
||||
const [m, _] = set.call(">");
|
||||
assertEquals(
|
||||
m.map((x) => x.toString()).sort(),
|
||||
[
|
||||
"Av",
|
||||
].sort(),
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("inputCommand", () => {
|
||||
const handler = inputCommand("^");
|
||||
const [m, a] = handler.call(">");
|
||||
assertEquals(
|
||||
m.map((x) => x.toString()).sort(),
|
||||
[
|
||||
"<^A",
|
||||
"^<A",
|
||||
].sort(),
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("inputCommands", () => {
|
||||
const handler = inputCommands(Path.from([">", "A"]));
|
||||
const [m, a] = handler.call(">");
|
||||
assertEquals(
|
||||
m.map((x) => x.toString()).sort(),
|
||||
[
|
||||
"A^A",
|
||||
].sort(),
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("shortestPathLength", () => {
|
||||
const inputcmdsShort = (start: Command,
|
||||
cmds: Command[]) => shortestPathLength(inputCommands(Path.from(cmds)))(start)[0];
|
||||
|
||||
assertEquals(inputcmdsShort("A", (["A"])), 1);
|
||||
assertEquals(inputcmdsShort("A", (["A", ">"])), 3);
|
||||
assertEquals(inputcmdsShort("A", (["A", ">", "^"])), 6);
|
||||
|
||||
assertEquals(inputcmdsShort("A", (["<"])), 4);
|
||||
assertEquals(inputcmdsShort("A", (["<", "A"])), 8);
|
||||
});
|
||||
|
||||
Deno.test("shortestPathLength - property", () => {
|
||||
// random path list
|
||||
const pathes = [
|
||||
Path.from(["^", "v", "<", ">", "A"]),
|
||||
Path.from(["^", "v", "<", ">", "A", "v"]),
|
||||
Path.from(["^", "v", "<", ">", "A", "v", "<"]),
|
||||
Path.from(["^", "v", "<", ">", "A", "v", "<", ">"]),
|
||||
Path.from(["A", "v", "v", ">", ">", "v", ">", "v"]),
|
||||
];
|
||||
for (const paths of pathes) {
|
||||
for (const s of ["^", "v", "<", ">", "A"] as Command[]) {
|
||||
const [x, ...xs] = paths;
|
||||
const s1 = shortestPathLength(inputCommands(Path.from([x])))(s)[0];
|
||||
const s2 = shortestPathLength(inputCommands(Path.from(xs)))(x)[0];
|
||||
const s3 = shortestPathLength(inputCommands(paths))(s)[0];
|
||||
assertEquals(
|
||||
s1 + s2,
|
||||
s3,
|
||||
`s: ${s}, x: ${x}, xs: ${xs}, s1: ${s1}, s2: ${s2}, s3: ${s3}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
460
day_21/solve_2.ts
Normal file
460
day_21/solve_2.ts
Normal file
@ -0,0 +1,460 @@
|
||||
// fg is shortest available pathes function that returns a path from pos to target.
|
||||
// type cmd :: enum {
|
||||
// up'^', down'v', right'<', left'>', accept'A'
|
||||
|
||||
import { getPosFromNumpadNumber, NUMPAD_FIRST_POS, shortestPathesOnNumpadRobot } from "./solve_1.ts";
|
||||
|
||||
// }
|
||||
export type Command = "^" | "v" | "<" | ">" | "A";
|
||||
export type Pos = [number, number];
|
||||
// type path = list of cmd;
|
||||
// type path_set = set of path;
|
||||
export type PathSet = Path[];
|
||||
|
||||
// |--|--|--|
|
||||
// | |^ | A|
|
||||
// |--|--|--|
|
||||
// |< |v | >|
|
||||
// |--|--|--|
|
||||
const lookupCommandPos: Record<Command, Pos> = {
|
||||
"^": [1, 0],
|
||||
"v": [1, 1],
|
||||
"<": [0, 1],
|
||||
">": [2, 1],
|
||||
"A": [2, 0],
|
||||
};
|
||||
export function commandToPos(command: Command): Pos {
|
||||
return lookupCommandPos[command];
|
||||
}
|
||||
export function posToCommand(pos: Pos): Command {
|
||||
for (const [key, value] of Object.entries(lookupCommandPos)) {
|
||||
if (value[0] === pos[0] && value[1] === pos[1]) {
|
||||
return key as Command;
|
||||
}
|
||||
}
|
||||
throw new Error(`invalid pos: ${pos}`);
|
||||
}
|
||||
|
||||
export class Path {
|
||||
value: string;
|
||||
constructor(commands: Command[]) {
|
||||
this.value = commands.join("");
|
||||
}
|
||||
get length() {
|
||||
return this.value.length;
|
||||
}
|
||||
[Symbol.iterator](): IterableIterator<Command> {
|
||||
return this.value[Symbol.iterator]() as IterableIterator<Command>;
|
||||
}
|
||||
first() {
|
||||
return this.value[0] as Command;
|
||||
}
|
||||
rest() {
|
||||
const ret = new Path([]);
|
||||
ret.value = this.value.slice(1);
|
||||
return ret;
|
||||
}
|
||||
map<T>(fn: (cmd: Command) => T) {
|
||||
const result: T[] = [];
|
||||
for (const cmd of this.value) {
|
||||
result.push(fn(cmd as Command));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
flatMap<T>(fn: (cmd: Command) => T[]) {
|
||||
const result: T[] = [];
|
||||
for (const cmd of this.value) {
|
||||
result.push(...fn(cmd as Command));
|
||||
}
|
||||
}
|
||||
reduce<T>(fn: (acc: T, cmd: Command) => T, init: T) {
|
||||
let acc = init;
|
||||
for (const cmd of this.value) {
|
||||
acc = fn(acc, cmd as Command);
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
get last() {
|
||||
return this.value[this.value.length - 1];
|
||||
}
|
||||
toString() {
|
||||
return this.value;
|
||||
}
|
||||
toCommands() {
|
||||
return this.value.split("") as Command[];
|
||||
}
|
||||
// ++ is union of two sets.
|
||||
// concat :: cmd -> path_set -> path_set
|
||||
// concat x [] = [x]
|
||||
// concat x (y:ys) = (y:x) ++ (concat x ys)
|
||||
concat(command: Command) {
|
||||
const ret = new Path([]);
|
||||
ret.value = this.value + command;
|
||||
return ret;
|
||||
}
|
||||
concatPath(path: Path) {
|
||||
const ret = new Path([]);
|
||||
ret.value = this.value + path.value;
|
||||
return ret;
|
||||
}
|
||||
static from(commands: Command[]) {
|
||||
return new Path(commands);
|
||||
}
|
||||
// \product is a function that returns a list of all possible combinations of two lists.
|
||||
// \product :: path_set -> path_set -> path_set
|
||||
// \product x [] = []
|
||||
// \product (x:xs) y = (concat x y) ++ (\product xs y)
|
||||
// infix 5 \product // associativity is not important. because it commutative.
|
||||
// product :: path_set -> path_set -> path_set
|
||||
// product a b = a \product b
|
||||
|
||||
static product(a: PathSet, b: PathSet): PathSet {
|
||||
return a.flatMap((x) => b.map((y) => x.concatPath(y)));
|
||||
}
|
||||
|
||||
// it's not pure function.
|
||||
// only use for test.
|
||||
// execute :: (cmd, cmd -> never) -> Option of next_pos
|
||||
execute(start: Command, onAccept: (accept: Command) => void): Pos | null {
|
||||
let pos = commandToPos(start);
|
||||
for (const command of this.toCommands()) {
|
||||
if (command === "A") {
|
||||
onAccept(posToCommand(pos));
|
||||
continue;
|
||||
}
|
||||
const newPos = moveSingle(pos, command);
|
||||
if (newPos === null) {
|
||||
return null;
|
||||
}
|
||||
pos = newPos;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
equals(other: Path) {
|
||||
return this.value === other.value;
|
||||
}
|
||||
}
|
||||
|
||||
const lookupCommandDelta: Record<Command, Pos> = {
|
||||
"^": [0, -1],
|
||||
"v": [0, 1],
|
||||
"<": [-1, 0],
|
||||
">": [1, 0],
|
||||
"A": [0, 0],
|
||||
};
|
||||
export function pathToDelta(command: Command): Pos {
|
||||
return lookupCommandDelta[command];
|
||||
}
|
||||
export function moveSingle(pos: Pos, command: Command): Pos | null {
|
||||
const [x, y] = pos;
|
||||
const [dx, dy] = pathToDelta(command);
|
||||
const newPos: [number, number] = [x + dx, y + dy];
|
||||
// out of range
|
||||
if (newPos[0] < 0 || newPos[0] > 2 || newPos[1] < 0 || newPos[1] > 1) {
|
||||
return null;
|
||||
}
|
||||
// invalid position
|
||||
if (newPos[0] === 0 && newPos[1] === 0) {
|
||||
return null;
|
||||
}
|
||||
return newPos;
|
||||
}
|
||||
|
||||
// type Handler 'T :: start_pos -> ('T, next_pos)
|
||||
export type Handler<T> = (start: Command) => [T, Command];
|
||||
export class PathSetHandler {
|
||||
private fn: Handler<PathSet>;
|
||||
constructor(fn: Handler<PathSet>) {
|
||||
this.fn = fn;
|
||||
}
|
||||
// call :: Handler of start_pos -> (path_set, next_pos)
|
||||
// call f = f
|
||||
call(command: Command) {
|
||||
return this.fn(command);
|
||||
}
|
||||
|
||||
// mempty :: Handler of path_set
|
||||
// mempty = \s -> ([], s)
|
||||
static empty() {
|
||||
return new PathSetHandler(
|
||||
(s: Command) => [[], s],
|
||||
);
|
||||
}
|
||||
// munit :: Handler of path_set
|
||||
// munit = \s -> ([[]], s)
|
||||
static unit() {
|
||||
return new PathSetHandler(
|
||||
(s: Command) => [[Path.from([])], s],
|
||||
);
|
||||
}
|
||||
static fromPathSet(t: PathSet) {
|
||||
return new PathSetHandler(
|
||||
(s: Command) => [t, s],
|
||||
);
|
||||
}
|
||||
|
||||
coproduct(f: PathSetHandler) {
|
||||
// union of two sets.
|
||||
return new PathSetHandler(
|
||||
(s: Command) => {
|
||||
const [t, s1] = this.fn(s);
|
||||
const [a, s2] = f.call(s1);
|
||||
// merge two sets t and a.
|
||||
const set = new Set([...t, ...a]);
|
||||
const result = Array.from(set);
|
||||
return [result, s2];
|
||||
},
|
||||
);
|
||||
}
|
||||
// mproduct :: Handler of path_set -> Handler of path_set -> Handler of path_set
|
||||
// mproduct f g = s ->
|
||||
// let (t, s') = f s in
|
||||
// let (a ,s'') = g s'
|
||||
// in ( (product t a ) , s'')
|
||||
// op ; = mproduct
|
||||
// infixr 5 ;
|
||||
product(f: PathSetHandler) {
|
||||
return new PathSetHandler(
|
||||
(s: Command) => {
|
||||
const [t, s1] = this.fn(s);
|
||||
const [a, s2] = f.fn(s1);
|
||||
return [Path.product(t, a), s2];
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// available_paths :: (start_pos, target_pos) -> path_set
|
||||
}
|
||||
|
||||
// type start_pos = cmd
|
||||
// type target_pos = cmd
|
||||
// type next_pos = cmd
|
||||
// available_paths :: (start_pos, target_pos) -> path_set
|
||||
export function availableShortestPaths(
|
||||
start: Command,
|
||||
target: Command,
|
||||
): PathSet {
|
||||
const startPos = commandToPos(start);
|
||||
const targetPos = commandToPos(target);
|
||||
const paths: Path[] = [];
|
||||
const queue: [Path, Pos][] = [[new Path([]), startPos]];
|
||||
const deltaSum = Math.abs(targetPos[0] - startPos[0]) +
|
||||
Math.abs(targetPos[1] - startPos[1]);
|
||||
while (queue.length > 0) {
|
||||
const [path, pos] = queue.shift()!;
|
||||
if (pos[0] === targetPos[0] && pos[1] === targetPos[1]) {
|
||||
paths.push(path);
|
||||
continue;
|
||||
}
|
||||
if (path.length >= deltaSum) {
|
||||
continue;
|
||||
}
|
||||
for (const command of ["^", "v", "<", ">"] as Command[]) {
|
||||
const nextPos = moveSingle(pos, command);
|
||||
if (nextPos) {
|
||||
queue.push([path.concat(command), nextPos]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (paths.length === 0) {
|
||||
throw new Error(`no path found from ${start} to ${target}`);
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
// move :: cmd -> Handler of path_set
|
||||
// move cmd = \s -> (available_paths (s, cmd), cmd)
|
||||
export const move: (cmd: Command) => PathSetHandler = (command: Command) => {
|
||||
return new PathSetHandler((start: Command) => {
|
||||
return [availableShortestPaths(start, command), command];
|
||||
});
|
||||
};
|
||||
export const accept: PathSetHandler = PathSetHandler.fromPathSet([
|
||||
Path.from(["A"]),
|
||||
]);
|
||||
// inputCommand :: cmd -> Handler of path_set
|
||||
// inputCommand cmd = (move cmd); accept
|
||||
export const inputCommand = (command: Command) => move(command).product(accept);
|
||||
// inputCommands :: list of cmd -> Handler of path_set
|
||||
// inputCommands xs = foldl (x -> y -> x; y) munit xs
|
||||
// or
|
||||
// inputCommands [] = munit
|
||||
// inputCommands (x:xs) = (inputCommand x) ; (inputCommands xs)
|
||||
export const inputCommands = (commands: Path) => {
|
||||
return commands.reduce((acc, command) => {
|
||||
return acc.product(inputCommand(command));
|
||||
}, PathSetHandler.unit());
|
||||
};
|
||||
|
||||
// shortest_path_length :: (handler of PathSet) -> handler of Int
|
||||
// shortest_path_length h = \s -> (h s) |> foldl ((x,y) -> min(len x,len y)) INF
|
||||
export function shortestPathLength(h: PathSetHandler): Handler<number> {
|
||||
return (s: Command) => {
|
||||
const [paths, last] = h.call(s);
|
||||
return [
|
||||
paths.map((x) => x.length).reduce(
|
||||
(x, y) => Math.min(x, y),
|
||||
Infinity,
|
||||
),
|
||||
last,
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
// shortest_path_length has property of shortest path length.
|
||||
// shortest_path_length (inputCommands x:xs) s = shortest_path_length (inputCommands [x]) s + shortest_path_length (inputCommands xs) x
|
||||
/**
|
||||
* the following assertion is always true.
|
||||
* ```ts
|
||||
* for (const s of ["^", "v", "<", ">", "A"] as Command[]) {
|
||||
* // any path
|
||||
* const paths = makeRandomSnapshotOfPath();
|
||||
* const [x, ...xs] = paths;
|
||||
* const s1 = shortestPathLengthRaw(inputCommands(Path.from([x])))(s)[0];
|
||||
* const s2 = shortestPathLengthRaw(inputCommands(Path.from(xs)))(x)[0];
|
||||
* const s3 = shortestPathLengthRaw(inputCommands(paths))(s)[0];
|
||||
* assertEquals(
|
||||
* s1 + s2,
|
||||
* s3,
|
||||
* `s: ${s}, x: ${x}, xs: ${xs}, s1: ${s1}, s2: ${s2}, s3: ${s3}`,
|
||||
* );
|
||||
* }
|
||||
*/
|
||||
|
||||
function double(xs: Path) {
|
||||
return (s1: Command) => {
|
||||
return (s2: Command) => {
|
||||
const [pathset] = inputCommands(xs).call(s1);
|
||||
return pathset.flatMap((p) => {
|
||||
const [pathset2] = inputCommands(p).call(s2);
|
||||
return pathset2;
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
// const data = double(Path.from("<A^A>^^AvvvA".split("") as Command[]))("A")("A");
|
||||
// console.log(data.map((x) => x.toString()));
|
||||
// double xs = \s1 -> \s2 -> flatMap (\cmd -> fst.inputCommands cmd s2) (fst.inputCommands xs s1)
|
||||
// = \s1 -> \s2 -> flatMap ((\cmd -> fst.inputCommands cmd s2) (fst.inputCommands x:xs s1))
|
||||
// = \s1 -> \s2 -> flatMap (\cmd -> fst.inputCommands cmd s2) ((fst.inputCommand x; fst.inputCommands xs) s1)
|
||||
// = \s1 -> \s2 -> flatMap (\cmd -> fst.inputCommands cmd s2) ((move x; accept; fst.inputCommands xs) s1)
|
||||
// = \s1 -> \s2 -> flatMap (\cmd -> fst.inputCommands cmd s2) (
|
||||
// let (p,s3) = (move x; accept; s1) in
|
||||
// p \product (inputCommands xs s3)
|
||||
// )
|
||||
// = \s1 -> \s2 -> flatMap (\cmd -> fst.inputCommands cmd s2) (
|
||||
// let (p,s3) = (concat (move x s1) 'A') in
|
||||
// p \product (inputCommands xs s3)
|
||||
// )
|
||||
// = \s1 -> \s2 -> (
|
||||
// let (p,s3) = (concat (move x s1) 'A') in
|
||||
// flatMap (\cmd -> fst.inputCommands cmd s2) p \product flatMap (\cmd -> fst.inputCommands cmd s2) (fst.inputCommands xs s3)
|
||||
// )
|
||||
//
|
||||
|
||||
// shortest_path_length \s' -> (double xs 'A' s') 'A'
|
||||
// = shortest_path_length \s' -> (
|
||||
// let (p,s3) = (concat (move x 'A') 'A') in
|
||||
// flatMap (\cmd -> fst.inputCommands cmd s') p \product flatMap (\cmd -> fst.inputCommands cmd 's) (fst.inputCommands xs s3)
|
||||
// ) 'A'
|
||||
// = (
|
||||
// let (p,s3) = (concat (move x 'A') 'A') in
|
||||
// let paths = flatMap (\cmd -> fst.inputCommands cmd 'A') p \product flatMap (\cmd -> fst.inputCommands cmd 'A') (fst.inputCommands xs s3)
|
||||
// in
|
||||
// paths |> foldl ((x,y) -> min(len x,len y)) INF
|
||||
// )
|
||||
// = (
|
||||
// let handler2 = \cmd -> fst.inputCommands cmd 'A' in
|
||||
// let (p,s3) = (concat (move x 'A') 'A') in
|
||||
// let paths = flatMap handler2 p \product flatMap handler2 (fst.inputCommands xs s3) in
|
||||
// paths |> set.min (\x -> len x)
|
||||
// )
|
||||
// = (
|
||||
// let handler2 = \cmd -> fst.inputCommands cmd 'A' in
|
||||
// let (p,s3) = (concat (move x 'A') 'A') in
|
||||
// set.min (\x -> len x)
|
||||
// (flatMap handler2 p \product flatMap handler2 (fst.inputCommands xs s3))
|
||||
// = (
|
||||
// let handler2 = \cmd -> fst.inputCommands cmd 'A' in
|
||||
// let (p,s3) = (concat (move x 'A') 'A') in
|
||||
// (set.min (\x -> len x)
|
||||
// (flatMap handler2 p)) +
|
||||
// (set.min (\x -> len x) (flatMap handler2 (fst.inputCommands xs s3))
|
||||
// )
|
||||
|
||||
function shortL(
|
||||
curPos: Command,
|
||||
command: Command,
|
||||
): number {
|
||||
const [n] = shortestPathLength(inputCommands(Path.from([command])))(curPos);
|
||||
return n;
|
||||
}
|
||||
|
||||
const memo = new Map<string, number>();
|
||||
function myShortestPathLength(
|
||||
depth: number,
|
||||
cmds: Path,
|
||||
): number {
|
||||
const key = cmds.toString() + depth;
|
||||
if (memo.has(key)) {
|
||||
return memo.get(key)!;
|
||||
}
|
||||
if (depth === 1) {
|
||||
const ret = shortestPathLength(inputCommands(cmds))("A")[0];
|
||||
memo.set(key, ret);
|
||||
return ret;
|
||||
}
|
||||
const ret = cmds.reduce((acc, cmd) => {
|
||||
const handler = inputCommand(cmd);
|
||||
const [pathset, last] = handler.call(acc.last);
|
||||
const n = pathset.map((x) => myShortestPathLength(depth - 1, x)).reduce(
|
||||
(x, y) => Math.min(x, y),
|
||||
Infinity,
|
||||
);
|
||||
return {
|
||||
last: last,
|
||||
sum: acc.sum + n,
|
||||
};
|
||||
}, {
|
||||
last: "A" as Command,
|
||||
sum: 0,
|
||||
}).sum;
|
||||
memo.set(key, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
const example = Path.from("<A^A>^^AvvvA".split("") as Command[]);
|
||||
console.log(myShortestPathLength(1, example));
|
||||
|
||||
const codes = Deno.readTextFileSync("input.txt").replaceAll("\r", "").split(
|
||||
"\n",
|
||||
);
|
||||
|
||||
let total = 0;
|
||||
for (const code of codes) {
|
||||
let start = NUMPAD_FIRST_POS;
|
||||
for (const num of code.split("")) {
|
||||
console.log("num", num);
|
||||
const numpadPaths = shortestPathesOnNumpadRobot({ pos: start }, num)
|
||||
.map((p) => p.concat("A"));
|
||||
if (numpadPaths.length === 0) {
|
||||
throw new Error(`No path found for ${num} from ${start}`);
|
||||
}
|
||||
console.log("avail path", numpadPaths.map((p) => p.join("")));
|
||||
|
||||
let min = Infinity;
|
||||
for (const path of numpadPaths) {
|
||||
const len = myShortestPathLength(25, Path.from(path));
|
||||
min = Math.min(min, len);
|
||||
}
|
||||
const n = parseInt(code.slice(0, 3));
|
||||
console.log("len", min, "n", n);
|
||||
total += n * min;
|
||||
|
||||
start = getPosFromNumpadNumber(num);
|
||||
}
|
||||
}
|
||||
console.log("total", total);
|
||||
}
|
Loading…
Reference in New Issue
Block a user