dsh-dock 0.9.6 → 0.9.8

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 (35) hide show
  1. package/LICENSE +18 -18
  2. package/README.md +325 -325
  3. package/client.js +42 -8
  4. package/cordis.patch.yml +2 -2
  5. package/features/animation/host.js +660 -660
  6. package/features/animation/view.jsx +2247 -2247
  7. package/features/balance/host.js +494 -494
  8. package/features/balance/view.js +351 -351
  9. package/features/games/games/breakout.jsx +227 -227
  10. package/features/games/games/gomoku.jsx +244 -244
  11. package/features/games/games/racer.jsx +169 -169
  12. package/features/games/games/shared.jsx +118 -118
  13. package/features/games/games/snake.jsx +144 -144
  14. package/features/games/games/sokoban.jsx +180 -180
  15. package/features/games/games/tank.jsx +128 -128
  16. package/features/games/games/tetris.jsx +308 -308
  17. package/features/games/games/xiangqi.jsx +277 -277
  18. package/features/games/tank-core.js +152 -152
  19. package/features/games/view.jsx +639 -639
  20. package/features/games/xiangqi-core.js +299 -299
  21. package/features/heartbeat/view.js +35 -35
  22. package/features/mobile-relay/gateway.js +472 -472
  23. package/features/mobile-relay/host.js +570 -569
  24. package/features/mobile-relay/view.jsx +300 -300
  25. package/features/modelconfig/host.js +403 -403
  26. package/features/modelconfig/view.js +490 -490
  27. package/features/theme/view.js +61 -61
  28. package/features/tokenlog/host.js +690 -678
  29. package/features/tokenlog/view.jsx +561 -561
  30. package/features/visionproxy/host.js +315 -315
  31. package/index.js +175 -118
  32. package/package.json +59 -59
  33. package/src/client.jsx +668 -668
  34. package/src/host-core.js +98 -95
  35. package/src/shared.js +167 -130
@@ -1,152 +1,152 @@
1
- /* 趣味游戏 · 坦克大战核心逻辑(纯函数,无 React)——供组件复用,便于单测。 */
2
- export const GRID = 15;
3
- export const EMPTY = 0, STEEL = 1, BRICK = 2, BASE = 3;
4
- const DIRS = { up: [-1, 0], down: [1, 0], left: [0, -1], right: [0, 1] };
5
- export const DIR_KEYS = Object.keys(DIRS);
6
- const SPAWNS = [{ r: 0, c: 0 }, { r: 0, c: 7 }, { r: 0, c: 14 }];
7
-
8
- function cloneGrid(g) { return g.map((row) => row.slice()); }
9
- function inBounds(r, c) { return r >= 0 && r < GRID && c >= 0 && c < GRID; }
10
- function cellBlockedForTank(grid, r, c) { return !inBounds(r, c) || grid[r][c] !== EMPTY; }
11
-
12
- export function makeGrid() {
13
- const g = Array.from({ length: GRID }, () => Array(GRID).fill(EMPTY));
14
- const set = (r, c, v) => { if (r >= 0 && r < GRID && c >= 0 && c < GRID) g[r][c] = v; };
15
- for (let r = 2; r <= 5; r += 1) { set(r, 3, STEEL); set(r, 11, STEEL); set(r, 7, STEEL); }
16
- for (let c = 4; c <= 10; c += 1) set(8, c, BRICK);
17
- set(9, 5, BRICK); set(9, 6, BRICK); set(9, 8, BRICK); set(9, 9, BRICK); set(9, 10, BRICK);
18
- set(13, 7, BASE);
19
- set(12, 6, BRICK); set(12, 7, BRICK); set(12, 8, BRICK);
20
- set(13, 6, BRICK); set(13, 8, BRICK);
21
- return g;
22
- }
23
- export function findBase(grid) {
24
- for (let r = 0; r < GRID; r += 1) for (let c = 0; c < GRID; c += 1) if (grid[r][c] === BASE) return { r, c };
25
- return { r: 13, c: 7 };
26
- }
27
- function playerSpawn() { return { r: 11, c: 7, dir: "up", alive: true, lives: 3, cooldown: 0 }; }
28
-
29
- export function initialState() {
30
- return {
31
- grid: makeGrid(),
32
- player: playerSpawn(),
33
- enemies: [],
34
- bullets: [],
35
- score: 0,
36
- wave: 1,
37
- remaining: 4,
38
- active: 2,
39
- spawnTick: 0,
40
- over: false, win: false,
41
- respawnTicks: 0,
42
- };
43
- }
44
- function spawnBullet(r, c, dir, from) {
45
- const [dr, dc] = DIRS[dir];
46
- return { r: r + dr, c: c + dc, dr, dc, from };
47
- }
48
- function spawnOne(grid, enemies) {
49
- for (const s of SPAWNS) {
50
- if (grid[s.r][s.c] === EMPTY && !enemies.some((e) => e.r === s.r && e.c === s.c)) {
51
- return { r: s.r, c: s.c, dir: "down", hp: 1, cooldown: 3, moveTick: 0 };
52
- }
53
- }
54
- return null;
55
- }
56
-
57
- export function step(state, input) {
58
- if (state.over || state.win) return state;
59
- const grid = cloneGrid(state.grid);
60
- let player = Object.assign({}, state.player);
61
- let enemies = state.enemies.map((e) => Object.assign({}, e));
62
- let bullets = state.bullets.map((b) => Object.assign({}, b));
63
- let remaining = state.remaining, score = state.score, over = state.over;
64
- let respawnTicks = state.respawnTicks;
65
- let spawnTick = state.spawnTick + 1;
66
-
67
- // 玩家移动
68
- if (player.alive && input.dir && DIRS[input.dir]) {
69
- const [dr, dc] = DIRS[input.dir];
70
- player.dir = input.dir;
71
- const nr = player.r + dr, nc = player.c + dc;
72
- if (!cellBlockedForTank(grid, nr, nc)) { player.r = nr; player.c = nc; }
73
- }
74
- // 玩家射击
75
- if (player.alive && input.shoot && player.cooldown <= 0) {
76
- bullets.push(spawnBullet(player.r, player.c, player.dir, "p"));
77
- player.cooldown = 9;
78
- }
79
- player.cooldown = Math.max(0, player.cooldown - 1);
80
-
81
- // 敌人移动/射击
82
- for (let i = 0; i < enemies.length; i += 1) {
83
- const e = enemies[i];
84
- e.cooldown -= 1; e.moveTick += 1;
85
- if (e.moveTick % 12 === 0) {
86
- const [dr, dc] = DIRS[e.dir];
87
- const nr = e.r + dr, nc = e.c + dc;
88
- if (cellBlockedForTank(grid, nr, nc) || enemies.some((o, oi) => oi !== i && o.r === nr && o.c === nc)) {
89
- const opts = DIR_KEYS.filter((d) => {
90
- const [ddr, ddc] = DIRS[d];
91
- const tr = e.r + ddr, tc = e.c + ddc;
92
- return !cellBlockedForTank(grid, tr, tc) && !enemies.some((o, oi) => oi !== i && o.r === tr && o.c === tc);
93
- });
94
- if (opts.length) e.dir = opts[Math.floor(Math.random() * opts.length)];
95
- } else { e.r = nr; e.c = nc; }
96
- }
97
- if (e.cooldown <= 0) {
98
- bullets.push(spawnBullet(e.r, e.c, e.dir, "e"));
99
- e.cooldown = 9 + Math.floor(Math.random() * 9);
100
- }
101
- }
102
-
103
- // 子弹移动/碰撞
104
- let baseHit = false, playerHit = false;
105
- const nextBullets = [];
106
- const base = findBase(grid);
107
- for (const b of bullets) {
108
- const nr = b.r + b.dr, nc = b.c + b.dc;
109
- if (!inBounds(nr, nc)) continue;
110
- const cell = grid[nr][nc];
111
- if (cell === STEEL) continue;
112
- if (cell === BRICK) { grid[nr][nc] = EMPTY; continue; }
113
- if (cell === BASE) { if (b.from === "e") baseHit = true; continue; }
114
- if (b.from === "p") {
115
- const idx = enemies.findIndex((e) => e.r === nr && e.c === nc);
116
- if (idx >= 0) { enemies.splice(idx, 1); score += 100; continue; }
117
- } else if (player.alive && player.r === nr && player.c === nc) {
118
- player.lives -= 1;
119
- if (player.lives <= 0) over = true;
120
- else { player.alive = false; respawnTicks = 24; }
121
- continue;
122
- }
123
- b.r = nr; b.c = nc;
124
- nextBullets.push(b);
125
- }
126
-
127
- // 生成敌人
128
- if (remaining > 0 && enemies.length < state.active && spawnTick % 9 === 0) {
129
- const fresh = spawnOne(grid, enemies);
130
- if (fresh) { enemies.push(fresh); remaining -= 1; }
131
- }
132
- // 玩家重生
133
- if (!player.alive && !over) {
134
- if (respawnTicks <= 0) { player.alive = true; player.r = 11; player.c = 7; player.dir = "up"; player.cooldown = 0; }
135
- else respawnTicks -= 1;
136
- }
137
-
138
- const win = remaining <= 0 && enemies.length === 0;
139
- return Object.assign({}, state, {
140
- grid, player, enemies, bullets: nextBullets, score, remaining,
141
- over: over || baseHit, win, spawnTick, respawnTicks,
142
- });
143
- }
144
-
145
- export function nextWave(state) {
146
- return Object.assign({}, state, {
147
- wave: state.wave + 1,
148
- remaining: Math.min(3 + state.wave, 8),
149
- active: Math.min(2 + Math.floor(state.wave / 2), 4),
150
- enemies: [], win: false, spawnTick: 0,
151
- });
152
- }
1
+ /* 趣味游戏 · 坦克大战核心逻辑(纯函数,无 React)——供组件复用,便于单测。 */
2
+ export const GRID = 15;
3
+ export const EMPTY = 0, STEEL = 1, BRICK = 2, BASE = 3;
4
+ const DIRS = { up: [-1, 0], down: [1, 0], left: [0, -1], right: [0, 1] };
5
+ export const DIR_KEYS = Object.keys(DIRS);
6
+ const SPAWNS = [{ r: 0, c: 0 }, { r: 0, c: 7 }, { r: 0, c: 14 }];
7
+
8
+ function cloneGrid(g) { return g.map((row) => row.slice()); }
9
+ function inBounds(r, c) { return r >= 0 && r < GRID && c >= 0 && c < GRID; }
10
+ function cellBlockedForTank(grid, r, c) { return !inBounds(r, c) || grid[r][c] !== EMPTY; }
11
+
12
+ export function makeGrid() {
13
+ const g = Array.from({ length: GRID }, () => Array(GRID).fill(EMPTY));
14
+ const set = (r, c, v) => { if (r >= 0 && r < GRID && c >= 0 && c < GRID) g[r][c] = v; };
15
+ for (let r = 2; r <= 5; r += 1) { set(r, 3, STEEL); set(r, 11, STEEL); set(r, 7, STEEL); }
16
+ for (let c = 4; c <= 10; c += 1) set(8, c, BRICK);
17
+ set(9, 5, BRICK); set(9, 6, BRICK); set(9, 8, BRICK); set(9, 9, BRICK); set(9, 10, BRICK);
18
+ set(13, 7, BASE);
19
+ set(12, 6, BRICK); set(12, 7, BRICK); set(12, 8, BRICK);
20
+ set(13, 6, BRICK); set(13, 8, BRICK);
21
+ return g;
22
+ }
23
+ export function findBase(grid) {
24
+ for (let r = 0; r < GRID; r += 1) for (let c = 0; c < GRID; c += 1) if (grid[r][c] === BASE) return { r, c };
25
+ return { r: 13, c: 7 };
26
+ }
27
+ function playerSpawn() { return { r: 11, c: 7, dir: "up", alive: true, lives: 3, cooldown: 0 }; }
28
+
29
+ export function initialState() {
30
+ return {
31
+ grid: makeGrid(),
32
+ player: playerSpawn(),
33
+ enemies: [],
34
+ bullets: [],
35
+ score: 0,
36
+ wave: 1,
37
+ remaining: 4,
38
+ active: 2,
39
+ spawnTick: 0,
40
+ over: false, win: false,
41
+ respawnTicks: 0,
42
+ };
43
+ }
44
+ function spawnBullet(r, c, dir, from) {
45
+ const [dr, dc] = DIRS[dir];
46
+ return { r: r + dr, c: c + dc, dr, dc, from };
47
+ }
48
+ function spawnOne(grid, enemies) {
49
+ for (const s of SPAWNS) {
50
+ if (grid[s.r][s.c] === EMPTY && !enemies.some((e) => e.r === s.r && e.c === s.c)) {
51
+ return { r: s.r, c: s.c, dir: "down", hp: 1, cooldown: 3, moveTick: 0 };
52
+ }
53
+ }
54
+ return null;
55
+ }
56
+
57
+ export function step(state, input) {
58
+ if (state.over || state.win) return state;
59
+ const grid = cloneGrid(state.grid);
60
+ let player = Object.assign({}, state.player);
61
+ let enemies = state.enemies.map((e) => Object.assign({}, e));
62
+ let bullets = state.bullets.map((b) => Object.assign({}, b));
63
+ let remaining = state.remaining, score = state.score, over = state.over;
64
+ let respawnTicks = state.respawnTicks;
65
+ let spawnTick = state.spawnTick + 1;
66
+
67
+ // 玩家移动
68
+ if (player.alive && input.dir && DIRS[input.dir]) {
69
+ const [dr, dc] = DIRS[input.dir];
70
+ player.dir = input.dir;
71
+ const nr = player.r + dr, nc = player.c + dc;
72
+ if (!cellBlockedForTank(grid, nr, nc)) { player.r = nr; player.c = nc; }
73
+ }
74
+ // 玩家射击
75
+ if (player.alive && input.shoot && player.cooldown <= 0) {
76
+ bullets.push(spawnBullet(player.r, player.c, player.dir, "p"));
77
+ player.cooldown = 9;
78
+ }
79
+ player.cooldown = Math.max(0, player.cooldown - 1);
80
+
81
+ // 敌人移动/射击
82
+ for (let i = 0; i < enemies.length; i += 1) {
83
+ const e = enemies[i];
84
+ e.cooldown -= 1; e.moveTick += 1;
85
+ if (e.moveTick % 12 === 0) {
86
+ const [dr, dc] = DIRS[e.dir];
87
+ const nr = e.r + dr, nc = e.c + dc;
88
+ if (cellBlockedForTank(grid, nr, nc) || enemies.some((o, oi) => oi !== i && o.r === nr && o.c === nc)) {
89
+ const opts = DIR_KEYS.filter((d) => {
90
+ const [ddr, ddc] = DIRS[d];
91
+ const tr = e.r + ddr, tc = e.c + ddc;
92
+ return !cellBlockedForTank(grid, tr, tc) && !enemies.some((o, oi) => oi !== i && o.r === tr && o.c === tc);
93
+ });
94
+ if (opts.length) e.dir = opts[Math.floor(Math.random() * opts.length)];
95
+ } else { e.r = nr; e.c = nc; }
96
+ }
97
+ if (e.cooldown <= 0) {
98
+ bullets.push(spawnBullet(e.r, e.c, e.dir, "e"));
99
+ e.cooldown = 9 + Math.floor(Math.random() * 9);
100
+ }
101
+ }
102
+
103
+ // 子弹移动/碰撞
104
+ let baseHit = false, playerHit = false;
105
+ const nextBullets = [];
106
+ const base = findBase(grid);
107
+ for (const b of bullets) {
108
+ const nr = b.r + b.dr, nc = b.c + b.dc;
109
+ if (!inBounds(nr, nc)) continue;
110
+ const cell = grid[nr][nc];
111
+ if (cell === STEEL) continue;
112
+ if (cell === BRICK) { grid[nr][nc] = EMPTY; continue; }
113
+ if (cell === BASE) { if (b.from === "e") baseHit = true; continue; }
114
+ if (b.from === "p") {
115
+ const idx = enemies.findIndex((e) => e.r === nr && e.c === nc);
116
+ if (idx >= 0) { enemies.splice(idx, 1); score += 100; continue; }
117
+ } else if (player.alive && player.r === nr && player.c === nc) {
118
+ player.lives -= 1;
119
+ if (player.lives <= 0) over = true;
120
+ else { player.alive = false; respawnTicks = 24; }
121
+ continue;
122
+ }
123
+ b.r = nr; b.c = nc;
124
+ nextBullets.push(b);
125
+ }
126
+
127
+ // 生成敌人
128
+ if (remaining > 0 && enemies.length < state.active && spawnTick % 9 === 0) {
129
+ const fresh = spawnOne(grid, enemies);
130
+ if (fresh) { enemies.push(fresh); remaining -= 1; }
131
+ }
132
+ // 玩家重生
133
+ if (!player.alive && !over) {
134
+ if (respawnTicks <= 0) { player.alive = true; player.r = 11; player.c = 7; player.dir = "up"; player.cooldown = 0; }
135
+ else respawnTicks -= 1;
136
+ }
137
+
138
+ const win = remaining <= 0 && enemies.length === 0;
139
+ return Object.assign({}, state, {
140
+ grid, player, enemies, bullets: nextBullets, score, remaining,
141
+ over: over || baseHit, win, spawnTick, respawnTicks,
142
+ });
143
+ }
144
+
145
+ export function nextWave(state) {
146
+ return Object.assign({}, state, {
147
+ wave: state.wave + 1,
148
+ remaining: Math.min(3 + state.wave, 8),
149
+ active: Math.min(2 + Math.floor(state.wave / 2), 4),
150
+ enemies: [], win: false, spawnTick: 0,
151
+ });
152
+ }