texoport

solving yelp ratings at the type level

i swear im going to actually do a writeup on this in 2 days (remind me)

problem: https://programmingpuzzles.com/puzzles/012-the-perfect-menu

type TupleN<N extends number, Acc extends any[] = []> =
  Acc["length"] extends N ? Acc : TupleN<N, [...Acc, 0]>;

declare namespace Parsing {
  type Num<S> =
    S extends `${infer N extends number}` ? N : never;

  type CommaSep<S extends string> =
    S extends `${infer A},${infer Rest}` ?
      [A, ...CommaSep<Rest>]
    : S extends "" ? []
    : [S];

  type ParseLines<S> =
    S extends `${infer X}\n${infer XS}` ?
      [X, ...ParseLines<XS>]
    : [];

  type ParseLine<Line extends string> =
    CommaSep<Line> extends (
      [...infer Dishes extends string[], infer N]
    ) ?
      [
        Dishes,
        [
          ...TupleN<Num<N>>,
          ...TupleN<Num<N>>,
          ...TupleN<Num<N>>,
        ],
      ]
    : never;

  type ParseDishes<
    Str extends string,
    Lines extends string[] = ParseLines<Str>,
  > = {
    [K in keyof Lines]: CommaSep<Lines[K]> extends (
      [...infer Rest, any]
    ) ?
      Rest
    : never;
  };
}

declare namespace Comb {
  type Tup = 0[];

  type PossibleValue = 1 | 2 | 3 | 4 | 5;

  type Triple = [Tup, Tup, Tup];

  type ChooseA<N extends Tup, A extends Tup> =
    A["length"] extends PossibleValue ?
      [...ChooseB<N, A, [0]>, ...ChooseA<N, [...A, 0]>]
    : [];

  type ChooseB<
    N extends Tup,
    A extends Tup,
    B extends Tup,
  > =
    B["length"] extends PossibleValue ?
      ChooseB<N, A, [...B, 0]> extends (
        infer Rest extends any[]
      ) ?
        N extends [...A, ...B, ...infer C extends Tup] ?
          C["length"] extends PossibleValue ?
            [
              [A["length"], B["length"], C["length"]],
              ...Rest,
            ]
          : Rest
        : Rest
      : never
    : [];

  export type Splits3<N extends Tup> = ChooseA<N, [0]>;
}

type Clean<X> = {
  [K in keyof X]: X[K];
} & {};

declare namespace Dict {
  type MakeDict<
    Dishes extends string[],
    Values extends any[],
  > = Clean<
    {
      [K in Dishes[0]]: Values[0];
    } & {
      [K in Dishes[1]]: Values[1];
    } & {
      [K in Dishes[2]]: Values[2];
    }
  >;

  type DictCombinations<
    Dishes extends string[],
    Values extends any[],
    Combinations extends any[] = Comb.Splits3<Values>,
  > = {
    [K in keyof Combinations]: MakeDict<
      Dishes,
      Combinations[K]
    >;
  };
}

type Search<Lists extends any[][], Choice = unknown> =
  Lists["length"] extends 0 ? Choice
  : Lists extends (
    [
      infer First extends any[],
      ...infer Rest extends any[][],
    ]
  ) ?
    TryEach<First, Rest, Choice>
  : never;

type TryEach<
  List extends any[],
  RestLists extends any[][],
  Choice extends any,
> =
  List["length"] extends 0 ? never
  : List extends (
    [infer X extends object, ...infer Xs extends object[]]
  ) ?
    Search<RestLists, Choice & X> extends infer R ?
      [R] extends [never] ?
        TryEach<Xs, RestLists, Choice>
      : R
    : never
  : never;

type Solution<
  Input,
  Lines extends any[] = Parsing.ParseLines<Input>,
  ReviewLines extends any[] = {
    [K in keyof Lines]: Parsing.ParseLine<
      Lines[K]
    > extends (
      [
        infer Dishes extends any[],
        infer Value extends any[],
      ]
    ) ?
      Dict.DictCombinations<Dishes, Value>
    : never;
  },
> = Clean<Search<ReviewLines>>;

type Res = Solution<`Gazpaco,Ragu,Tiramisu,3
Gazpaco,Trippa alla Romana,Panettone,1
Gazpaco,Ragu,Panettone,2
Caprese,Trippa alla Romana,Panettone,2
`>;