@my-games/sudoku-core 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,20 +1,62 @@
1
1
  # @my-games/sudoku-core
2
2
 
3
- > 数独引擎核心,纯 TypeScript、无框架依赖。**当前为 `0.0.0` 占位版本**,
4
- > 仅导出占位常量,用于种下 npm 首版并配置 Trusted Publisher。
3
+ > 数独引擎核心,纯 TypeScript、无框架依赖。产出 ESM / CJS / UMD / IIFE 四格式;
4
+ > IIFE/UMD 全局名 `SudokuCore`。样式:`@my-games/sudoku-core/style.css`。
5
+
6
+ **如果你要用 Vue 或 React,直接装对应适配器包更省事**(见 `@my-games/sudoku-vue` / `-react`)。
5
7
 
6
8
  ```bash
7
9
  pnpm add @my-games/sudoku-core
8
10
  ```
9
11
 
12
+ ## 快速开始
13
+
10
14
  ```ts
11
- import { SUDOKU_PACKAGE, SUDOKU_STAGE } from '@my-games/sudoku-core';
15
+ import { Sudoku } from '@my-games/sudoku-core';
16
+
17
+ const game = new Sudoku({ difficulty: 'easy' });
18
+ const unsub = game.subscribe((state) => {
19
+ // state: difficulty / grid / givenMask / notes / selected / conflicts /
20
+ // status / elapsedSeconds / bestSeconds / hintCount / mistakeCount /
21
+ // moveCount / historySize
22
+ console.log(state.status, state.elapsedSeconds);
23
+ });
24
+
25
+ game.select(0, 2);
26
+ game.input(4); // 在选中空格填 4(给定格/已通关会被拒)
27
+ game.toggleNote(7); // 空格铅笔笔记
28
+ game.hint(); // 用解填一格
29
+ game.undo();
30
+ unsub();
12
31
  ```
13
32
 
14
- 真实引擎(`Sudoku` 类 + 求解器 + 生成器 + 存档)将在占位包发布后作为后续提交补入。
15
- 产出 ESM / CJS / UMD / IIFE 四格式;IIFE/UMD 全局名 `SudokuCore`。样式:`@my-games/sudoku-core/style.css`。
33
+ ## 引擎 API
34
+
35
+ - `new Sudoku(options?)` — 全可注入:`difficulty` / `rng` / `clock` / `storage` / `saveKey` /
36
+ `bestKey` / `freshStart` / `puzzle` / `solution` / `undoLimit`。默认 `difficulty:'easy'` 现场生成唯一解题。
37
+ - `game.subscribe(fn): () => void` — 注册即收一次快照,返回退订函数。
38
+ - `game.getState()` — `GameState` 深拷贝快照;`getGrid()` / `getPuzzle()` / `getSolution()`。
39
+ - 操作:`select(row, col)` / `input(digit)` / `erase()` / `toggleNote(digit)` / `hint()` /
40
+ `newGame(difficulty?)` / `undo()` / `canUndo()` / `clearSaved()`。
41
+ - 求解/生成:`solve(grid)` / `countSolutions(grid, limit=2)` / `generatePuzzle(difficulty, rng)` /
42
+ `generateFullGrid(rng)` / `TARGET_CLUES`。
43
+ - 纯函数原语:`createEmptyGrid` / `cloneGrid` / `cellAt` / `setCell` / `withCell` / `peers` /
44
+ `isSafe` / `candidates` / `emptyCells` / `isFull` / `findConflicts` / `hasConflict` /
45
+ `isSolved` / `gridsEqual` / `countFilled` / `boxOrigin` / `makeMask` + 常量 `SIZE` / `BOX` / `DIGITS`。
46
+ - 存储:`loadSave` / `writeSave` / `clearSave` / `isSerializedSave` / `loadBest` / `writeBest` /
47
+ `emptyBestTimes` / `tryGetGlobalStorage` + 键常量。
48
+
49
+ ## 规则要点
50
+
51
+ - 9×9,3×3 宫;题面给定格不可改写。
52
+ - `input` 填的值若与同行/列/宫重复 → 计入 `mistakeCount` 且该格进 `conflicts`,但仍允许填(不强制拦下)。
53
+ - 全部 81 格填满且无冲突 → `status:'won'`,按难度写入 `bestSeconds`(更快才覆盖)。
54
+ - 计时用注入的 `clock`(默认 `Date.now`):**首次填充(`input`/`hint`)才起表**,仅选中/记笔记不计时;
55
+ `elapsedSeconds` 在 `won` 时定格。
56
+ - 存档(含题面、解、当前盘、笔记、计数、用时)落 `saveKey`;`best`(分难度)落 `bestKey`;
57
+ 同难度同题面才续档,损坏 JSON / 版本不符自动开新局。
16
58
 
17
- 完整说明见仓库根 README:<https://github.com/my-gam/sudoku#readme>
59
+ 完整玩法、Props / Events / ref API、设计取舍见仓库根 README:<https://github.com/my-gam/sudoku#readme>
18
60
 
19
61
  ## License
20
62
 
@@ -0,0 +1,63 @@
1
+ import { Cell, ConflictMask, Digit, Grid } from './types';
2
+ /**
3
+ * 纯函数棋盘工具:只用 `Grid`(`Cell[][]`,0 = 空),不涉及对局可变态。
4
+ *
5
+ * 设计原则:所有「产出新棋盘」的函数都返回新数组,不原地修改输入。
6
+ * solver / generator / Sudoku 类都建立在这套底层原语上。
7
+ *
8
+ * 严格模式(noUncheckedIndexedAccess):读取一律走 `cellAt`(越界/空返回 0),
9
+ * 派生 9×9 结构时用 `makeMask` / `setAt` 保证行存在后再写下标。
10
+ */
11
+ /** 数独边长(标准 9×9)。 */
12
+ export declare const SIZE: 9;
13
+ /** 宫的边长(3×3 宫,共 9 宫)。 */
14
+ export declare const BOX: 3;
15
+ /** 数字 1..9 常量表(升序)。 */
16
+ export declare const DIGITS: readonly Digit[];
17
+ /** 生成 SIZE×SIZE 全 0 空棋盘。 */
18
+ export declare function createEmptyGrid(): Grid;
19
+ /** 深拷贝棋盘。 */
20
+ export declare function cloneGrid(grid: Grid): Grid;
21
+ /** 生成 SIZE×SIZE 掩码,初值由 `fill` 决定。 */
22
+ export declare function makeMask(fill: boolean): ConflictMask;
23
+ /** 安全读取:越界返回 0。 */
24
+ export declare function cellAt(grid: Grid, row: number, col: number): Cell;
25
+ /** 安全写入:仅在行存在时写入(返回同一引用,供链式)。 */
26
+ export declare function setCell(grid: Grid, row: number, col: number, value: Cell): Grid;
27
+ /** 返回把 (row,col) 设为 value 后的新棋盘(不改动入参)。 */
28
+ export declare function withCell(grid: Grid, row: number, col: number, value: Cell): Grid;
29
+ /** (row,col) 所属宫的左上角坐标。 */
30
+ export declare function boxOrigin(row: number, col: number): {
31
+ r: number;
32
+ c: number;
33
+ };
34
+ /** 与 (row,col) 同处一行、一列、一宫(不含自身)的所有坐标,去重后共 20 个。 */
35
+ export declare function peers(row: number, col: number): Array<[number, number]>;
36
+ /**
37
+ * 数字 digit 能否放到 (row,col):检查所有 peer 是否已占用同值。
38
+ * 不关心 (row,col) 当前值(用于「换个值是否合法」判断)。
39
+ */
40
+ export declare function isSafe(grid: Grid, row: number, col: number, digit: Digit): boolean;
41
+ /** 空格 (row,col) 的所有合法候选数字;非空格返回 []。 */
42
+ export declare function candidates(grid: Grid, row: number, col: number): Digit[];
43
+ /** 所有空格坐标(行优先)。 */
44
+ export declare function emptyCells(grid: Grid): Array<[number, number]>;
45
+ /** 是否填满(无 0)。 */
46
+ export declare function isFull(grid: Grid): boolean;
47
+ /**
48
+ * 计算冲突掩码:某格为非 0 且与任一 peer 同值 → true。
49
+ * 供 UI 标红「当前矛盾」的格,允许存在空格(部分填写)。
50
+ */
51
+ export declare function findConflicts(grid: Grid): ConflictMask;
52
+ /** 是否存在任意冲突(有重复)。 */
53
+ export declare function hasConflict(grid: Grid): boolean;
54
+ /**
55
+ * 是否为「合法完成盘」:填满且无冲突。
56
+ * 等价于每行/列/宫都是 1..9 的一个排列。
57
+ */
58
+ export declare function isSolved(grid: Grid): boolean;
59
+ /** 棋盘相等(逐格比较)。 */
60
+ export declare function gridsEqual(a: Grid, b: Grid): boolean;
61
+ /** 数非空格数量(题面线索数)。 */
62
+ export declare function countFilled(grid: Grid): number;
63
+ //# sourceMappingURL=board.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"board.d.ts","sourceRoot":"","sources":["../src/board.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/D;;;;;;;;GAQG;AAEH,oBAAoB;AACpB,eAAO,MAAM,IAAI,EAAG,CAAU,CAAC;AAC/B,yBAAyB;AACzB,eAAO,MAAM,GAAG,EAAG,CAAU,CAAC;AAE9B,uBAAuB;AACvB,eAAO,MAAM,MAAM,EAAE,SAAS,KAAK,EAAgC,CAAC;AAEpE,4BAA4B;AAC5B,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAED,aAAa;AACb,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAE1C;AAED,qCAAqC;AACrC,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,CAEpD;AAED,mBAAmB;AACnB,wBAAgB,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAEjE;AAED,iCAAiC;AACjC,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,IAAI,CAI/E;AAED,2CAA2C;AAC3C,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,IAAI,CAGhF;AAED,2BAA2B;AAC3B,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAE5E;AAED,mDAAmD;AACnD,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAqBvE;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAKlF;AAED,uCAAuC;AACvC,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,KAAK,EAAE,CAOxE;AAED,mBAAmB;AACnB,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAQ9D;AAED,iBAAiB;AACjB,wBAAgB,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAO1C;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,YAAY,CAgBtD;AAED,qBAAqB;AACrB,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAK/C;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE5C;AAED,kBAAkB;AAClB,wBAAgB,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,GAAG,OAAO,CAOpD;AAED,qBAAqB;AACrB,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAQ9C"}
package/dist/game.d.ts ADDED
@@ -0,0 +1,95 @@
1
+ import { Difficulty, Digit, GameOptions, GameState, Grid } from './types';
2
+ export type { GameOptions } from './types';
3
+ /**
4
+ * Sudoku —— 数独状态机与规则实现,与 UI 完全解耦。
5
+ *
6
+ * - 通过 `subscribe(fn)` 推完整 `GameState` 快照(不推 diff);注册即收到一次。
7
+ * - 每次改变局面的公开操作(input/erase/toggleNote/hint/newGame/undo)成功后
8
+ * 走一次 `commit()`:写存档 + 通知订阅。`select()` 只更新选中并通知,不落盘。
9
+ * - 谜题来源:默认按 `difficulty` 现场生成唯一解题;也可注入 `puzzle`(+可选 solution)
10
+ * 用作固定题/测试。`newGame()` 总是重新随机生成一道。
11
+ * - 计时:注入 `clock`(默认 Date.now);`elapsedMs = baseMs + (clock()-origin)`,
12
+ * 通关瞬间冻结为 `finalMs`。undo 回未到通关的快照会按当时累计值续表。
13
+ * - 存档:注入 storage(默认尝试 globalThis.localStorage);`best` 是分难度最佳用时。
14
+ */
15
+ export declare class Sudoku {
16
+ private readonly rng;
17
+ private readonly clock;
18
+ private readonly storage;
19
+ private readonly saveKey;
20
+ private readonly bestKey;
21
+ private readonly undoLimit;
22
+ private readonly fixedPuzzle;
23
+ private readonly fixedSolution;
24
+ private difficulty;
25
+ private puzzle;
26
+ private solution;
27
+ private givenMask;
28
+ private grid;
29
+ private notes;
30
+ private selected;
31
+ private status;
32
+ private hintCount;
33
+ private mistakeCount;
34
+ private moveCount;
35
+ private history;
36
+ private started;
37
+ private baseMs;
38
+ private origin;
39
+ private finalMs;
40
+ private best;
41
+ private listeners;
42
+ private notifying;
43
+ constructor(opts?: GameOptions);
44
+ /** 订阅状态变化,返回退订函数;注册后立刻收到一次当前快照。 */
45
+ subscribe(fn: (state: GameState) => void): () => void;
46
+ getState(): GameState;
47
+ getGrid(): Grid;
48
+ getPuzzle(): Grid;
49
+ getSolution(): Grid;
50
+ canUndo(): boolean;
51
+ /** 选中某格(越界则忽略)。仅更新选中并通知订阅,不写存档、不入 undo 栈。 */
52
+ select(row: number, col: number): void;
53
+ /**
54
+ * 在选中格填数字。题面给定格 / 已通关时忽略。
55
+ * 返回 true = 棋盘确有变化。填了与盘面冲突的值会计一次 mistake(仍允许填)。
56
+ */
57
+ input(digit: Digit): boolean;
58
+ /** 擦除选中格(值与笔记一起清)。题面给定 / 已通关忽略。返回是否有变化。 */
59
+ erase(): boolean;
60
+ /**
61
+ * 切换选中空格的某条铅笔笔记。仅对「非给定且当前为空」的格生效。
62
+ * 返回 true = 笔记集合确有变化。
63
+ */
64
+ toggleNote(digit: Digit): boolean;
65
+ /**
66
+ * 提示:优先填「选中的空格」,否则填第一个空格;写入解中的正确值并清该格笔记。
67
+ * 返回 true = 确有一格被填。计一次 hint(不单独计 mistake)。
68
+ */
69
+ hint(): boolean;
70
+ /**
71
+ * 开启新局:随机生成指定难度(默认沿用当前难度)的唯一解题,清空局面与历史,
72
+ * 计时归零,best 保留。会清除旧存档并落一份新局存档。
73
+ */
74
+ newGame(difficulty?: Difficulty): void;
75
+ /** 撤销一步;返回是否成功。 */
76
+ undo(): boolean;
77
+ /** 清除当前存档(不影响 best)。 */
78
+ clearSaved(): void;
79
+ /** 由(部分)题面推导完整解:优先用注入的 solution,否则用 solve。 */
80
+ private deriveSolution;
81
+ /** 存档是否可续用:同难度、同题面(防切难度/换题后串档)。 */
82
+ private canReserve;
83
+ private startNewGame;
84
+ /** 首次填充(input/hint)时懒起表;未起表前 elapsedMs 恒 0。 */
85
+ private ensureStarted;
86
+ private elapsedMs;
87
+ private checkWin;
88
+ private pickHintTarget;
89
+ private pushHistory;
90
+ private restoreSnapshot;
91
+ private loadFromSave;
92
+ private commit;
93
+ private notify;
94
+ }
95
+ //# sourceMappingURL=game.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"game.d.ts","sourceRoot":"","sources":["../src/game.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAEV,UAAU,EACV,KAAK,EACL,WAAW,EACX,SAAS,EAGT,IAAI,EAQL,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AA2B3C;;;;;;;;;;;GAWG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAc;IAE5C,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,MAAM,CAAO;IACrB,OAAO,CAAC,QAAQ,CAAO;IAGvB,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,IAAI,CAAQ;IACpB,OAAO,CAAC,KAAK,CAAY;IACzB,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,OAAO,CAAkB;IAGjC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAK;IAEpB,OAAO,CAAC,IAAI,CAAY;IAExB,OAAO,CAAC,SAAS,CAAyC;IAC1D,OAAO,CAAC,SAAS,CAAS;gBAEd,IAAI,GAAE,WAAgB;IA2BlC,mCAAmC;IACnC,SAAS,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,GAAG,MAAM,IAAI;IAarD,QAAQ,IAAI,SAAS;IAkBrB,OAAO,IAAI,IAAI;IAIf,SAAS,IAAI,IAAI;IAIjB,WAAW,IAAI,IAAI;IAInB,OAAO,IAAI,OAAO;IAIlB,6CAA6C;IAC7C,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAOtC;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO;IAmB5B,2CAA2C;IAC3C,KAAK,IAAI,OAAO;IAkBhB;;;OAGG;IACH,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO;IAmBjC;;;OAGG;IACH,IAAI,IAAI,OAAO;IAoBf;;;OAGG;IACH,OAAO,CAAC,UAAU,GAAE,UAA4B,GAAG,IAAI;IAKvD,mBAAmB;IACnB,IAAI,IAAI,OAAO;IAQf,wBAAwB;IACxB,UAAU,IAAI,IAAI;IAMlB,8CAA8C;IAC9C,OAAO,CAAC,cAAc;IAQtB,mCAAmC;IACnC,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,YAAY;IA0BpB,+CAA+C;IAC/C,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,QAAQ;IAqBhB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,YAAY;IA2BpB,OAAO,CAAC,MAAM;IAiBd,OAAO,CAAC,MAAM;CAiBf"}
@@ -0,0 +1,28 @@
1
+ import { Difficulty, Grid, RandomSource } from './types';
2
+ /**
3
+ * 数独生成器:产出「有唯一解」的题目。
4
+ *
5
+ * 流程:
6
+ * 1. `generateFullGrid` —— 随机回溯填满一张合法终盘(作为 solution)。
7
+ * 2. `digHoles` —— 以随机顺序尝试把格子挖空;每挖一格前用 `countSolutions(limit=2)`
8
+ * 校验:挖掉后仍「唯一解」才保留,否则回填。挖到目标线索数或无格可挖为止。
9
+ *
10
+ * 这样产出的 puzzle 保证唯一解,且 puzzle ⊂ solution(挖掉的都是解里可推出来的格)。
11
+ * rng 注入保证单测可复现;默认 `Math.random`。
12
+ */
13
+ /** 各难度目标线索数(题面给定数字个数)。实际可能因唯一性约束略多。 */
14
+ export declare const TARGET_CLUES: Record<Difficulty, number>;
15
+ /** 生成一张随机合法终盘(9×9 全填、无冲突)。 */
16
+ export declare function generateFullGrid(rng?: RandomSource): Grid;
17
+ export interface GeneratedPuzzle {
18
+ /** 题面(含 0 空格),有唯一解。 */
19
+ puzzle: Grid;
20
+ /** 与题面一致的完整解(题面 ⊂ 解)。 */
21
+ solution: Grid;
22
+ difficulty: Difficulty;
23
+ /** 实际线索数(题面给定个数)。 */
24
+ clues: number;
25
+ }
26
+ /** 生成一道指定难度、唯一解的数独题。 */
27
+ export declare function generatePuzzle(difficulty?: Difficulty, rng?: RandomSource): GeneratedPuzzle;
28
+ //# sourceMappingURL=generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE9D;;;;;;;;;;GAUG;AAEH,uCAAuC;AACvC,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAInD,CAAC;AAiCF,8BAA8B;AAC9B,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,YAA0B,GAAG,IAAI,CAKtE;AAgCD,MAAM,WAAW,eAAe;IAC9B,uBAAuB;IACvB,MAAM,EAAE,IAAI,CAAC;IACb,yBAAyB;IACzB,QAAQ,EAAE,IAAI,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAwB;AACxB,wBAAgB,cAAc,CAC5B,UAAU,GAAE,UAAmB,EAC/B,GAAG,GAAE,YAA0B,GAC9B,eAAe,CAMjB"}