/*
--------- Candy Crush 1D -------------
Write a function to crush candy in one dimensional board. In candy crushing games,
groups of like items are removed from the board. In this problem, any sequence of 3
or more like items should be removed and any items adjacent to that sequence should
now be considered adjacent to each other. This process should be repeated as many
time as possible. You should greedily remove characters from left to right.
*/
type ConstructTuple<T extends number, P extends unknown[] = []> = P['length'] extends T ?
P : ConstructTuple<T,[...P, unknown]>
type Subtract<Left extends number, Right extends number> =
ConstructTuple<Left> extends [...infer Head, ...ConstructTuple<Right>]
? Head['length']
: never;
type StrikeThrough = {
a: "a̶";
b: "b̶";
c: "c̶";
d: "d̶";
e: "e̶";
f: "f̶";
g: "g̶";
h: "h̶";
i: "i̶";
j: "j̶";
k: "k̶";
l: "l̶";
m: "m̶";
n: "n̶";
o: "o̶";
p: "p̶";
q: "q̶";
r: "r̶";
s: "s̶";
t: "t̶";
u: "u̶";
v: "v̶";
w: "w̶";
x: "x̶";
y: "y̶";
z: "z̶";
"☐": "☐̶";
"❍": "❍̶";
"△": "△̶";
"▽": "▽̶"
};
type Cast<T, U> = T extends U ? T : never;
type StringToArray<T extends string> = T extends `${infer Left}${infer Right}`
? [Left, ...StringToArray<Right>]
: [];
type Join<
T extends unknown[],
P extends string = "",
JoinedString extends string = ""
> = T extends [infer Head, ...infer C]
? T["length"] extends 1
? Join<C, P, `${JoinedString}${Head & string}`>
: Join<C, P, `${JoinedString}${Head & string}${P}`>
: JoinedString;
type CandyCrush1D<
T extends unknown[],
B extends string[] = [],
Previous extends string = "",
Original extends string[] = [],
Steps extends string[] = [Join<T>]
> = T extends [infer Left, ...infer Right]
? T["length"] extends 0
? Original
: Left extends Previous
? CandyCrush1D<Right, [...B, Left], Left, Original, Steps>
: B["length"] extends 0 | 1 | 2
? CandyCrush1D<
Right,
[Cast<Left, string>],
Cast<Left, string>,
[...Original, ...B],
Steps
>
: CandyCrush1D<
[...Original, Left, ...Right],
[],
"",
[],
[
...Steps,
`${Join<Original>} |-${ConvertToStrikethrough<B>}-| ${Left &
string}${Join<Right>}`
]
>
: B["length"] extends 0 | 1 | 2
? [...Steps, Join<[...Original, ...B]>]
: [...Steps, `${Join<Original>} |-${ConvertToStrikethrough<B>}-|`, Join<Original>];
type Count<
T extends number,
P extends number[] = [],
Counter extends number[] = []
> = P["length"] extends T
? P
: Count<T, [...P, Counter["length"]], [...Counter, 1]>;
type Lap<T extends string[], P extends number[]> = {
[K in P[number] as K extends 0 ? `Start` : K extends Subtract<T['length'],1> ? 'Final' : `Step ${K}`]: T[K];
};
type ConvertToStrikethrough<
T extends string[],
Output extends string = ""
> = T extends [infer Left, ...infer Right]
? ConvertToStrikethrough<Cast<Right,string[]>, `${Output}${StrikeThrough[Left & keyof StrikeThrough]}`>
: Output;
type CandyCrushArray<T extends string> = CandyCrush1D<StringToArray<T>>;
type CandyCrushSteps<T extends any[]> = Lap<T, Count<T["length"]>>;
type SolveCandyCrush<T extends string> = CandyCrushSteps<CandyCrushArray<T>>
// ───────────────────────── @results ─────────────────────────
// (only the type aliases below resolve in the live view)
type Test1 = SolveCandyCrush<'abaaccddddcabbaaa'>
type Test2 = SolveCandyCrush<'ccaaaccddddcccdbaaa'>
type Test3 = SolveCandyCrush<"asdsssaassasdaaddxxsssszfyaddddaaattt">
type Test4 = SolveCandyCrush<'△△❍☐☐☐❍❍❍❍❍△△▽▽△▽▽▽❍❍❍▽△☐'>