sa2kit 1.6.91 → 1.6.96

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.
Files changed (117) hide show
  1. package/dist/client-BlkUL2To.d.ts +26 -0
  2. package/dist/client-DpMIhrlS.d.mts +26 -0
  3. package/dist/huarongdao/index.d.mts +8 -0
  4. package/dist/huarongdao/index.d.ts +8 -0
  5. package/dist/huarongdao/index.js +360 -0
  6. package/dist/huarongdao/index.js.map +1 -0
  7. package/dist/huarongdao/index.mjs +338 -0
  8. package/dist/huarongdao/index.mjs.map +1 -0
  9. package/dist/huarongdao/logic/index.d.mts +11 -0
  10. package/dist/huarongdao/logic/index.d.ts +11 -0
  11. package/dist/huarongdao/logic/index.js +89 -0
  12. package/dist/huarongdao/logic/index.js.map +1 -0
  13. package/dist/huarongdao/logic/index.mjs +81 -0
  14. package/dist/huarongdao/logic/index.mjs.map +1 -0
  15. package/dist/huarongdao/routes/index.d.mts +38 -0
  16. package/dist/huarongdao/routes/index.d.ts +38 -0
  17. package/dist/huarongdao/routes/index.js +114 -0
  18. package/dist/huarongdao/routes/index.js.map +1 -0
  19. package/dist/huarongdao/routes/index.mjs +108 -0
  20. package/dist/huarongdao/routes/index.mjs.map +1 -0
  21. package/dist/huarongdao/server/index.d.mts +14 -0
  22. package/dist/huarongdao/server/index.d.ts +14 -0
  23. package/dist/huarongdao/server/index.js +60 -0
  24. package/dist/huarongdao/server/index.js.map +1 -0
  25. package/dist/huarongdao/server/index.mjs +57 -0
  26. package/dist/huarongdao/server/index.mjs.map +1 -0
  27. package/dist/huarongdao/service/index.d.mts +31 -0
  28. package/dist/huarongdao/service/index.d.ts +31 -0
  29. package/dist/huarongdao/service/index.js +45 -0
  30. package/dist/huarongdao/service/index.js.map +1 -0
  31. package/dist/huarongdao/service/index.mjs +42 -0
  32. package/dist/huarongdao/service/index.mjs.map +1 -0
  33. package/dist/huarongdao/types/index.d.mts +46 -0
  34. package/dist/huarongdao/types/index.d.ts +46 -0
  35. package/dist/huarongdao/types/index.js +4 -0
  36. package/dist/huarongdao/types/index.js.map +1 -0
  37. package/dist/huarongdao/types/index.mjs +3 -0
  38. package/dist/huarongdao/types/index.mjs.map +1 -0
  39. package/dist/huarongdao/ui/web/index.d.mts +3 -0
  40. package/dist/huarongdao/ui/web/index.d.ts +3 -0
  41. package/dist/huarongdao/ui/web/index.js +237 -0
  42. package/dist/huarongdao/ui/web/index.js.map +1 -0
  43. package/dist/huarongdao/ui/web/index.mjs +229 -0
  44. package/dist/huarongdao/ui/web/index.mjs.map +1 -0
  45. package/dist/index-B48rcsqv.d.ts +27 -0
  46. package/dist/index-BNqJdwX4.d.ts +37 -0
  47. package/dist/index-C7yh6b5Q.d.mts +17 -0
  48. package/dist/index-CDapUIT5.d.mts +51 -0
  49. package/dist/index-Cv9jlnNz.d.ts +17 -0
  50. package/dist/index-D3UbkUai.d.ts +51 -0
  51. package/dist/index-DOtQI_mz.d.mts +37 -0
  52. package/dist/index-Da2X78GE.d.mts +27 -0
  53. package/dist/index.d.mts +18 -0
  54. package/dist/index.d.ts +18 -0
  55. package/dist/index.js +1707 -79
  56. package/dist/index.js.map +1 -1
  57. package/dist/index.mjs +1675 -82
  58. package/dist/index.mjs.map +1 -1
  59. package/dist/mikuContest/index.d.mts +13 -0
  60. package/dist/mikuContest/index.d.ts +13 -0
  61. package/dist/mikuContest/index.js +1310 -0
  62. package/dist/mikuContest/index.js.map +1 -0
  63. package/dist/mikuContest/index.mjs +1253 -0
  64. package/dist/mikuContest/index.mjs.map +1 -0
  65. package/dist/mikuContest/logic/index.d.mts +32 -0
  66. package/dist/mikuContest/logic/index.d.ts +32 -0
  67. package/dist/mikuContest/logic/index.js +511 -0
  68. package/dist/mikuContest/logic/index.js.map +1 -0
  69. package/dist/mikuContest/logic/index.mjs +483 -0
  70. package/dist/mikuContest/logic/index.mjs.map +1 -0
  71. package/dist/mikuContest/routes/index.d.mts +80 -0
  72. package/dist/mikuContest/routes/index.d.ts +80 -0
  73. package/dist/mikuContest/routes/index.js +821 -0
  74. package/dist/mikuContest/routes/index.js.map +1 -0
  75. package/dist/mikuContest/routes/index.mjs +791 -0
  76. package/dist/mikuContest/routes/index.mjs.map +1 -0
  77. package/dist/mikuContest/server/index.d.mts +766 -0
  78. package/dist/mikuContest/server/index.d.ts +766 -0
  79. package/dist/mikuContest/server/index.js +705 -0
  80. package/dist/mikuContest/server/index.js.map +1 -0
  81. package/dist/mikuContest/server/index.mjs +672 -0
  82. package/dist/mikuContest/server/index.mjs.map +1 -0
  83. package/dist/mikuContest/service/index.d.mts +30 -0
  84. package/dist/mikuContest/service/index.d.ts +30 -0
  85. package/dist/mikuContest/service/index.js +139 -0
  86. package/dist/mikuContest/service/index.js.map +1 -0
  87. package/dist/mikuContest/service/index.mjs +135 -0
  88. package/dist/mikuContest/service/index.mjs.map +1 -0
  89. package/dist/mikuContest/types/index.d.mts +179 -0
  90. package/dist/mikuContest/types/index.d.ts +179 -0
  91. package/dist/mikuContest/types/index.js +4 -0
  92. package/dist/mikuContest/types/index.js.map +1 -0
  93. package/dist/mikuContest/types/index.mjs +3 -0
  94. package/dist/mikuContest/types/index.mjs.map +1 -0
  95. package/dist/mikuContest/ui/miniapp/index.d.mts +3 -0
  96. package/dist/mikuContest/ui/miniapp/index.d.ts +3 -0
  97. package/dist/mikuContest/ui/miniapp/index.js +566 -0
  98. package/dist/mikuContest/ui/miniapp/index.js.map +1 -0
  99. package/dist/mikuContest/ui/miniapp/index.mjs +540 -0
  100. package/dist/mikuContest/ui/miniapp/index.mjs.map +1 -0
  101. package/dist/mikuContest/ui/web/index.d.mts +4 -0
  102. package/dist/mikuContest/ui/web/index.d.ts +4 -0
  103. package/dist/mikuContest/ui/web/index.js +353 -0
  104. package/dist/mikuContest/ui/web/index.js.map +1 -0
  105. package/dist/mikuContest/ui/web/index.mjs +343 -0
  106. package/dist/mikuContest/ui/web/index.mjs.map +1 -0
  107. package/dist/qqbot/server/index.d.mts +126 -1
  108. package/dist/qqbot/server/index.d.ts +126 -1
  109. package/dist/qqbot/server/index.js +250 -0
  110. package/dist/qqbot/server/index.js.map +1 -1
  111. package/dist/qqbot/server/index.mjs +246 -1
  112. package/dist/qqbot/server/index.mjs.map +1 -1
  113. package/dist/service-D7DM1wW-.d.ts +38 -0
  114. package/dist/service-DPr2rlvH.d.mts +38 -0
  115. package/dist/types-BS7Xz09b.d.mts +14 -0
  116. package/dist/types-k4koMp4m.d.ts +14 -0
  117. package/package.json +76 -1
@@ -0,0 +1,42 @@
1
+ // src/huarongdao/service/api/client.ts
2
+ var unwrap = (result) => {
3
+ if (!result.success || result.data === void 0) throw new Error(result.error || "\u8BF7\u6C42\u5931\u8D25");
4
+ return result.data;
5
+ };
6
+ var createHuarongdaoApiClient = (basePath, requester2) => ({
7
+ async getSnapshot() {
8
+ return unwrap(await requester2(`${basePath}/snapshot`, { method: "GET" }));
9
+ },
10
+ async listConfigs() {
11
+ return unwrap(await requester2(`${basePath}/configs`, { method: "GET" }));
12
+ },
13
+ async createConfig(input) {
14
+ return unwrap(await requester2(`${basePath}/configs`, { method: "POST", body: input }));
15
+ },
16
+ async updateConfig(id, patch) {
17
+ return unwrap(await requester2(`${basePath}/configs`, { method: "PATCH", body: { id, patch } }));
18
+ },
19
+ async deleteConfig(id) {
20
+ return unwrap(await requester2(`${basePath}/configs`, { method: "DELETE", body: { id } }));
21
+ }
22
+ });
23
+
24
+ // src/huarongdao/service/web/client.ts
25
+ var requester = (options) => {
26
+ const baseUrl = options.baseUrl || "";
27
+ return async (url, req) => {
28
+ const res = await fetch(`${baseUrl}${url}`, {
29
+ method: req?.method || "GET",
30
+ headers: { "Content-Type": "application/json", ...options.headers || {} },
31
+ body: req?.body ? JSON.stringify(req.body) : void 0
32
+ });
33
+ return await res.json();
34
+ };
35
+ };
36
+ var createHuarongdaoWebClient = (options = {}) => {
37
+ return createHuarongdaoApiClient(options.basePath || "/api/huarongdao", requester(options));
38
+ };
39
+
40
+ export { createHuarongdaoApiClient, createHuarongdaoWebClient };
41
+ //# sourceMappingURL=index.mjs.map
42
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/huarongdao/service/api/client.ts","../../../src/huarongdao/service/web/client.ts"],"names":["requester"],"mappings":";AAaA,IAAM,MAAA,GAAS,CAAI,MAAA,KAA8B;AAC/C,EAAA,IAAI,CAAC,MAAA,CAAO,OAAA,IAAW,MAAA,CAAO,IAAA,KAAS,MAAA,EAAW,MAAM,IAAI,KAAA,CAAM,MAAA,CAAO,KAAA,IAAS,0BAAM,CAAA;AACxF,EAAA,OAAO,MAAA,CAAO,IAAA;AAChB,CAAA;AAEO,IAAM,yBAAA,GAA4B,CAAC,QAAA,EAAkBA,UAAAA,MAA0B;AAAA,EACpF,MAAM,WAAA,GAAgD;AACpD,IAAA,OAAO,MAAA,CAAO,MAAMA,UAAAA,CAAgD,CAAA,EAAG,QAAQ,aAAa,EAAE,MAAA,EAAQ,KAAA,EAAO,CAAC,CAAA;AAAA,EAChH,CAAA;AAAA,EACA,MAAM,WAAA,GAA2C;AAC/C,IAAA,OAAO,MAAA,CAAO,MAAMA,UAAAA,CAA2C,CAAA,EAAG,QAAQ,YAAY,EAAE,MAAA,EAAQ,KAAA,EAAO,CAAC,CAAA;AAAA,EAC1G,CAAA;AAAA,EACA,MAAM,aAAa,KAAA,EAA+D;AAChF,IAAA,OAAO,MAAA,CAAO,MAAMA,UAAAA,CAAyC,CAAA,EAAG,QAAQ,CAAA,QAAA,CAAA,EAAY,EAAE,MAAA,EAAQ,MAAA,EAAQ,IAAA,EAAM,KAAA,EAAO,CAAC,CAAA;AAAA,EACtH,CAAA;AAAA,EACA,MAAM,YAAA,CAAa,EAAA,EAAY,KAAA,EAA6D;AAC1F,IAAA,OAAO,MAAA,CAAO,MAAMA,UAAAA,CAAyC,CAAA,EAAG,QAAQ,CAAA,QAAA,CAAA,EAAY,EAAE,MAAA,EAAQ,OAAA,EAAS,MAAM,EAAE,EAAA,EAAI,KAAA,EAAM,EAAG,CAAC,CAAA;AAAA,EAC/H,CAAA;AAAA,EACA,MAAM,aAAa,EAAA,EAA8B;AAC/C,IAAA,OAAO,MAAA,CAAO,MAAMA,UAAAA,CAAgC,CAAA,EAAG,QAAQ,CAAA,QAAA,CAAA,EAAY,EAAE,MAAA,EAAQ,QAAA,EAAU,IAAA,EAAM,EAAE,EAAA,EAAG,EAAG,CAAC,CAAA;AAAA,EAChH;AACF,CAAA;;;AC1BA,IAAM,SAAA,GAAY,CAAC,OAAA,KAAmD;AACpE,EAAA,MAAM,OAAA,GAAU,QAAQ,OAAA,IAAW,EAAA;AACnC,EAAA,OAAO,OAAU,KAAa,GAAA,KAAuF;AACnH,IAAA,MAAM,MAAM,MAAM,KAAA,CAAM,GAAG,OAAO,CAAA,EAAG,GAAG,CAAA,CAAA,EAAI;AAAA,MAC1C,MAAA,EAAQ,KAAK,MAAA,IAAU,KAAA;AAAA,MACvB,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAoB,GAAI,OAAA,CAAQ,OAAA,IAAW,EAAC,EAAG;AAAA,MAC1E,MAAM,GAAA,EAAK,IAAA,GAAO,KAAK,SAAA,CAAU,GAAA,CAAI,IAAI,CAAA,GAAI;AAAA,KAC9C,CAAA;AACD,IAAA,OAAQ,MAAM,IAAI,IAAA,EAAK;AAAA,EACzB,CAAA;AACF,CAAA;AAEO,IAAM,yBAAA,GAA4B,CAAC,OAAA,GAAsC,EAAC,KAAM;AACrF,EAAA,OAAO,0BAA0B,OAAA,CAAQ,QAAA,IAAY,iBAAA,EAAmB,SAAA,CAAU,OAAO,CAAC,CAAA;AAC5F","file":"index.mjs","sourcesContent":["import type { CreateHuarongdaoConfigInput, HuarongdaoConfig, HuarongdaoStateSnapshot } from '../../types';\n\nexport type HttpMethod = 'GET' | 'POST' | 'PATCH' | 'DELETE';\nexport interface Requester {\n <T>(url: string, options?: { method?: HttpMethod; body?: unknown }): Promise<T>;\n}\n\ninterface ApiEnvelope<T> {\n success: boolean;\n data?: T;\n error?: string;\n}\n\nconst unwrap = <T>(result: ApiEnvelope<T>): T => {\n if (!result.success || result.data === undefined) throw new Error(result.error || '请求失败');\n return result.data;\n};\n\nexport const createHuarongdaoApiClient = (basePath: string, requester: Requester) => ({\n async getSnapshot(): Promise<HuarongdaoStateSnapshot> {\n return unwrap(await requester<ApiEnvelope<HuarongdaoStateSnapshot>>(`${basePath}/snapshot`, { method: 'GET' }));\n },\n async listConfigs(): Promise<HuarongdaoConfig[]> {\n return unwrap(await requester<ApiEnvelope<HuarongdaoConfig[]>>(`${basePath}/configs`, { method: 'GET' }));\n },\n async createConfig(input: CreateHuarongdaoConfigInput): Promise<HuarongdaoConfig> {\n return unwrap(await requester<ApiEnvelope<HuarongdaoConfig>>(`${basePath}/configs`, { method: 'POST', body: input }));\n },\n async updateConfig(id: string, patch: Partial<HuarongdaoConfig>): Promise<HuarongdaoConfig> {\n return unwrap(await requester<ApiEnvelope<HuarongdaoConfig>>(`${basePath}/configs`, { method: 'PATCH', body: { id, patch } }));\n },\n async deleteConfig(id: string): Promise<boolean> {\n return unwrap(await requester<ApiEnvelope<boolean>>(`${basePath}/configs`, { method: 'DELETE', body: { id } }));\n },\n});\n","import { createHuarongdaoApiClient, type Requester } from '../api';\n\nexport interface HuarongdaoWebClientOptions {\n baseUrl?: string;\n basePath?: string;\n headers?: Record<string, string>;\n}\n\nconst requester = (options: HuarongdaoWebClientOptions): Requester => {\n const baseUrl = options.baseUrl || '';\n return async <T>(url: string, req?: { method?: 'GET' | 'POST' | 'PATCH' | 'DELETE'; body?: unknown }): Promise<T> => {\n const res = await fetch(`${baseUrl}${url}`, {\n method: req?.method || 'GET',\n headers: { 'Content-Type': 'application/json', ...(options.headers || {}) },\n body: req?.body ? JSON.stringify(req.body) : undefined,\n });\n return (await res.json()) as T;\n };\n};\n\nexport const createHuarongdaoWebClient = (options: HuarongdaoWebClientOptions = {}) => {\n return createHuarongdaoApiClient(options.basePath || '/api/huarongdao', requester(options));\n};\n"]}
@@ -0,0 +1,46 @@
1
+ type PuzzleStatus = 'draft' | 'active' | 'archived';
2
+ interface HuarongdaoConfig {
3
+ id: string;
4
+ slug: string;
5
+ name: string;
6
+ description?: string;
7
+ status: PuzzleStatus;
8
+ rows: number;
9
+ cols: number;
10
+ sourceImageUrl: string;
11
+ showReference: boolean;
12
+ shuffleSteps: number;
13
+ timeLimitSec?: number;
14
+ startMode: 'random-solvable' | 'custom-layout';
15
+ initialTiles?: number[];
16
+ createdAt: string;
17
+ updatedAt: string;
18
+ }
19
+ interface HuarongdaoStateSnapshot {
20
+ configs: HuarongdaoConfig[];
21
+ activeConfig?: HuarongdaoConfig;
22
+ }
23
+ interface CreateHuarongdaoConfigInput {
24
+ slug: string;
25
+ name: string;
26
+ description?: string;
27
+ rows: number;
28
+ cols: number;
29
+ sourceImageUrl: string;
30
+ showReference?: boolean;
31
+ shuffleSteps?: number;
32
+ timeLimitSec?: number;
33
+ startMode?: 'random-solvable' | 'custom-layout';
34
+ initialTiles?: number[];
35
+ }
36
+ interface HuarongdaoGameState {
37
+ tiles: number[];
38
+ rows: number;
39
+ cols: number;
40
+ moveCount: number;
41
+ startedAt: number;
42
+ finishedAt?: number;
43
+ isSolved: boolean;
44
+ }
45
+
46
+ export type { CreateHuarongdaoConfigInput, HuarongdaoConfig, HuarongdaoGameState, HuarongdaoStateSnapshot, PuzzleStatus };
@@ -0,0 +1,46 @@
1
+ type PuzzleStatus = 'draft' | 'active' | 'archived';
2
+ interface HuarongdaoConfig {
3
+ id: string;
4
+ slug: string;
5
+ name: string;
6
+ description?: string;
7
+ status: PuzzleStatus;
8
+ rows: number;
9
+ cols: number;
10
+ sourceImageUrl: string;
11
+ showReference: boolean;
12
+ shuffleSteps: number;
13
+ timeLimitSec?: number;
14
+ startMode: 'random-solvable' | 'custom-layout';
15
+ initialTiles?: number[];
16
+ createdAt: string;
17
+ updatedAt: string;
18
+ }
19
+ interface HuarongdaoStateSnapshot {
20
+ configs: HuarongdaoConfig[];
21
+ activeConfig?: HuarongdaoConfig;
22
+ }
23
+ interface CreateHuarongdaoConfigInput {
24
+ slug: string;
25
+ name: string;
26
+ description?: string;
27
+ rows: number;
28
+ cols: number;
29
+ sourceImageUrl: string;
30
+ showReference?: boolean;
31
+ shuffleSteps?: number;
32
+ timeLimitSec?: number;
33
+ startMode?: 'random-solvable' | 'custom-layout';
34
+ initialTiles?: number[];
35
+ }
36
+ interface HuarongdaoGameState {
37
+ tiles: number[];
38
+ rows: number;
39
+ cols: number;
40
+ moveCount: number;
41
+ startedAt: number;
42
+ finishedAt?: number;
43
+ isSolved: boolean;
44
+ }
45
+
46
+ export type { CreateHuarongdaoConfigInput, HuarongdaoConfig, HuarongdaoGameState, HuarongdaoStateSnapshot, PuzzleStatus };
@@ -0,0 +1,4 @@
1
+ 'use strict';
2
+
3
+ //# sourceMappingURL=index.js.map
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=index.mjs.map
3
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.mjs"}
@@ -0,0 +1,3 @@
1
+ export { H as HuarongdaoBoard, b as HuarongdaoConfigPage, a as HuarongdaoGamePage } from '../../../index-Da2X78GE.mjs';
2
+ import 'react';
3
+ import '../../types/index.mjs';
@@ -0,0 +1,3 @@
1
+ export { H as HuarongdaoBoard, b as HuarongdaoConfigPage, a as HuarongdaoGamePage } from '../../../index-B48rcsqv.js';
2
+ import 'react';
3
+ import '../../types/index.js';
@@ -0,0 +1,237 @@
1
+ 'use strict';
2
+
3
+ var React3 = require('react');
4
+
5
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
6
+
7
+ var React3__default = /*#__PURE__*/_interopDefault(React3);
8
+
9
+ // src/huarongdao/ui/web/components/HuarongdaoBoard.tsx
10
+ var HuarongdaoBoard = ({ tiles, rows, cols, imageUrl, onClickTile }) => {
11
+ const total = rows * cols;
12
+ return /* @__PURE__ */ React3__default.default.createElement(
13
+ "div",
14
+ {
15
+ style: {
16
+ display: "grid",
17
+ gridTemplateColumns: `repeat(${cols}, 96px)`,
18
+ gridTemplateRows: `repeat(${rows}, 96px)`,
19
+ gap: 6
20
+ }
21
+ },
22
+ tiles.map((tile, index) => {
23
+ if (tile === 0) return /* @__PURE__ */ React3__default.default.createElement("div", { key: `blank-${index}`, style: { background: "#ddd" } });
24
+ const pieceIndex = tile - 1;
25
+ const pr = Math.floor(pieceIndex / cols);
26
+ const pc = pieceIndex % cols;
27
+ return /* @__PURE__ */ React3__default.default.createElement(
28
+ "button",
29
+ {
30
+ key: `${tile}-${index}`,
31
+ onClick: () => onClickTile(index),
32
+ style: {
33
+ border: "1px solid #666",
34
+ cursor: "pointer",
35
+ backgroundImage: `url(${imageUrl})`,
36
+ backgroundSize: `${cols * 96}px ${rows * 96}px`,
37
+ backgroundPosition: `${-pc * 96}px ${-pr * 96}px`
38
+ }
39
+ }
40
+ );
41
+ }),
42
+ tiles.length !== total ? /* @__PURE__ */ React3__default.default.createElement("div", null, "tiles invalid") : null
43
+ );
44
+ };
45
+ var HuarongdaoBoard_default = HuarongdaoBoard;
46
+
47
+ // src/huarongdao/logic/game.ts
48
+ var clone = (v) => [...v];
49
+ var buildSolvedTiles = (rows, cols) => {
50
+ const total = rows * cols;
51
+ return Array.from({ length: total }, (_, i) => (i + 1) % total);
52
+ };
53
+ var isSolved = (tiles) => {
54
+ return tiles.every((v, i, arr) => v === (i + 1) % arr.length);
55
+ };
56
+ var canMove = (tiles, rows, cols, tileIndex) => {
57
+ const blank = tiles.indexOf(0);
58
+ if (blank < 0 || tileIndex < 0 || tileIndex >= tiles.length) return false;
59
+ const br = Math.floor(blank / cols);
60
+ const bc = blank % cols;
61
+ const tr = Math.floor(tileIndex / cols);
62
+ const tc = tileIndex % cols;
63
+ return Math.abs(br - tr) + Math.abs(bc - tc) === 1;
64
+ };
65
+ var moveTile = (state, tileIndex) => {
66
+ if (!canMove(state.tiles, state.rows, state.cols, tileIndex)) return state;
67
+ const nextTiles = clone(state.tiles);
68
+ const blank = nextTiles.indexOf(0);
69
+ const blankValue = nextTiles[blank];
70
+ const tileValue = nextTiles[tileIndex];
71
+ if (blankValue === void 0 || tileValue === void 0) return state;
72
+ nextTiles[blank] = tileValue;
73
+ nextTiles[tileIndex] = blankValue;
74
+ const solved = isSolved(nextTiles);
75
+ return {
76
+ ...state,
77
+ tiles: nextTiles,
78
+ moveCount: state.moveCount + 1,
79
+ isSolved: solved,
80
+ finishedAt: solved ? Date.now() : void 0
81
+ };
82
+ };
83
+ var shuffleSolvable = (rows, cols, steps = 80) => {
84
+ let tiles = buildSolvedTiles(rows, cols);
85
+ for (let i = 0; i < steps; i += 1) {
86
+ const blank = tiles.indexOf(0);
87
+ const br = Math.floor(blank / cols);
88
+ const bc = blank % cols;
89
+ const candidate = [];
90
+ const dirs = [
91
+ [1, 0],
92
+ [-1, 0],
93
+ [0, 1],
94
+ [0, -1]
95
+ ];
96
+ dirs.forEach(([dr, dc]) => {
97
+ const nr = br + dr;
98
+ const nc = bc + dc;
99
+ if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) candidate.push(nr * cols + nc);
100
+ });
101
+ const idx = candidate[Math.floor(Math.random() * candidate.length)] ?? blank;
102
+ const next = clone(tiles);
103
+ [next[blank], next[idx]] = [next[idx] ?? 0, next[blank] ?? 0];
104
+ tiles = next;
105
+ }
106
+ return tiles;
107
+ };
108
+
109
+ // src/huarongdao/ui/web/pages/HuarongdaoGamePage.tsx
110
+ var HuarongdaoGamePage = ({ config }) => {
111
+ const initial = React3.useMemo(() => {
112
+ const tiles = config.startMode === "custom-layout" && config.initialTiles?.length === config.rows * config.cols ? config.initialTiles : shuffleSolvable(config.rows, config.cols, config.shuffleSteps);
113
+ return {
114
+ tiles,
115
+ rows: config.rows,
116
+ cols: config.cols,
117
+ moveCount: 0,
118
+ startedAt: Date.now(),
119
+ isSolved: false
120
+ };
121
+ }, [config]);
122
+ const [state, setState] = React3.useState(initial);
123
+ const reset = () => {
124
+ setState({
125
+ ...state,
126
+ tiles: shuffleSolvable(config.rows, config.cols, config.shuffleSteps),
127
+ moveCount: 0,
128
+ startedAt: Date.now(),
129
+ finishedAt: void 0,
130
+ isSolved: false
131
+ });
132
+ };
133
+ const solveNow = () => {
134
+ setState((prev) => ({ ...prev, tiles: buildSolvedTiles(config.rows, config.cols), isSolved: true, finishedAt: Date.now() }));
135
+ };
136
+ const durationSec = Math.floor(((state.finishedAt || Date.now()) - state.startedAt) / 1e3);
137
+ return /* @__PURE__ */ React3__default.default.createElement("section", null, /* @__PURE__ */ React3__default.default.createElement("h2", null, config.name), /* @__PURE__ */ React3__default.default.createElement("p", null, "\u6B65\u6570\uFF1A", state.moveCount, " \uFF5C \u7528\u65F6\uFF1A", durationSec, "s ", state.isSolved ? "\uFF5C\u5DF2\u901A\u5173 \u{1F389}" : ""), /* @__PURE__ */ React3__default.default.createElement(
138
+ HuarongdaoBoard_default,
139
+ {
140
+ tiles: state.tiles,
141
+ rows: state.rows,
142
+ cols: state.cols,
143
+ imageUrl: config.sourceImageUrl,
144
+ onClickTile: (idx) => setState((prev) => moveTile(prev, idx))
145
+ }
146
+ ), /* @__PURE__ */ React3__default.default.createElement("div", { style: { marginTop: 12 } }, /* @__PURE__ */ React3__default.default.createElement("button", { onClick: reset }, "\u91CD\u65B0\u6253\u4E71"), /* @__PURE__ */ React3__default.default.createElement("button", { onClick: solveNow }, "\u4E00\u952E\u5B8C\u6210(\u6D4B\u8BD5)")), config.showReference ? /* @__PURE__ */ React3__default.default.createElement("div", { style: { marginTop: 12 } }, /* @__PURE__ */ React3__default.default.createElement("div", null, "\u53C2\u8003\u56FE\uFF1A"), /* @__PURE__ */ React3__default.default.createElement("img", { src: config.sourceImageUrl, alt: "reference", style: { width: 180, border: "1px solid #ccc" } })) : null);
147
+ };
148
+ var HuarongdaoGamePage_default = HuarongdaoGamePage;
149
+
150
+ // src/huarongdao/server/service.ts
151
+ var id = () => `puzzle_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
152
+ var HuarongdaoService = class {
153
+ constructor() {
154
+ this.configs = /* @__PURE__ */ new Map();
155
+ }
156
+ listConfigs() {
157
+ return [...this.configs.values()];
158
+ }
159
+ getBySlug(slug) {
160
+ return [...this.configs.values()].find((c) => c.slug === slug) || null;
161
+ }
162
+ createConfig(input) {
163
+ const now = (/* @__PURE__ */ new Date()).toISOString();
164
+ const next = {
165
+ id: id(),
166
+ slug: input.slug,
167
+ name: input.name,
168
+ description: input.description,
169
+ status: "draft",
170
+ rows: input.rows,
171
+ cols: input.cols,
172
+ sourceImageUrl: input.sourceImageUrl,
173
+ showReference: input.showReference ?? true,
174
+ shuffleSteps: input.shuffleSteps ?? 80,
175
+ timeLimitSec: input.timeLimitSec,
176
+ startMode: input.startMode ?? "random-solvable",
177
+ initialTiles: input.initialTiles,
178
+ createdAt: now,
179
+ updatedAt: now
180
+ };
181
+ this.configs.set(next.id, next);
182
+ return next;
183
+ }
184
+ updateConfig(id2, patch) {
185
+ const cur = this.configs.get(id2);
186
+ if (!cur) throw new Error("\u914D\u7F6E\u4E0D\u5B58\u5728");
187
+ const next = { ...cur, ...patch, id: cur.id, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
188
+ this.configs.set(id2, next);
189
+ return next;
190
+ }
191
+ deleteConfig(id2) {
192
+ this.configs.delete(id2);
193
+ }
194
+ getSnapshot() {
195
+ const configs = this.listConfigs();
196
+ return {
197
+ configs,
198
+ activeConfig: configs.find((c) => c.status === "active")
199
+ };
200
+ }
201
+ };
202
+ var createHuarongdaoService = () => new HuarongdaoService();
203
+
204
+ // src/huarongdao/ui/web/pages/HuarongdaoConfigPage.tsx
205
+ var HuarongdaoConfigPage = () => {
206
+ const service = React3.useMemo(() => createHuarongdaoService(), []);
207
+ const [version, setVersion] = React3.useState(0);
208
+ const [name, setName] = React3.useState("\u793A\u4F8B\u534E\u5BB9\u9053");
209
+ const [slug, setSlug] = React3.useState("sample-huarongdao");
210
+ const [imageUrl, setImageUrl] = React3.useState("https://i.imgur.com/6z7Qw6M.png");
211
+ const [rows, setRows] = React3.useState(3);
212
+ const [cols, setCols] = React3.useState(3);
213
+ const list = React3.useMemo(() => {
214
+ return service.listConfigs();
215
+ }, [service, version]);
216
+ const create = () => {
217
+ const input = {
218
+ name,
219
+ slug,
220
+ sourceImageUrl: imageUrl,
221
+ rows,
222
+ cols,
223
+ showReference: true,
224
+ shuffleSteps: 80
225
+ };
226
+ service.createConfig(input);
227
+ setVersion((v) => v + 1);
228
+ };
229
+ return /* @__PURE__ */ React3__default.default.createElement("section", null, /* @__PURE__ */ React3__default.default.createElement("h2", null, "\u534E\u5BB9\u9053\u540E\u53F0\u914D\u7F6E\u9875"), /* @__PURE__ */ React3__default.default.createElement("p", null, "\u652F\u6301\u591A\u5957\u914D\u7F6E\u521B\u5EFA\u3001\u53C2\u6570\u5316\u7BA1\u7406\uFF08V1 \u9AA8\u67B6\uFF09"), /* @__PURE__ */ React3__default.default.createElement("div", null, /* @__PURE__ */ React3__default.default.createElement("input", { value: name, onChange: (e) => setName(e.target.value), placeholder: "name" }), /* @__PURE__ */ React3__default.default.createElement("input", { value: slug, onChange: (e) => setSlug(e.target.value), placeholder: "slug" }), /* @__PURE__ */ React3__default.default.createElement("input", { value: imageUrl, onChange: (e) => setImageUrl(e.target.value), placeholder: "image url" }), /* @__PURE__ */ React3__default.default.createElement("input", { type: "number", value: rows, onChange: (e) => setRows(Number(e.target.value) || 3) }), /* @__PURE__ */ React3__default.default.createElement("input", { type: "number", value: cols, onChange: (e) => setCols(Number(e.target.value) || 3) }), /* @__PURE__ */ React3__default.default.createElement("button", { onClick: create }, "\u521B\u5EFA\u914D\u7F6E")), /* @__PURE__ */ React3__default.default.createElement("ul", null, list.map((item) => /* @__PURE__ */ React3__default.default.createElement("li", { key: item.id }, item.name, " (", item.slug, ") - ", item.rows, "x", item.cols, " - ", item.status))));
230
+ };
231
+ var HuarongdaoConfigPage_default = HuarongdaoConfigPage;
232
+
233
+ exports.HuarongdaoBoard = HuarongdaoBoard_default;
234
+ exports.HuarongdaoConfigPage = HuarongdaoConfigPage_default;
235
+ exports.HuarongdaoGamePage = HuarongdaoGamePage_default;
236
+ //# sourceMappingURL=index.js.map
237
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/huarongdao/ui/web/components/HuarongdaoBoard.tsx","../../../../src/huarongdao/logic/game.ts","../../../../src/huarongdao/ui/web/pages/HuarongdaoGamePage.tsx","../../../../src/huarongdao/server/service.ts","../../../../src/huarongdao/ui/web/pages/HuarongdaoConfigPage.tsx"],"names":["React","useMemo","useState","id"],"mappings":"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","file":"index.js","sourcesContent":["import React from 'react';\n\nexport interface HuarongdaoBoardProps {\n tiles: number[];\n rows: number;\n cols: number;\n imageUrl: string;\n onClickTile: (index: number) => void;\n}\n\nconst HuarongdaoBoard: React.FC<HuarongdaoBoardProps> = ({ tiles, rows, cols, imageUrl, onClickTile }) => {\n const total = rows * cols;\n return (\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: `repeat(${cols}, 96px)`,\n gridTemplateRows: `repeat(${rows}, 96px)`,\n gap: 6,\n }}\n >\n {tiles.map((tile, index) => {\n if (tile === 0) return <div key={`blank-${index}`} style={{ background: '#ddd' }} />;\n const pieceIndex = tile - 1;\n const pr = Math.floor(pieceIndex / cols);\n const pc = pieceIndex % cols;\n return (\n <button\n key={`${tile}-${index}`}\n onClick={() => onClickTile(index)}\n style={{\n border: '1px solid #666',\n cursor: 'pointer',\n backgroundImage: `url(${imageUrl})`,\n backgroundSize: `${cols * 96}px ${rows * 96}px`,\n backgroundPosition: `${-pc * 96}px ${-pr * 96}px`,\n }}\n />\n );\n })}\n {tiles.length !== total ? <div>tiles invalid</div> : null}\n </div>\n );\n};\n\nexport default HuarongdaoBoard;\n","import type { HuarongdaoGameState } from '../types';\n\nconst clone = <T>(v: T[]): T[] => [...v];\n\nexport const buildSolvedTiles = (rows: number, cols: number): number[] => {\n const total = rows * cols;\n return Array.from({ length: total }, (_, i) => (i + 1) % total);\n};\n\nexport const isSolved = (tiles: number[]): boolean => {\n return tiles.every((v, i, arr) => v === (i + 1) % arr.length);\n};\n\nexport const canMove = (tiles: number[], rows: number, cols: number, tileIndex: number): boolean => {\n const blank = tiles.indexOf(0);\n if (blank < 0 || tileIndex < 0 || tileIndex >= tiles.length) return false;\n const br = Math.floor(blank / cols);\n const bc = blank % cols;\n const tr = Math.floor(tileIndex / cols);\n const tc = tileIndex % cols;\n return Math.abs(br - tr) + Math.abs(bc - tc) === 1;\n};\n\nexport const moveTile = (state: HuarongdaoGameState, tileIndex: number): HuarongdaoGameState => {\n if (!canMove(state.tiles, state.rows, state.cols, tileIndex)) return state;\n const nextTiles = clone(state.tiles);\n const blank = nextTiles.indexOf(0);\n const blankValue = nextTiles[blank];\n const tileValue = nextTiles[tileIndex];\n if (blankValue === undefined || tileValue === undefined) return state;\n nextTiles[blank] = tileValue;\n nextTiles[tileIndex] = blankValue;\n const solved = isSolved(nextTiles);\n return {\n ...state,\n tiles: nextTiles,\n moveCount: state.moveCount + 1,\n isSolved: solved,\n finishedAt: solved ? Date.now() : undefined,\n };\n};\n\nexport const inversionCount = (tiles: number[]): number => {\n const arr = tiles.filter((n) => n !== 0);\n let cnt = 0;\n for (let i = 0; i < arr.length; i += 1) {\n for (let j = i + 1; j < arr.length; j += 1) {\n if ((arr[i] ?? 0) > (arr[j] ?? 0)) cnt += 1;\n }\n }\n return cnt;\n};\n\nexport const isSolvable = (tiles: number[], rows: number, cols: number): boolean => {\n const inv = inversionCount(tiles);\n if (cols % 2 === 1) return inv % 2 === 0;\n const blankRowFromBottom = rows - Math.floor(tiles.indexOf(0) / cols);\n return (blankRowFromBottom % 2 === 0) !== (inv % 2 === 0);\n};\n\nexport const shuffleSolvable = (rows: number, cols: number, steps = 80): number[] => {\n let tiles = buildSolvedTiles(rows, cols);\n for (let i = 0; i < steps; i += 1) {\n const blank = tiles.indexOf(0);\n const br = Math.floor(blank / cols);\n const bc = blank % cols;\n const candidate: number[] = [];\n const dirs: Array<[number, number]> = [\n [1, 0],\n [-1, 0],\n [0, 1],\n [0, -1],\n ];\n dirs.forEach(([dr, dc]) => {\n const nr = br + dr;\n const nc = bc + dc;\n if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) candidate.push(nr * cols + nc);\n });\n const idx = candidate[Math.floor(Math.random() * candidate.length)] ?? blank;\n const next = clone(tiles);\n [next[blank], next[idx]] = [next[idx] ?? 0, next[blank] ?? 0];\n tiles = next;\n }\n return tiles;\n};\n","import React, { useMemo, useState } from 'react';\nimport { buildSolvedTiles, moveTile, shuffleSolvable } from '../../../logic';\nimport type { HuarongdaoConfig, HuarongdaoGameState } from '../../../types';\nimport HuarongdaoBoard from '../components/HuarongdaoBoard';\n\nexport interface HuarongdaoGamePageProps {\n config: HuarongdaoConfig;\n}\n\nconst HuarongdaoGamePage: React.FC<HuarongdaoGamePageProps> = ({ config }) => {\n const initial = useMemo<HuarongdaoGameState>(() => {\n const tiles = config.startMode === 'custom-layout' && config.initialTiles?.length === config.rows * config.cols\n ? config.initialTiles\n : shuffleSolvable(config.rows, config.cols, config.shuffleSteps);\n return {\n tiles,\n rows: config.rows,\n cols: config.cols,\n moveCount: 0,\n startedAt: Date.now(),\n isSolved: false,\n };\n }, [config]);\n\n const [state, setState] = useState(initial);\n\n const reset = () => {\n setState({\n ...state,\n tiles: shuffleSolvable(config.rows, config.cols, config.shuffleSteps),\n moveCount: 0,\n startedAt: Date.now(),\n finishedAt: undefined,\n isSolved: false,\n });\n };\n\n const solveNow = () => {\n setState((prev) => ({ ...prev, tiles: buildSolvedTiles(config.rows, config.cols), isSolved: true, finishedAt: Date.now() }));\n };\n\n const durationSec = Math.floor(((state.finishedAt || Date.now()) - state.startedAt) / 1000);\n\n return (\n <section>\n <h2>{config.name}</h2>\n <p>步数:{state.moveCount} | 用时:{durationSec}s {state.isSolved ? '|已通关 🎉' : ''}</p>\n <HuarongdaoBoard\n tiles={state.tiles}\n rows={state.rows}\n cols={state.cols}\n imageUrl={config.sourceImageUrl}\n onClickTile={(idx) => setState((prev) => moveTile(prev, idx))}\n />\n <div style={{ marginTop: 12 }}>\n <button onClick={reset}>重新打乱</button>\n <button onClick={solveNow}>一键完成(测试)</button>\n </div>\n {config.showReference ? (\n <div style={{ marginTop: 12 }}>\n <div>参考图:</div>\n <img src={config.sourceImageUrl} alt=\"reference\" style={{ width: 180, border: '1px solid #ccc' }} />\n </div>\n ) : null}\n </section>\n );\n};\n\nexport default HuarongdaoGamePage;\n","import type { CreateHuarongdaoConfigInput, HuarongdaoConfig, HuarongdaoStateSnapshot } from '../types';\n\nconst id = () => `puzzle_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;\n\nexport class HuarongdaoService {\n private readonly configs = new Map<string, HuarongdaoConfig>();\n\n listConfigs(): HuarongdaoConfig[] {\n return [...this.configs.values()];\n }\n\n getBySlug(slug: string): HuarongdaoConfig | null {\n return [...this.configs.values()].find((c) => c.slug === slug) || null;\n }\n\n createConfig(input: CreateHuarongdaoConfigInput): HuarongdaoConfig {\n const now = new Date().toISOString();\n const next: HuarongdaoConfig = {\n id: id(),\n slug: input.slug,\n name: input.name,\n description: input.description,\n status: 'draft',\n rows: input.rows,\n cols: input.cols,\n sourceImageUrl: input.sourceImageUrl,\n showReference: input.showReference ?? true,\n shuffleSteps: input.shuffleSteps ?? 80,\n timeLimitSec: input.timeLimitSec,\n startMode: input.startMode ?? 'random-solvable',\n initialTiles: input.initialTiles,\n createdAt: now,\n updatedAt: now,\n };\n this.configs.set(next.id, next);\n return next;\n }\n\n updateConfig(id: string, patch: Partial<HuarongdaoConfig>): HuarongdaoConfig {\n const cur = this.configs.get(id);\n if (!cur) throw new Error('配置不存在');\n const next: HuarongdaoConfig = { ...cur, ...patch, id: cur.id, updatedAt: new Date().toISOString() };\n this.configs.set(id, next);\n return next;\n }\n\n deleteConfig(id: string): void {\n this.configs.delete(id);\n }\n\n getSnapshot(): HuarongdaoStateSnapshot {\n const configs = this.listConfigs();\n return {\n configs,\n activeConfig: configs.find((c) => c.status === 'active'),\n };\n }\n}\n\nexport const createHuarongdaoService = () => new HuarongdaoService();\n","import React, { useMemo, useState } from 'react';\nimport { createHuarongdaoService } from '../../../server';\nimport type { CreateHuarongdaoConfigInput } from '../../../types';\n\nconst HuarongdaoConfigPage: React.FC = () => {\n const service = useMemo(() => createHuarongdaoService(), []);\n const [version, setVersion] = useState(0);\n const [name, setName] = useState('示例华容道');\n const [slug, setSlug] = useState('sample-huarongdao');\n const [imageUrl, setImageUrl] = useState('https://i.imgur.com/6z7Qw6M.png');\n const [rows, setRows] = useState(3);\n const [cols, setCols] = useState(3);\n\n const list = useMemo(() => {\n void version;\n return service.listConfigs();\n }, [service, version]);\n\n const create = () => {\n const input: CreateHuarongdaoConfigInput = {\n name,\n slug,\n sourceImageUrl: imageUrl,\n rows,\n cols,\n showReference: true,\n shuffleSteps: 80,\n };\n service.createConfig(input);\n setVersion((v) => v + 1);\n };\n\n return (\n <section>\n <h2>华容道后台配置页</h2>\n <p>支持多套配置创建、参数化管理(V1 骨架)</p>\n <div>\n <input value={name} onChange={(e) => setName(e.target.value)} placeholder=\"name\" />\n <input value={slug} onChange={(e) => setSlug(e.target.value)} placeholder=\"slug\" />\n <input value={imageUrl} onChange={(e) => setImageUrl(e.target.value)} placeholder=\"image url\" />\n <input type=\"number\" value={rows} onChange={(e) => setRows(Number(e.target.value) || 3)} />\n <input type=\"number\" value={cols} onChange={(e) => setCols(Number(e.target.value) || 3)} />\n <button onClick={create}>创建配置</button>\n </div>\n <ul>\n {list.map((item) => (\n <li key={item.id}>{item.name} ({item.slug}) - {item.rows}x{item.cols} - {item.status}</li>\n ))}\n </ul>\n </section>\n );\n};\n\nexport default HuarongdaoConfigPage;\n"]}