ice-web-components 1.3.0 → 1.4.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.
@@ -113,3 +113,7 @@ export * from './model/ICE2048Model';
113
113
  export * from './model/ICEHistoryModel';
114
114
  export * from './model/ICEPixelModel';
115
115
  export * from './model/ICEBiosModel';
116
+ export * from './model/ICETracePlayerModel';
117
+ export * from './model/ICESortModel';
118
+ export * from './model/ICEMazeModel';
119
+ export * from './model/ICEDosModel';
@@ -0,0 +1,84 @@
1
+ /**
2
+ * DOS 终端(虚拟文件系统 + 命令解释器,纯逻辑,不碰 DOM)。
3
+ *
4
+ * 页面只做三件事:把 `run()` 返回的行画成文字、把键盘输入交给模型、按 `effect` 做副作用
5
+ * (`cls` 清屏 / `exit` 退出)。所以「命令怎么解析、路径怎么算、文件怎么改」全都能在 node 里断言。
6
+ *
7
+ * 约定:
8
+ * - 路径分隔符 `\` 与 `/` 等价;支持绝对(`\GAMES`)、相对(`GAMES`)、`.`、`..`;
9
+ * - 命令大小写不敏感(`DIR` = `dir`),参数里的文件名也大小写不敏感;
10
+ * - `run()` 从不抛:任何坏输入都变成一行 `error`,终端不会因为打错字崩掉;
11
+ * - `echo x > a.txt` / `echo x >> a.txt` 也是模型的一部分(重定向在解析层处理掉);
12
+ * - 历史和 Tab 补全归模型管(真终端也有这两个),页面只管按键。
13
+ */
14
+ export declare type ICEDosLineType = 'output' | 'error';
15
+ export interface ICEDosLine {
16
+ text: string;
17
+ type: ICEDosLineType;
18
+ }
19
+ export interface ICEDosEffect {
20
+ clear?: boolean;
21
+ exit?: boolean;
22
+ }
23
+ export interface ICEDosResult {
24
+ lines: ICEDosLine[];
25
+ effect?: ICEDosEffect;
26
+ }
27
+ export interface ICEDosOptions {
28
+ /** 时间源(`date` / `time` 用),测试可注入固定时间 */
29
+ now?: () => Date;
30
+ }
31
+ export declare class ICEDosModel {
32
+ private root;
33
+ private cwd;
34
+ private history;
35
+ private historyIndex;
36
+ private now;
37
+ constructor(options?: ICEDosOptions);
38
+ getCwd(): string;
39
+ getPrompt(): string;
40
+ getBanner(): string[];
41
+ getHistory(): string[];
42
+ /** 执行一行命令。永不抛:坏输入变成一行 error。 */
43
+ run(input: string): ICEDosResult;
44
+ historyPrev(): string;
45
+ historyNext(): string;
46
+ /**
47
+ * Tab 补全:命令名补到唯一前缀(带空格),路径按当前目录补(目录名不带空格,方便继续往下打)。
48
+ * 没有匹配 / 多个匹配时原样返回(真 DOS 会再按一次列出候选,这里从简)。
49
+ */
50
+ complete(input: string): string;
51
+ private __dir;
52
+ private __cd;
53
+ private __mkdir;
54
+ private __rmdir;
55
+ private __type;
56
+ private __echo;
57
+ private __copy;
58
+ private __rename;
59
+ private __del;
60
+ private __tree;
61
+ private __current;
62
+ private __resolve;
63
+ private __resolveParent;
64
+ private __parentOf;
65
+ /** 目标节点在树里的路径(从根目录起算,不含 C:)。 */
66
+ private __pathOf;
67
+ /** 相对路径 → 分段(处理 `.` / `..` / 绝对路径)。 */
68
+ private __segments;
69
+ private __baseName;
70
+ private __removeChild;
71
+ private __completePath;
72
+ private __sortedChildren;
73
+ private __fileSize;
74
+ private __fileRow;
75
+ private __nodeDate;
76
+ private __volumeLabel;
77
+ private __formatDate;
78
+ private __formatTime;
79
+ private __lines;
80
+ private __error;
81
+ /** 出厂文件系统:一个像样的小 DOS 盘(命令都能玩起来)。 */
82
+ private __createSeed;
83
+ }
84
+ export default ICEDosModel;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * 迷宫寻路轨迹(纯逻辑,不碰 canvas)。
3
+ *
4
+ * 和排序那边同一套路:**先算完整段过程录成一串帧**,回放交给 `ICETracePlayerModel`,
5
+ * 画面由页面用一张 ICETileMap 画(每格一个状态)。
6
+ *
7
+ * 网格是「行优先的一维数组」,每格一个状态(空 / 墙 / 起点 / 终点 / 已访问 / 边界 / 路径)。
8
+ * 四种算法在无权网格上每步代价都是 1,区别只在**怎么挑下一个格子**:
9
+ * - BFS:队列 → 层序扩展,首次到达即最短;
10
+ * - DFS:栈 → 一条道走到黑,找得到但不保证最短;
11
+ * - Dijkstra:按代价取最小(无权网格里等价于 BFS,保留它是为了「代价」这条语义);
12
+ * - A*:代价 + 曼哈顿距离启发 → 同样最短,但访问的格子更少。
13
+ */
14
+ export declare const ICE_MAZE_CELL: {
15
+ readonly EMPTY: 0;
16
+ readonly WALL: 1;
17
+ readonly START: 2;
18
+ readonly GOAL: 3;
19
+ readonly VISITED: 4;
20
+ readonly FRONTIER: 5;
21
+ readonly PATH: 6;
22
+ };
23
+ export interface ICEMazeAlgorithm {
24
+ key: string;
25
+ label: string;
26
+ note: string;
27
+ }
28
+ export interface ICEMazeFrame {
29
+ /** 本帧的网格快照(可视化直接照着画) */
30
+ cells: number[];
31
+ /** 当前正在处理的格子(-1 表示没有) */
32
+ current: number;
33
+ /** 待探索的边界 */
34
+ frontier: number[];
35
+ /** 已经处理过的格子 */
36
+ visited: number[];
37
+ /** 找到路径之后才有(按起点 → 终点排序) */
38
+ path: number[];
39
+ reached: boolean;
40
+ note: string;
41
+ /** 访问过多少格(代价指标) */
42
+ visitedCount: number;
43
+ }
44
+ export interface ICEMazeOptions {
45
+ rows?: number;
46
+ cols?: number;
47
+ random?: () => number;
48
+ }
49
+ export declare const ICE_MAZE_ALGORITHMS: ICEMazeAlgorithm[];
50
+ export declare class ICEMazeModel {
51
+ private rows;
52
+ private cols;
53
+ private random;
54
+ private walls;
55
+ private start;
56
+ private goal;
57
+ constructor(options?: ICEMazeOptions);
58
+ getRows(): number;
59
+ getCols(): number;
60
+ getStart(): [number, number];
61
+ getGoal(): [number, number];
62
+ getAlgorithms(): ICEMazeAlgorithm[];
63
+ /** 可视化用的网格状态(起点/终点/墙)。 */
64
+ getCells(): number[];
65
+ getCell(row: number, col: number): number;
66
+ isWall(row: number, col: number): boolean;
67
+ setWall(row: number, col: number, wall: boolean): boolean;
68
+ toggleWall(row: number, col: number): boolean;
69
+ clearWalls(): void;
70
+ /** 按密度随机撒墙(起点终点永远不盖)。 */
71
+ randomWalls(density?: number): void;
72
+ setStart(row: number, col: number): boolean;
73
+ setGoal(row: number, col: number): boolean;
74
+ solve(algorithm: string): ICEMazeFrame[];
75
+ private __inside;
76
+ private __index;
77
+ }
78
+ export default ICEMazeModel;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * 排序轨迹(纯逻辑,不碰 canvas)。
3
+ *
4
+ * 它只做一件事:**把排序算法跑一遍,把过程录成一串帧**。回放交给 `ICETracePlayerModel`,
5
+ * 画柱子交给页面(`examples/algorithm-sandbox.html` 里用一张 ICETileMap 画)。
6
+ *
7
+ * 一帧里带四样东西,正好对应可视化要画的四样:
8
+ * - `values`:当前数组(柱子高度);
9
+ * - `compare`:正在比较的下标(黄色);
10
+ * - `swap`:刚刚交换 / 写入的下标(红色);
11
+ * - `sortedFrom`:从这里往后已经就位(绿色)。
12
+ * 再带上 `comparisons` / `swaps` 两个计数 —— 页面上当「代价」展示,也是算法的客观指标。
13
+ *
14
+ * 为什么「先录轨迹、再回放」而不是「一边算一边画」:
15
+ * 1. 算法本身能被单测(最终升序、每帧只是重排、比较次数上界);
16
+ * 2. 回放可以随便暂停、单步、倒带、变速,算法不用知道「现在第几帧」;
17
+ * 3. 同一段轨迹可以换任意渲染方式(柱子 / 数字 / 音效),互不影响。
18
+ */
19
+ export interface ICESortAlgorithm {
20
+ key: string;
21
+ label: string;
22
+ complexity: string;
23
+ }
24
+ export interface ICESortFrame {
25
+ values: number[];
26
+ compare: number[];
27
+ swap: number[];
28
+ /** 从这里往后的元素已经就位;排完是 0 */
29
+ sortedFrom: number;
30
+ note: string;
31
+ comparisons: number;
32
+ swaps: number;
33
+ }
34
+ export interface ICESortOptions {
35
+ /** 元素个数,默认 24 */
36
+ size?: number;
37
+ /** 最大值(柱子最高多少),默认 32 */
38
+ max?: number;
39
+ /** 随机源,测试可注入 */
40
+ random?: () => number;
41
+ }
42
+ export declare const ICE_SORT_ALGORITHMS: ICESortAlgorithm[];
43
+ export declare class ICESortModel {
44
+ private size;
45
+ private max;
46
+ private random;
47
+ private values;
48
+ constructor(options?: ICESortOptions);
49
+ getSize(): number;
50
+ getMax(): number;
51
+ getArray(): number[];
52
+ getAlgorithms(): ICESortAlgorithm[];
53
+ /** 直接给一组数据(会取整、夹到 1..max)。 */
54
+ setArray(values: number[]): void;
55
+ /** 换一批新数据(1..max 的随机数)。 */
56
+ randomize(): void;
57
+ /** 洗牌:把**当前这批数字**重新打乱(同一批数据换个顺序,方便对比不同算法)。 */
58
+ shuffle(): void;
59
+ /** 跑一遍算法,返回整段轨迹。 */
60
+ run(algorithm: string): ICESortFrame[];
61
+ private __normalize;
62
+ }
63
+ export default ICESortModel;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * 「轨迹播放器」:把一串预先算好的帧按时间回放(纯逻辑,不碰 canvas)。
3
+ *
4
+ * 算法可视化(排序、寻路、正则匹配、Diff…)都是同一个套路:**先把整段过程算成一串帧,
5
+ * 再按时间回放**。播放 / 暂停 / 单步 / 调速 / 进度 / 到头停住这套逻辑跟具体算法无关,
6
+ * 所以单独抽出来 —— 算法只负责产帧(`ICESortModel` / `ICEMazeModel`),播放器只管「第几帧、要不要走」。
7
+ *
8
+ * 约定:
9
+ * - `load(frames)` 从头开始(游标 0、暂停);帧是只读的,播放器不复制、不改写;
10
+ * - `tick(dt)` 只在播放态推进,按 `stepsPerSecond` 换算(一帧 = 1000 / speed 毫秒):
11
+ * 攒够几帧就走几帧(页面每帧调一次,所以正常就是每帧一帧;卡顿后不会「欠着时间」);
12
+ * - 走到最后一帧自动停(`isFinished()`),此时再 `play()` 会从头重放;
13
+ * - 速度夹在 1..60 步/秒,非法值忽略(NaN / 0 / 负数都当没调用)。
14
+ */
15
+ export declare type ICETraceListener = (model: ICETracePlayerModel<any>) => void;
16
+ export interface ICETracePlayerOptions {
17
+ /** 初始速度(步/秒),默认 6 */
18
+ speed?: number;
19
+ }
20
+ export declare class ICETracePlayerModel<T> {
21
+ private frames;
22
+ private index;
23
+ private playing;
24
+ private speed;
25
+ private accumulator;
26
+ private listeners;
27
+ constructor(options?: ICETracePlayerOptions);
28
+ getFrameCount(): number;
29
+ getIndex(): number;
30
+ getFrame(): T | null;
31
+ getFrames(): T[];
32
+ isPlaying(): boolean;
33
+ getSpeed(): number;
34
+ getProgress(): number;
35
+ /** 已经走到(或停在)最后一帧。 */
36
+ isFinished(): boolean;
37
+ load(frames: T[]): void;
38
+ play(): void;
39
+ pause(): void;
40
+ togglePlay(): void;
41
+ stepForward(): boolean;
42
+ stepBackward(): boolean;
43
+ seek(index: number): boolean;
44
+ reset(): void;
45
+ setSpeed(stepsPerSecond: number): void;
46
+ /** 播放中按真实时间推进(页面每帧调用)。 */
47
+ tick(delta: number): boolean;
48
+ addChangeListener(listener: ICETraceListener): () => void;
49
+ /** 夹到 1..60;非法值返回 null(调用方据此忽略)。 */
50
+ private __clampSpeed;
51
+ private notify;
52
+ }
53
+ export default ICETracePlayerModel;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ice-web-components",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Swing-style Canvas UI components built on ice-render.",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -23,6 +23,8 @@
23
23
  "qa:xp": "node scripts/qa-xp.mjs",
24
24
  "qa:arcade": "node scripts/qa-arcade.mjs",
25
25
  "qa:pixel": "node scripts/qa-pixel.mjs",
26
+ "qa:algo": "node scripts/qa-algo.mjs",
27
+ "qa:dos": "node scripts/qa-dos.mjs",
26
28
  "docs:api": "node scripts/gen-docs.mjs",
27
29
  "docs:check": "node scripts/check-docs.mjs",
28
30
  "docs": "npm run docs:api && npm run docs:check",