koishi-plugin-cchess 1.0.3 → 1.0.4
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/lib/index.js +696 -961
- package/package.json +3 -3
- package/readme.md +8 -3
package/lib/index.js
CHANGED
|
@@ -57,7 +57,6 @@ var usage = `## 使用
|
|
|
57
57
|
|
|
58
58
|
- 956758505`;
|
|
59
59
|
var inject = {
|
|
60
|
-
// required: ['monetary', 'database', 'puppeteer', 'canvas'],
|
|
61
60
|
required: ["database", "puppeteer", "canvas"],
|
|
62
61
|
optional: ["markdownToImage"]
|
|
63
62
|
};
|
|
@@ -66,8 +65,8 @@ var boardSkins = ["棋弈无限红绿棋盘", "一鸣惊人棋盘", "三星堆
|
|
|
66
65
|
var Config = import_koishi.Schema.object({
|
|
67
66
|
boardSkin: import_koishi.Schema.union(boardSkins).default("象甲2023棋盘").description(`棋盘皮肤。`),
|
|
68
67
|
pieceSkin: import_koishi.Schema.union(pieceSkins).default("象甲棋子").description(`棋子皮肤。`),
|
|
69
|
-
allowFreePieceMovementInHumanMachineMode: import_koishi.Schema.boolean().default(false).description(
|
|
70
|
-
defaultEngineThinkingDepth: import_koishi.Schema.number().min(0).max(100).default(10).description(
|
|
68
|
+
allowFreePieceMovementInHumanMachineMode: import_koishi.Schema.boolean().default(false).description(`是否允许在人机模式下所有用户都可以自由移动棋子,开启后可以不需要加入游戏直接开始玩人机模式。`),
|
|
69
|
+
defaultEngineThinkingDepth: import_koishi.Schema.number().min(0).max(100).default(10).description(`默认引擎思考深度,最小为 0,最大为 100,越高 AI 棋力越强。由于 Nodejs 不支持 SIMD,所以不建议设置过高。`),
|
|
71
70
|
defaultMaxLeaderboardEntries: import_koishi.Schema.number().min(0).default(10).description(`显示排行榜时默认的最大人数。`),
|
|
72
71
|
retractDelay: import_koishi.Schema.number().min(0).default(0).description(`自动撤回等待的时间,单位是秒。值为 0 时不启用自动撤回功能。`),
|
|
73
72
|
imgScale: import_koishi.Schema.number().min(1).default(1).description(`图片分辨率倍率。`),
|
|
@@ -82,29 +81,26 @@ function apply(ctx, config) {
|
|
|
82
81
|
const channelIds = Object.keys(engines);
|
|
83
82
|
channelIds.forEach((channelId) => {
|
|
84
83
|
if (engines[channelId]) {
|
|
85
|
-
|
|
84
|
+
try {
|
|
85
|
+
engines[channelId].send_command("quit");
|
|
86
|
+
} catch (e) {
|
|
87
|
+
}
|
|
86
88
|
engines[channelId] = null;
|
|
87
89
|
}
|
|
88
90
|
});
|
|
89
91
|
});
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
const ye = 550;
|
|
97
|
-
const ze = 500;
|
|
98
|
-
const ke = 120;
|
|
99
|
-
const weight = 500;
|
|
100
|
-
const height = 550;
|
|
92
|
+
const CELL_WIDTH = 54;
|
|
93
|
+
const ARROW_OFFSET = 33;
|
|
94
|
+
const BOARD_PADDING = 33;
|
|
95
|
+
const PIECE_SIZE = 50;
|
|
96
|
+
const BOARD_WIDTH = 500;
|
|
97
|
+
const BOARD_HEIGHT = 550;
|
|
101
98
|
const scale = config.imgScale;
|
|
102
99
|
const isFlipBoard = false;
|
|
103
100
|
const engineSettings = {
|
|
104
101
|
Threads: 1,
|
|
105
102
|
Hash: 128,
|
|
106
103
|
MultiPV: 1
|
|
107
|
-
// "Skill Level": 20,
|
|
108
104
|
};
|
|
109
105
|
const thinkingSettings = {
|
|
110
106
|
moveTime: 1,
|
|
@@ -126,7 +122,6 @@ function apply(ctx, config) {
|
|
|
126
122
|
isDraw: { type: "boolean", initial: false },
|
|
127
123
|
winSide: { type: "string", initial: "" },
|
|
128
124
|
loseSide: { type: "string", initial: "" },
|
|
129
|
-
// lastPV: {type: 'json', initial: null},
|
|
130
125
|
originalState: { type: "json", initial: {} },
|
|
131
126
|
thinkingDetail: { type: "json", initial: [] },
|
|
132
127
|
currentMoveId: { type: "string", initial: "" },
|
|
@@ -135,14 +130,12 @@ function apply(ctx, config) {
|
|
|
135
130
|
isAnalyzing: { type: "boolean", initial: false },
|
|
136
131
|
turnAfterLastEat: { type: "string", initial: "w" },
|
|
137
132
|
isEngineReady: { type: "boolean", initial: false },
|
|
138
|
-
// multiPvInfoBuffer: {type: 'json', initial: {}},
|
|
139
133
|
isEnginePlayRed: { type: "boolean", initial: false },
|
|
140
134
|
isEngineAnalyze: { type: "boolean", initial: false },
|
|
141
135
|
isRegretRequest: { type: "boolean", initial: false },
|
|
142
136
|
lastMove: { type: "json", initial: [[0, 0], [0, 0]] },
|
|
143
137
|
isEnginePlayBlack: { type: "boolean", initial: false },
|
|
144
138
|
ponderHint: { type: "json", initial: [[0, 0], [0, 0]] },
|
|
145
|
-
// bestMoveHint: {type: 'json', initial: [[0, 0], [0, 0]]},
|
|
146
139
|
fen: { type: "string", initial: "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w" },
|
|
147
140
|
startFen: { type: "string", initial: "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w" },
|
|
148
141
|
board: { type: "json", initial: fenToBoard("rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w") },
|
|
@@ -220,20 +213,18 @@ function apply(ctx, config) {
|
|
|
220
213
|
if (gameRecord.isStarted) {
|
|
221
214
|
const buffer = await drawChessBoard(channelId);
|
|
222
215
|
const hImg = import_koishi.h.image(buffer, `image/${config.imageType}`);
|
|
223
|
-
return await sendMessage(session,
|
|
216
|
+
return await sendMessage(session, `**【@${username}】**
|
|
224
217
|
游戏已经开始啦!
|
|
225
218
|
${hImg}`);
|
|
226
219
|
}
|
|
227
220
|
await checkEngine(channelId);
|
|
228
221
|
if (engines[channelId] && !gameRecord.isEngineReady) {
|
|
229
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
230
|
-
isEngineReady: true
|
|
231
|
-
});
|
|
222
|
+
await ctx.database.set("cchess_game_records", { channelId }, { isEngineReady: true });
|
|
232
223
|
}
|
|
233
224
|
if (!choice) {
|
|
234
225
|
choice = Math.random() < 0.5 ? "红方" : "黑方";
|
|
235
226
|
} else if (choice.includes("红") && choice.includes("黑")) {
|
|
236
|
-
return await sendMessage(session,
|
|
227
|
+
return await sendMessage(session, `**【@${username}】**
|
|
237
228
|
一次只能选择一方哦!`);
|
|
238
229
|
} else if (choice.includes("红")) {
|
|
239
230
|
choice = "红方";
|
|
@@ -247,19 +238,19 @@ ${hImg}`);
|
|
|
247
238
|
const playersNum = getPlayerRecords.length;
|
|
248
239
|
if (getPlayerRecord.length === 0) {
|
|
249
240
|
await ctx.database.create("cchess_gaming_player_records", { channelId, userId, username, side: choice });
|
|
250
|
-
return await sendMessage(session,
|
|
241
|
+
return await sendMessage(session, `**【@${username}】**
|
|
251
242
|
您加入游戏啦!
|
|
252
|
-
|
|
253
|
-
|
|
243
|
+
您的队伍为:**【${choice}】**
|
|
244
|
+
当前玩家人数为:**【${playersNum + 1}】**
|
|
254
245
|
带“黑”或“红”加入游戏可以更换队伍哦~
|
|
255
246
|
例如:
|
|
256
247
|
- 加入指令 红
|
|
257
248
|
- 加入指令 黑`);
|
|
258
249
|
} else {
|
|
259
250
|
await ctx.database.set("cchess_gaming_player_records", { channelId, userId }, { side: choice });
|
|
260
|
-
return await sendMessage(session,
|
|
261
|
-
|
|
262
|
-
|
|
251
|
+
return await sendMessage(session, `**【@${username}】**
|
|
252
|
+
您已更换队伍为:**【${choice}】**
|
|
253
|
+
当前玩家人数为:**【${playersNum}】**`);
|
|
263
254
|
}
|
|
264
255
|
});
|
|
265
256
|
ctx.command("cchess.退出", "退出游戏").action(async ({ session }) => {
|
|
@@ -267,27 +258,22 @@ ${hImg}`);
|
|
|
267
258
|
await updateNameInPlayerRecord(userId, username);
|
|
268
259
|
const gameRecord = await getGameRecord(channelId);
|
|
269
260
|
if (gameRecord.isStarted) {
|
|
270
|
-
return await sendMessage(session,
|
|
261
|
+
return await sendMessage(session, `**【@${username}】**
|
|
271
262
|
游戏已经开始啦~
|
|
272
263
|
不许逃跑哦!`);
|
|
273
264
|
}
|
|
274
265
|
await checkEngine(channelId);
|
|
275
|
-
if (engines[channelId] && !gameRecord.isEngineReady) {
|
|
276
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
277
|
-
isEngineReady: true
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
266
|
const getPlayerRecord = await ctx.database.get("cchess_gaming_player_records", { channelId, userId });
|
|
281
267
|
const getPlayerRecords = await ctx.database.get("cchess_gaming_player_records", { channelId });
|
|
282
268
|
const playersNum = getPlayerRecords.length;
|
|
283
269
|
if (getPlayerRecord.length === 0) {
|
|
284
|
-
return await sendMessage(session,
|
|
270
|
+
return await sendMessage(session, `**【@${username}】**
|
|
285
271
|
您还未加入游戏呢!`);
|
|
286
272
|
} else {
|
|
287
273
|
await ctx.database.remove("cchess_gaming_player_records", { channelId, userId });
|
|
288
|
-
return await sendMessage(session,
|
|
274
|
+
return await sendMessage(session, `**【@${username}】**
|
|
289
275
|
您已退出游戏!
|
|
290
|
-
|
|
276
|
+
剩余玩家人数为:**【${playersNum - 1}】**`);
|
|
291
277
|
}
|
|
292
278
|
});
|
|
293
279
|
ctx.command("cchess.开始", "开始游戏指令帮助").action(async ({ session }) => {
|
|
@@ -298,33 +284,28 @@ ${hImg}`);
|
|
|
298
284
|
await updateNameInPlayerRecord(userId, username);
|
|
299
285
|
const gameRecord = await getGameRecord(channelId);
|
|
300
286
|
if (gameRecord.isStarted) {
|
|
301
|
-
return await sendMessage(session,
|
|
287
|
+
return await sendMessage(session, `**【@${username}】**
|
|
302
288
|
游戏已经开始啦!`);
|
|
303
289
|
}
|
|
304
290
|
await checkEngine(channelId);
|
|
305
|
-
if (engines[channelId] && !gameRecord.isEngineReady) {
|
|
306
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
307
|
-
isEngineReady: true
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
291
|
const getPlayerRecords = await ctx.database.get("cchess_gaming_player_records", { channelId });
|
|
311
292
|
const playersNum = getPlayerRecords.length;
|
|
312
293
|
let redPlayers = getPlayerRecords.filter((player) => player.side === "红方");
|
|
313
294
|
let blackPlayers = getPlayerRecords.filter((player) => player.side === "黑方");
|
|
314
295
|
let message = "";
|
|
315
296
|
if (playersNum < 2) {
|
|
316
|
-
return await sendMessage(session,
|
|
297
|
+
return await sendMessage(session, `**【@${username}】**
|
|
317
298
|
当前玩家人数不足 2 人,无法开始游戏!`);
|
|
318
299
|
} else {
|
|
319
300
|
if (redPlayers.length !== 1 || blackPlayers.length !== 1) {
|
|
320
301
|
const randomPlayer = getPlayerRecords[Math.floor(Math.random() * getPlayerRecords.length)];
|
|
321
302
|
if (redPlayers.length < 1) {
|
|
322
303
|
randomPlayer.side = "红方";
|
|
323
|
-
message +=
|
|
304
|
+
message += `**【@${randomPlayer.username}】**被自动分配到红方。
|
|
324
305
|
`;
|
|
325
306
|
} else {
|
|
326
307
|
randomPlayer.side = "黑方";
|
|
327
|
-
message +=
|
|
308
|
+
message += `**【@${randomPlayer.username}】**被自动分配到黑方。
|
|
328
309
|
`;
|
|
329
310
|
}
|
|
330
311
|
await ctx.database.set("cchess_gaming_player_records", {
|
|
@@ -335,21 +316,19 @@ ${hImg}`);
|
|
|
335
316
|
blackPlayers = getPlayerRecords.filter((player) => player.side === "黑方");
|
|
336
317
|
}
|
|
337
318
|
message += `红方玩家(${redPlayers.length}):
|
|
338
|
-
${redPlayers.map((player) =>
|
|
319
|
+
${redPlayers.map((player) => `**【@${player.username}】**`).join("\n")}
|
|
339
320
|
|
|
340
321
|
`;
|
|
341
322
|
message += `黑方玩家(${blackPlayers.length}):
|
|
342
|
-
${blackPlayers.map((player) =>
|
|
343
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
344
|
-
isStarted: true
|
|
345
|
-
});
|
|
323
|
+
${blackPlayers.map((player) => `**【@${player.username}】**`).join("\n")}`;
|
|
324
|
+
await ctx.database.set("cchess_game_records", { channelId }, { isStarted: true });
|
|
346
325
|
const turn = gameRecord.turn;
|
|
347
326
|
const sideString = convertTurnToString(turn);
|
|
348
327
|
const buffer = await drawChessBoard(channelId);
|
|
349
328
|
const hImg = import_koishi.h.image(buffer, `image/${config.imageType}`);
|
|
350
|
-
return await sendMessage(session,
|
|
329
|
+
return await sendMessage(session, `### 游戏开始!
|
|
351
330
|
${message}
|
|
352
|
-
|
|
331
|
+
先手方为:**【${sideString}】**
|
|
353
332
|
${hImg}`);
|
|
354
333
|
}
|
|
355
334
|
});
|
|
@@ -358,15 +337,10 @@ ${hImg}`);
|
|
|
358
337
|
await updateNameInPlayerRecord(userId, username);
|
|
359
338
|
const gameRecord = await getGameRecord(channelId);
|
|
360
339
|
if (gameRecord.isStarted) {
|
|
361
|
-
return await sendMessage(session,
|
|
340
|
+
return await sendMessage(session, `**【@${username}】**
|
|
362
341
|
游戏已经开始啦!`);
|
|
363
342
|
}
|
|
364
343
|
await checkEngine(channelId);
|
|
365
|
-
if (engines[channelId] && !gameRecord.isEngineReady) {
|
|
366
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
367
|
-
isEngineReady: true
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
344
|
const getPlayerRecords = await ctx.database.get("cchess_gaming_player_records", { channelId });
|
|
371
345
|
const playersNum = getPlayerRecords.length;
|
|
372
346
|
let redPlayers = getPlayerRecords.filter((player) => player.side === "红方");
|
|
@@ -374,48 +348,42 @@ ${hImg}`);
|
|
|
374
348
|
let message = "";
|
|
375
349
|
let AISide = "";
|
|
376
350
|
if (playersNum < 1 && !config.allowFreePieceMovementInHumanMachineMode) {
|
|
377
|
-
return await sendMessage(session,
|
|
351
|
+
return await sendMessage(session, `**【@${username}】**
|
|
378
352
|
当前玩家人数不足 1 人,无法开始游戏!`);
|
|
379
353
|
} else {
|
|
380
354
|
if (playersNum === 1) {
|
|
381
355
|
if (getPlayerRecords[0].side === "红方") {
|
|
382
356
|
await ctx.database.set("cchess_game_records", { channelId }, { isEnginePlayBlack: true });
|
|
383
357
|
AISide = "黑方";
|
|
384
|
-
message +=
|
|
358
|
+
message += `人类队伍为:**【红方】**
|
|
385
359
|
`;
|
|
386
360
|
} else {
|
|
387
361
|
await ctx.database.set("cchess_game_records", { channelId }, { isEnginePlayRed: true });
|
|
388
362
|
AISide = "红方";
|
|
389
|
-
message +=
|
|
363
|
+
message += `人类队伍为:**【黑方】**
|
|
390
364
|
`;
|
|
391
365
|
}
|
|
392
366
|
} else {
|
|
393
367
|
if (redPlayers.length > blackPlayers.length) {
|
|
394
368
|
await ctx.database.set("cchess_game_records", { channelId }, { isEnginePlayBlack: true });
|
|
395
369
|
AISide = "黑方";
|
|
396
|
-
message +=
|
|
370
|
+
message += `人类队伍为:**【红方】**
|
|
397
371
|
`;
|
|
398
372
|
for (const player of blackPlayers) {
|
|
399
|
-
await ctx.database.set("cchess_gaming_player_records", {
|
|
400
|
-
channelId,
|
|
401
|
-
userId: player.userId
|
|
402
|
-
}, { side: "红方" });
|
|
373
|
+
await ctx.database.set("cchess_gaming_player_records", { channelId, userId: player.userId }, { side: "红方" });
|
|
403
374
|
}
|
|
404
375
|
} else if (redPlayers.length < blackPlayers.length) {
|
|
405
376
|
await ctx.database.set("cchess_game_records", { channelId }, { isEnginePlayRed: true });
|
|
406
377
|
AISide = "红方";
|
|
407
|
-
message +=
|
|
378
|
+
message += `人类队伍为:**【黑方】**
|
|
408
379
|
`;
|
|
409
380
|
for (const player of redPlayers) {
|
|
410
|
-
await ctx.database.set("cchess_gaming_player_records", {
|
|
411
|
-
channelId,
|
|
412
|
-
userId: player.userId
|
|
413
|
-
}, { side: "黑方" });
|
|
381
|
+
await ctx.database.set("cchess_gaming_player_records", { channelId, userId: player.userId }, { side: "黑方" });
|
|
414
382
|
}
|
|
415
383
|
}
|
|
416
384
|
if (redPlayers.length === blackPlayers.length) {
|
|
417
385
|
const side = Math.random() < 0.5 ? "红方" : "黑方";
|
|
418
|
-
message +=
|
|
386
|
+
message += `人类队伍为:**【${side}】**
|
|
419
387
|
`;
|
|
420
388
|
const computerSide = side === "红方" ? "黑方" : "红方";
|
|
421
389
|
AISide = computerSide;
|
|
@@ -428,29 +396,23 @@ ${hImg}`);
|
|
|
428
396
|
}
|
|
429
397
|
}
|
|
430
398
|
}
|
|
431
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
432
|
-
isStarted: true
|
|
433
|
-
});
|
|
399
|
+
await ctx.database.set("cchess_game_records", { channelId }, { isStarted: true });
|
|
434
400
|
const turn = gameRecord.turn;
|
|
435
401
|
const sideString = convertTurnToString(turn);
|
|
436
402
|
if (AISide === sideString) {
|
|
437
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
438
|
-
isAnalyzing: true
|
|
439
|
-
});
|
|
403
|
+
await ctx.database.set("cchess_game_records", { channelId }, { isAnalyzing: true });
|
|
440
404
|
await engineAction(channelId);
|
|
441
405
|
while (true) {
|
|
442
406
|
const gameRecord2 = await getGameRecord(channelId);
|
|
443
|
-
if (!gameRecord2.isAnalyzing)
|
|
444
|
-
break;
|
|
445
|
-
}
|
|
407
|
+
if (!gameRecord2.isAnalyzing) break;
|
|
446
408
|
await (0, import_koishi.sleep)(500);
|
|
447
409
|
}
|
|
448
410
|
}
|
|
449
411
|
const buffer = await drawChessBoard(channelId);
|
|
450
412
|
const hImg = import_koishi.h.image(buffer, `image/${config.imageType}`);
|
|
451
|
-
return await sendMessage(session,
|
|
413
|
+
return await sendMessage(session, `### 游戏开始!
|
|
452
414
|
${message}
|
|
453
|
-
|
|
415
|
+
先手方为:**【${sideString}】**
|
|
454
416
|
${hImg}`);
|
|
455
417
|
}
|
|
456
418
|
});
|
|
@@ -459,177 +421,153 @@ ${hImg}`);
|
|
|
459
421
|
await updateNameInPlayerRecord(userId, username);
|
|
460
422
|
const gameRecord = await getGameRecord(channelId);
|
|
461
423
|
await checkEngine(channelId);
|
|
462
|
-
if (engines[channelId] && !gameRecord.isEngineReady) {
|
|
463
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
464
|
-
isEngineReady: true
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
424
|
if (!gameRecord.isStarted) {
|
|
468
|
-
return await sendMessage(session,
|
|
425
|
+
return await sendMessage(session, `**【@${username}】**
|
|
469
426
|
游戏未开始哦!
|
|
470
427
|
完全没必要强制结束嘛~`);
|
|
471
428
|
} else {
|
|
472
429
|
await endGame(channelId);
|
|
473
|
-
return await sendMessage(session,
|
|
430
|
+
return await sendMessage(session, `**【@${username}】**
|
|
474
431
|
游戏已被强制结束!`);
|
|
475
432
|
}
|
|
476
433
|
});
|
|
477
|
-
ctx.command("cchess.移动 <moveOperation:text>", "进行移动操作").action(
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
return await sendMessage(session, `【@${username}】
|
|
434
|
+
ctx.command("cchess.移动 <moveOperation:text>", "进行移动操作").action(async ({ session }, moveOperation) => {
|
|
435
|
+
const { username, userId, channelId } = session;
|
|
436
|
+
await updateNameInPlayerRecord(userId, username);
|
|
437
|
+
if (!isMoveString(moveOperation) && !isValidFigureMoveName(moveOperation)) {
|
|
438
|
+
return await sendMessage(session, `**【@${username}】**
|
|
483
439
|
无效的移动!`);
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
440
|
+
}
|
|
441
|
+
const gameRecord = await getGameRecord(channelId);
|
|
442
|
+
if (!gameRecord.isStarted) {
|
|
443
|
+
return await sendMessage(session, `**【@${username}】**
|
|
488
444
|
游戏还未开始哦!`);
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
445
|
+
}
|
|
446
|
+
if (gameRecord.isRegretRequest) {
|
|
447
|
+
return await sendMessage(session, `**【@${username}】**
|
|
492
448
|
正在请求悔棋,等待回应中...`);
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
}
|
|
506
|
-
} else {
|
|
507
|
-
return await sendMessage(session, `【@${username}】
|
|
508
|
-
您还未加入游戏呢!`);
|
|
449
|
+
}
|
|
450
|
+
const turn = gameRecord.turn;
|
|
451
|
+
const sideString = convertTurnToString(turn);
|
|
452
|
+
let playerRecord = await ctx.database.get("cchess_gaming_player_records", { channelId, userId });
|
|
453
|
+
if (playerRecord.length === 0) {
|
|
454
|
+
if (config.allowFreePieceMovementInHumanMachineMode) {
|
|
455
|
+
const record = { channelId, userId, username, side: "" };
|
|
456
|
+
if (gameRecord.isEnginePlayRed) record.side = "黑方";
|
|
457
|
+
else if (gameRecord.isEnginePlayBlack) record.side = "红方";
|
|
458
|
+
if (record.side) {
|
|
459
|
+
await ctx.database.create("cchess_gaming_player_records", record);
|
|
460
|
+
playerRecord = await ctx.database.get("cchess_gaming_player_records", { channelId, userId });
|
|
509
461
|
}
|
|
462
|
+
} else {
|
|
463
|
+
return await sendMessage(session, `**【@${username}】**
|
|
464
|
+
您还未加入游戏呢!`);
|
|
510
465
|
}
|
|
511
|
-
|
|
512
|
-
|
|
466
|
+
}
|
|
467
|
+
if (playerRecord.length === 0 || playerRecord[0].side !== sideString) {
|
|
468
|
+
return await sendMessage(session, `**【@${username}】**
|
|
513
469
|
还没轮到${sideString}走棋哦!
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
const
|
|
528
|
-
const
|
|
529
|
-
const
|
|
530
|
-
const
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
const
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
moveInfoList.push({
|
|
542
|
-
moveCord: [[pos[1], pos[0]], [toPos[1], toPos[0]]],
|
|
543
|
-
moveString,
|
|
544
|
-
figureMoveName
|
|
545
|
-
});
|
|
546
|
-
}
|
|
470
|
+
当前走棋方为:**【${convertTurnToString(gameRecord.turn)}】**`);
|
|
471
|
+
}
|
|
472
|
+
await checkEngine(channelId);
|
|
473
|
+
const board = gameRecord.board;
|
|
474
|
+
let selectedPos = void 0;
|
|
475
|
+
let newPos = void 0;
|
|
476
|
+
if (possibleFigureNames.some((figureName) => moveOperation.includes(figureName))) {
|
|
477
|
+
const figureName = getFigureNameFromMoveOperation(moveOperation);
|
|
478
|
+
const englishLetter = getEnglishLetterFromFigureName(figureName);
|
|
479
|
+
const processedLetter = processEnglishLetter(englishLetter, turn);
|
|
480
|
+
const letterPositions = findLetterPositions(board, processedLetter);
|
|
481
|
+
const moveInfoList = [];
|
|
482
|
+
for (const pos of letterPositions) {
|
|
483
|
+
const piece2 = processedLetter;
|
|
484
|
+
const side2 = getSide(piece2);
|
|
485
|
+
const type2 = getType(piece2);
|
|
486
|
+
const moveMap2 = getValidMoveMap(board, type2, side2, [pos[1], pos[0]]);
|
|
487
|
+
const legalPositions = findLegalMovePositions(moveMap2);
|
|
488
|
+
const fen = getFen(board, side2);
|
|
489
|
+
for (const toPos of legalPositions) {
|
|
490
|
+
const moveString = moveToMoveString([[pos[1], pos[0]], [toPos[1], toPos[0]]]);
|
|
491
|
+
const figureMoveName = getFigureMoveName(fen, moveString);
|
|
492
|
+
moveInfoList.push({
|
|
493
|
+
moveCord: [[pos[1], pos[0]], [toPos[1], toPos[0]]],
|
|
494
|
+
moveString,
|
|
495
|
+
figureMoveName
|
|
496
|
+
});
|
|
547
497
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
498
|
+
}
|
|
499
|
+
const duplicateMoveInfo = findMoveInfo(moveInfoList, moveOperation);
|
|
500
|
+
if (!duplicateMoveInfo) {
|
|
501
|
+
return await sendMessage(session, `**【@${username}】**
|
|
551
502
|
无效的移动!`);
|
|
552
|
-
} else {
|
|
553
|
-
[selectedPos, newPos] = duplicateMoveInfo.moveCord;
|
|
554
|
-
}
|
|
555
503
|
} else {
|
|
556
|
-
[selectedPos, newPos] =
|
|
504
|
+
[selectedPos, newPos] = duplicateMoveInfo.moveCord;
|
|
557
505
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
506
|
+
} else {
|
|
507
|
+
[selectedPos, newPos] = moveStringToPos(moveOperation);
|
|
508
|
+
}
|
|
509
|
+
const [newCol, newRow] = newPos;
|
|
510
|
+
const piece = getPiece(board, selectedPos);
|
|
511
|
+
const side = getSide(piece);
|
|
512
|
+
const type = getType(piece);
|
|
513
|
+
if (side !== gameRecord.turn) {
|
|
514
|
+
return await sendMessage(session, `**【@${username}】**
|
|
564
515
|
还没轮到${convertTurnToString(side)}走棋哦!
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
516
|
+
当前走棋方为:**【${convertTurnToString(gameRecord.turn)}】**`);
|
|
517
|
+
}
|
|
518
|
+
const moveMap = getValidMoveMap(board, type, side, selectedPos);
|
|
519
|
+
if (moveMap[newRow][newCol] !== "go" && moveMap[newRow][newCol] !== "eat") {
|
|
520
|
+
return await sendMessage(session, `**【@${username}】**
|
|
570
521
|
无效的移动!`);
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
await
|
|
522
|
+
}
|
|
523
|
+
await makeMove(channelId, selectedPos, newPos);
|
|
524
|
+
await ctx.database.set("cchess_game_records", { channelId }, { isAnalyzing: true });
|
|
525
|
+
await engineAction(channelId);
|
|
526
|
+
while (true) {
|
|
527
|
+
const gameRecord2 = await getGameRecord(channelId);
|
|
528
|
+
if (!gameRecord2.isAnalyzing) break;
|
|
529
|
+
await (0, import_koishi.sleep)(500);
|
|
530
|
+
}
|
|
531
|
+
const newGameRecord = await getGameRecord(channelId);
|
|
532
|
+
if (newGameRecord.winSide !== "") {
|
|
533
|
+
const buffer2 = await drawChessBoard(channelId);
|
|
534
|
+
const hImg2 = import_koishi.h.image(buffer2, `image/${config.imageType}`);
|
|
535
|
+
await updatePlayerRecords(channelId, newGameRecord.winSide, newGameRecord.loseSide);
|
|
536
|
+
await endGame(channelId);
|
|
537
|
+
return await sendMessage(session, `**【@${username}】**
|
|
538
|
+
### 绝杀!
|
|
539
|
+
游戏结束!
|
|
540
|
+
获胜方为:**【${convertTurnToString(newGameRecord.winSide)}】**
|
|
541
|
+
${hImg2}`);
|
|
542
|
+
}
|
|
543
|
+
if (gameRecord.isEnginePlayRed || gameRecord.isEnginePlayBlack) {
|
|
544
|
+
const gameRecordAfterMove = await getGameRecord(channelId);
|
|
545
|
+
let { movesAfterLastEat, boardAfterLastEat, turnAfterLastEat } = gameRecordAfterMove;
|
|
546
|
+
await ctx.database.set("cchess_game_records", { channelId }, { isAnalyzing: true });
|
|
547
|
+
await sendCommand(channelId, "fen " + getFenWithMove(movesAfterLastEat, boardAfterLastEat, turnAfterLastEat));
|
|
548
|
+
await go(channelId, 1e3 * thinkingSettings["movetime"], thinkingSettings["depth"], -1);
|
|
577
549
|
while (true) {
|
|
578
|
-
const
|
|
579
|
-
if (!
|
|
580
|
-
break;
|
|
581
|
-
}
|
|
550
|
+
const rec = await getGameRecord(channelId);
|
|
551
|
+
if (!rec.isAnalyzing) break;
|
|
582
552
|
await (0, import_koishi.sleep)(500);
|
|
583
553
|
}
|
|
584
|
-
const
|
|
585
|
-
if (
|
|
554
|
+
const engineResultRecord = await getGameRecord(channelId);
|
|
555
|
+
if (engineResultRecord.winSide !== "") {
|
|
586
556
|
const buffer2 = await drawChessBoard(channelId);
|
|
587
557
|
const hImg2 = import_koishi.h.image(buffer2, `image/${config.imageType}`);
|
|
588
|
-
await updatePlayerRecords(channelId,
|
|
558
|
+
await updatePlayerRecords(channelId, engineResultRecord.winSide, engineResultRecord.loseSide);
|
|
589
559
|
await endGame(channelId);
|
|
590
|
-
return await sendMessage(session,
|
|
591
|
-
绝杀!
|
|
560
|
+
return await sendMessage(session, `**【@${username}】**
|
|
561
|
+
### 绝杀!
|
|
592
562
|
游戏结束!
|
|
593
|
-
|
|
563
|
+
获胜方为:**【${convertTurnToString(engineResultRecord.winSide)}】**
|
|
594
564
|
${hImg2}`);
|
|
595
565
|
}
|
|
596
|
-
if (gameRecord.isEnginePlayRed || gameRecord.isEnginePlayBlack) {
|
|
597
|
-
const gameRecord2 = await getGameRecord(channelId);
|
|
598
|
-
let {
|
|
599
|
-
movesAfterLastEat,
|
|
600
|
-
boardAfterLastEat,
|
|
601
|
-
turnAfterLastEat
|
|
602
|
-
// multiPvInfoBuffer
|
|
603
|
-
} = gameRecord2;
|
|
604
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
605
|
-
isAnalyzing: true
|
|
606
|
-
});
|
|
607
|
-
await sendCommand(channelId, "fen " + getFenWithMove(movesAfterLastEat, boardAfterLastEat, turnAfterLastEat)), await go(channelId, 1e3 * thinkingSettings["movetime"], thinkingSettings["depth"], -1);
|
|
608
|
-
while (true) {
|
|
609
|
-
const gameRecord3 = await getGameRecord(channelId);
|
|
610
|
-
if (!gameRecord3.isAnalyzing) {
|
|
611
|
-
break;
|
|
612
|
-
}
|
|
613
|
-
await (0, import_koishi.sleep)(500);
|
|
614
|
-
}
|
|
615
|
-
const newGameRecord2 = await getGameRecord(channelId);
|
|
616
|
-
if (newGameRecord2.winSide !== "") {
|
|
617
|
-
const buffer2 = await drawChessBoard(channelId);
|
|
618
|
-
const hImg2 = import_koishi.h.image(buffer2, `image/${config.imageType}`);
|
|
619
|
-
await updatePlayerRecords(channelId, newGameRecord2.winSide, newGameRecord2.loseSide);
|
|
620
|
-
await endGame(channelId);
|
|
621
|
-
return await sendMessage(session, `【@${username}】
|
|
622
|
-
绝杀!
|
|
623
|
-
游戏结束!
|
|
624
|
-
获胜方为:【${convertTurnToString(newGameRecord2.winSide)}】
|
|
625
|
-
${hImg2}`);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
const buffer = await drawChessBoard(channelId);
|
|
629
|
-
const hImg = import_koishi.h.image(buffer, `image/${config.imageType}`);
|
|
630
|
-
return await sendMessage(session, `${hImg}`);
|
|
631
566
|
}
|
|
632
|
-
|
|
567
|
+
const buffer = await drawChessBoard(channelId);
|
|
568
|
+
const hImg = import_koishi.h.image(buffer, `image/${config.imageType}`);
|
|
569
|
+
return await sendMessage(session, `${hImg}`);
|
|
570
|
+
});
|
|
633
571
|
ctx.command("cchess.悔棋", "悔棋指令帮助").action(async ({ session }) => {
|
|
634
572
|
await session.execute(`cchess.悔棋 -h`);
|
|
635
573
|
});
|
|
@@ -638,32 +576,27 @@ ${hImg2}`);
|
|
|
638
576
|
await updateNameInPlayerRecord(userId, username);
|
|
639
577
|
const gameRecord = await getGameRecord(channelId);
|
|
640
578
|
if (!gameRecord.isStarted) {
|
|
641
|
-
return await sendMessage(session,
|
|
579
|
+
return await sendMessage(session, `**【@${username}】**
|
|
642
580
|
游戏还未开始哦!`);
|
|
643
581
|
}
|
|
644
582
|
await checkEngine(channelId);
|
|
645
|
-
if (engines[channelId] && !gameRecord.isEngineReady) {
|
|
646
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
647
|
-
isEngineReady: true
|
|
648
|
-
});
|
|
649
|
-
}
|
|
650
583
|
if (gameRecord.isRegretRequest) {
|
|
651
|
-
return await sendMessage(session,
|
|
584
|
+
return await sendMessage(session, `**【@${username}】**
|
|
652
585
|
已经有悔棋请求了!`);
|
|
653
586
|
}
|
|
654
587
|
const playerRecord = await ctx.database.get("cchess_gaming_player_records", { channelId, userId });
|
|
655
588
|
if (playerRecord.length === 0) {
|
|
656
|
-
return await sendMessage(session,
|
|
589
|
+
return await sendMessage(session, `**【@${username}】**
|
|
657
590
|
您还未加入游戏呢!`);
|
|
658
591
|
}
|
|
659
592
|
const turn = gameRecord.turn;
|
|
660
593
|
const sideString = convertTurnToString(turn);
|
|
661
594
|
if (gameRecord.isAnalyzing) {
|
|
662
|
-
return await sendMessage(session,
|
|
595
|
+
return await sendMessage(session, `**【@${username}】**
|
|
663
596
|
对局分析中,请稍后再试!`);
|
|
664
597
|
}
|
|
665
598
|
if (gameRecord.moveList.length < 1) {
|
|
666
|
-
return await sendMessage(session,
|
|
599
|
+
return await sendMessage(session, `**【@${username}】**
|
|
667
600
|
当前无法悔棋!`);
|
|
668
601
|
}
|
|
669
602
|
if (gameRecord.isEnginePlayBlack || gameRecord.isEnginePlayRed) {
|
|
@@ -671,20 +604,18 @@ ${hImg2}`);
|
|
|
671
604
|
await undoMove(channelId);
|
|
672
605
|
const buffer = await drawChessBoard(channelId);
|
|
673
606
|
const hImg = import_koishi.h.image(buffer, `image/${config.imageType}`);
|
|
674
|
-
return await sendMessage(session,
|
|
607
|
+
return await sendMessage(session, `**【@${username}】**
|
|
675
608
|
悔棋成功!
|
|
676
609
|
${hImg}`);
|
|
677
610
|
} else {
|
|
678
611
|
if (playerRecord[0].side === sideString) {
|
|
679
|
-
return await sendMessage(session,
|
|
612
|
+
return await sendMessage(session, `**【@${username}】**
|
|
680
613
|
现在轮到你下棋,不可悔棋!`);
|
|
681
614
|
} else {
|
|
682
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
683
|
-
|
|
684
|
-
});
|
|
685
|
-
return await sendMessage(session, `【@${username}】
|
|
615
|
+
await ctx.database.set("cchess_game_records", { channelId }, { isRegretRequest: true });
|
|
616
|
+
return await sendMessage(session, `**【@${username}】**
|
|
686
617
|
请求悔棋中...
|
|
687
|
-
|
|
618
|
+
请对方输入相关指令选择同意或拒绝!`);
|
|
688
619
|
}
|
|
689
620
|
}
|
|
690
621
|
});
|
|
@@ -693,41 +624,34 @@ ${hImg}`);
|
|
|
693
624
|
await updateNameInPlayerRecord(userId, username);
|
|
694
625
|
const gameRecord = await getGameRecord(channelId);
|
|
695
626
|
if (!gameRecord.isStarted) {
|
|
696
|
-
return await sendMessage(session,
|
|
627
|
+
return await sendMessage(session, `**【@${username}】**
|
|
697
628
|
游戏还未开始哦!`);
|
|
698
629
|
}
|
|
699
630
|
await checkEngine(channelId);
|
|
700
|
-
if (engines[channelId] && !gameRecord.isEngineReady) {
|
|
701
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
702
|
-
isEngineReady: true
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
631
|
if (gameRecord.isAnalyzing) {
|
|
706
|
-
return await sendMessage(session,
|
|
632
|
+
return await sendMessage(session, `**【@${username}】**
|
|
707
633
|
对局分析中,请稍后再试!`);
|
|
708
634
|
}
|
|
709
635
|
if (!gameRecord.isRegretRequest) {
|
|
710
|
-
return await sendMessage(session,
|
|
636
|
+
return await sendMessage(session, `**【@${username}】**
|
|
711
637
|
当前无悔棋请求!`);
|
|
712
638
|
}
|
|
713
639
|
const playerRecord = await ctx.database.get("cchess_gaming_player_records", { channelId, userId });
|
|
714
640
|
if (playerRecord.length === 0) {
|
|
715
|
-
return await sendMessage(session,
|
|
641
|
+
return await sendMessage(session, `**【@${username}】**
|
|
716
642
|
您还未加入游戏呢!`);
|
|
717
643
|
}
|
|
718
644
|
const turn = gameRecord.turn;
|
|
719
645
|
const sideString = convertTurnToString(turn);
|
|
720
646
|
if (playerRecord[0].side !== sideString) {
|
|
721
|
-
return await sendMessage(session,
|
|
647
|
+
return await sendMessage(session, `**【@${username}】**
|
|
722
648
|
您所在的队伍不能做出选择!`);
|
|
723
649
|
} else {
|
|
724
650
|
await undoMove(channelId);
|
|
725
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
726
|
-
isRegretRequest: false
|
|
727
|
-
});
|
|
651
|
+
await ctx.database.set("cchess_game_records", { channelId }, { isRegretRequest: false });
|
|
728
652
|
const buffer = await drawChessBoard(channelId);
|
|
729
653
|
const hImg = import_koishi.h.image(buffer, `image/${config.imageType}`);
|
|
730
|
-
return await sendMessage(session,
|
|
654
|
+
return await sendMessage(session, `**【@${username}】**
|
|
731
655
|
由于您同意了对方的悔棋请求!
|
|
732
656
|
悔棋成功!
|
|
733
657
|
${hImg}`);
|
|
@@ -738,40 +662,33 @@ ${hImg}`);
|
|
|
738
662
|
await updateNameInPlayerRecord(userId, username);
|
|
739
663
|
const gameRecord = await getGameRecord(channelId);
|
|
740
664
|
if (!gameRecord.isStarted) {
|
|
741
|
-
return await sendMessage(session,
|
|
665
|
+
return await sendMessage(session, `**【@${username}】**
|
|
742
666
|
游戏还未开始哦!`);
|
|
743
667
|
}
|
|
744
668
|
await checkEngine(channelId);
|
|
745
|
-
if (engines[channelId] && !gameRecord.isEngineReady) {
|
|
746
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
747
|
-
isEngineReady: true
|
|
748
|
-
});
|
|
749
|
-
}
|
|
750
669
|
if (gameRecord.isAnalyzing) {
|
|
751
|
-
return await sendMessage(session,
|
|
670
|
+
return await sendMessage(session, `**【@${username}】**
|
|
752
671
|
对局分析中,请稍后再试!`);
|
|
753
672
|
}
|
|
754
673
|
if (!gameRecord.isRegretRequest) {
|
|
755
|
-
return await sendMessage(session,
|
|
674
|
+
return await sendMessage(session, `**【@${username}】**
|
|
756
675
|
当前无悔棋请求!`);
|
|
757
676
|
}
|
|
758
677
|
const playerRecord = await ctx.database.get("cchess_gaming_player_records", { channelId, userId });
|
|
759
678
|
if (playerRecord.length === 0) {
|
|
760
|
-
return await sendMessage(session,
|
|
679
|
+
return await sendMessage(session, `**【@${username}】**
|
|
761
680
|
您还未加入游戏呢!`);
|
|
762
681
|
}
|
|
763
682
|
const turn = gameRecord.turn;
|
|
764
683
|
const sideString = convertTurnToString(turn);
|
|
765
684
|
if (playerRecord[0].side !== sideString) {
|
|
766
|
-
return await sendMessage(session,
|
|
685
|
+
return await sendMessage(session, `**【@${username}】**
|
|
767
686
|
您所在的队伍不能做出选择!`);
|
|
768
687
|
} else {
|
|
769
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
770
|
-
isRegretRequest: false
|
|
771
|
-
});
|
|
688
|
+
await ctx.database.set("cchess_game_records", { channelId }, { isRegretRequest: false });
|
|
772
689
|
const buffer = await drawChessBoard(channelId);
|
|
773
690
|
const hImg = import_koishi.h.image(buffer, `image/${config.imageType}`);
|
|
774
|
-
return await sendMessage(session,
|
|
691
|
+
return await sendMessage(session, `**【@${username}】**
|
|
775
692
|
由于您拒绝了对方的悔棋请求!
|
|
776
693
|
悔棋失败,游戏继续进行!
|
|
777
694
|
${hImg}`);
|
|
@@ -782,38 +699,33 @@ ${hImg}`);
|
|
|
782
699
|
await updateNameInPlayerRecord(userId, username);
|
|
783
700
|
const gameRecord = await getGameRecord(channelId);
|
|
784
701
|
if (!gameRecord.isStarted) {
|
|
785
|
-
return await sendMessage(session,
|
|
702
|
+
return await sendMessage(session, `**【@${username}】**
|
|
786
703
|
游戏还未开始哦!`);
|
|
787
704
|
}
|
|
788
705
|
await checkEngine(channelId);
|
|
789
|
-
if (engines[channelId] && !gameRecord.isEngineReady) {
|
|
790
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
791
|
-
isEngineReady: true
|
|
792
|
-
});
|
|
793
|
-
}
|
|
794
706
|
if (gameRecord.isAnalyzing) {
|
|
795
|
-
return await sendMessage(session,
|
|
707
|
+
return await sendMessage(session, `**【@${username}】**
|
|
796
708
|
对局分析中,请稍后再试!`);
|
|
797
709
|
}
|
|
798
710
|
const playerRecord = await ctx.database.get("cchess_gaming_player_records", { channelId, userId });
|
|
799
711
|
if (playerRecord.length === 0) {
|
|
800
|
-
return await sendMessage(session,
|
|
712
|
+
return await sendMessage(session, `**【@${username}】**
|
|
801
713
|
您还未加入游戏呢!`);
|
|
802
714
|
}
|
|
803
715
|
const turn = gameRecord.turn;
|
|
804
716
|
const sideString = convertTurnToString(turn);
|
|
805
717
|
if (playerRecord[0].side !== sideString) {
|
|
806
|
-
return await sendMessage(session,
|
|
718
|
+
return await sendMessage(session, `**【@${username}】**
|
|
807
719
|
还没轮到${sideString}走棋哦!
|
|
808
|
-
|
|
720
|
+
当前走棋方为:**【${convertTurnToString(gameRecord.turn)}】**`);
|
|
809
721
|
}
|
|
810
722
|
const anotherSideString = convertTurnToString(turn === "w" ? "b" : "w");
|
|
811
723
|
await updatePlayerRecords(channelId, turn === "w" ? "b" : "w", turn);
|
|
812
724
|
await endGame(channelId);
|
|
813
|
-
return await sendMessage(session,
|
|
725
|
+
return await sendMessage(session, `**【@${username}】**
|
|
814
726
|
认输成功!
|
|
815
727
|
游戏结束!
|
|
816
|
-
|
|
728
|
+
获胜方为:**【${anotherSideString}】**`);
|
|
817
729
|
});
|
|
818
730
|
ctx.command("cchess.查看云库残局", "云库残局指令帮助").action(async ({ session }) => {
|
|
819
731
|
await session.execute(`cchess.查看云库残局 -h`);
|
|
@@ -822,14 +734,14 @@ ${hImg}`);
|
|
|
822
734
|
const { username, userId, channelId } = session;
|
|
823
735
|
await updateNameInPlayerRecord(userId, username);
|
|
824
736
|
await checkEngine(channelId);
|
|
825
|
-
return await sendMessage(session,
|
|
737
|
+
return await sendMessage(session, `**【@${username}】**
|
|
826
738
|
https://www.chessdb.cn/egtb_info_dtm.html`);
|
|
827
739
|
});
|
|
828
740
|
ctx.command("cchess.查看云库残局.DTC统计", "云库残局DTM统计").action(async ({ session }) => {
|
|
829
741
|
const { username, userId, channelId } = session;
|
|
830
742
|
await updateNameInPlayerRecord(userId, username);
|
|
831
743
|
await checkEngine(channelId);
|
|
832
|
-
return await sendMessage(session,
|
|
744
|
+
return await sendMessage(session, `**【@${username}】**
|
|
833
745
|
https://www.chessdb.cn/egtb_info.html`);
|
|
834
746
|
});
|
|
835
747
|
ctx.command("cchess.编辑棋盘", "编辑棋盘指令帮助").action(async ({ session }) => {
|
|
@@ -840,61 +752,33 @@ https://www.chessdb.cn/egtb_info.html`);
|
|
|
840
752
|
await updateNameInPlayerRecord(userId, username);
|
|
841
753
|
const gameRecord = await getGameRecord(channelId);
|
|
842
754
|
if (gameRecord.isStarted) {
|
|
843
|
-
return await sendMessage(session,
|
|
755
|
+
return await sendMessage(session, `**【@${username}】**
|
|
844
756
|
游戏已经开始啦~
|
|
845
757
|
不可编辑棋盘!`);
|
|
846
758
|
}
|
|
847
759
|
await checkEngine(channelId);
|
|
848
|
-
if (
|
|
849
|
-
await
|
|
850
|
-
isEngineReady: true
|
|
851
|
-
});
|
|
852
|
-
}
|
|
853
|
-
if (!fen) {
|
|
854
|
-
return await sendMessage(session, `【@${username}】
|
|
760
|
+
if (!fen || !isValidateFen(fen)) {
|
|
761
|
+
return await sendMessage(session, `**【@${username}】**
|
|
855
762
|
无效的FEN串!`);
|
|
856
763
|
}
|
|
857
|
-
if (!isValidateFen(fen)) {
|
|
858
|
-
return await sendMessage(session, `【@${username}】
|
|
859
|
-
无效的FEN串!`);
|
|
860
|
-
}
|
|
861
|
-
let {
|
|
862
|
-
turn,
|
|
863
|
-
board,
|
|
864
|
-
moveList,
|
|
865
|
-
movesAfterLastEat,
|
|
866
|
-
boardAfterLastEat,
|
|
867
|
-
turnAfterLastEat,
|
|
868
|
-
lastMove,
|
|
869
|
-
isHistoryMode,
|
|
870
|
-
originalState,
|
|
871
|
-
startFen
|
|
872
|
-
} = gameRecord;
|
|
873
764
|
const t = fen;
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
movesAfterLastEat = [];
|
|
878
|
-
boardAfterLastEat = fenToBoard(t);
|
|
879
|
-
turnAfterLastEat = fenToTurn(t);
|
|
880
|
-
t.includes("moves") ? startFen = t.split("moves")[0] : startFen = t;
|
|
881
|
-
isHistoryMode = false;
|
|
882
|
-
originalState = {};
|
|
883
|
-
lastMove = null;
|
|
765
|
+
const newBoard = fenToBoard(t);
|
|
766
|
+
const newTurn = "w";
|
|
767
|
+
const newStartFen = t.includes("moves") ? t.split("moves")[0] : t;
|
|
884
768
|
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
885
|
-
turn,
|
|
886
|
-
board,
|
|
887
|
-
moveList,
|
|
888
|
-
movesAfterLastEat,
|
|
889
|
-
boardAfterLastEat,
|
|
890
|
-
turnAfterLastEat,
|
|
891
|
-
lastMove,
|
|
892
|
-
isHistoryMode,
|
|
893
|
-
originalState,
|
|
894
|
-
startFen
|
|
769
|
+
turn: newTurn,
|
|
770
|
+
board: newBoard,
|
|
771
|
+
moveList: [],
|
|
772
|
+
movesAfterLastEat: [],
|
|
773
|
+
boardAfterLastEat: fenToBoard(t),
|
|
774
|
+
turnAfterLastEat: fenToTurn(t),
|
|
775
|
+
lastMove: null,
|
|
776
|
+
isHistoryMode: false,
|
|
777
|
+
originalState: {},
|
|
778
|
+
startFen: newStartFen
|
|
895
779
|
});
|
|
896
780
|
await parseFen(channelId, t);
|
|
897
|
-
return await sendMessage(session,
|
|
781
|
+
return await sendMessage(session, `**【@${username}】**
|
|
898
782
|
棋盘编辑成功!`);
|
|
899
783
|
});
|
|
900
784
|
ctx.command("cchess.编辑棋盘.导出", "导出FEN串").action(async ({ session }) => {
|
|
@@ -902,48 +786,43 @@ https://www.chessdb.cn/egtb_info.html`);
|
|
|
902
786
|
await updateNameInPlayerRecord(userId, username);
|
|
903
787
|
const gameRecord = await getGameRecord(channelId);
|
|
904
788
|
if (!gameRecord.isStarted) {
|
|
905
|
-
return await sendMessage(session,
|
|
789
|
+
return await sendMessage(session, `**【@${username}】**
|
|
906
790
|
游戏还未开始哦!`);
|
|
907
791
|
}
|
|
908
792
|
if (gameRecord.isAnalyzing) {
|
|
909
|
-
return await sendMessage(session,
|
|
793
|
+
return await sendMessage(session, `**【@${username}】**
|
|
910
794
|
对局分析中,请稍后再试!`);
|
|
911
795
|
}
|
|
912
796
|
await checkEngine(channelId);
|
|
913
|
-
if (engines[channelId] && !gameRecord.isEngineReady) {
|
|
914
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
915
|
-
isEngineReady: true
|
|
916
|
-
});
|
|
917
|
-
}
|
|
918
797
|
const fenWithFullMove = await getFenWithFullMove(channelId);
|
|
919
|
-
return await sendMessage(session,
|
|
798
|
+
return await sendMessage(session, `**【@${username}】**
|
|
920
799
|
${fenWithFullMove}`);
|
|
921
800
|
});
|
|
922
801
|
ctx.command("cchess.编辑棋盘.使用方法", "查看编辑棋盘的fen使用方法").action(async ({ session }) => {
|
|
923
802
|
const { username, userId, channelId } = session;
|
|
924
803
|
await updateNameInPlayerRecord(userId, username);
|
|
925
804
|
await checkEngine(channelId);
|
|
926
|
-
return await sendMessage(session,
|
|
805
|
+
return await sendMessage(session, `**【@${username}】**
|
|
927
806
|
https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
928
807
|
});
|
|
929
808
|
ctx.command("cchess.排行榜.总胜场 [number:number]", "查看玩家总胜场排行榜").action(async ({ session }, number = config.defaultMaxLeaderboardEntries) => {
|
|
930
|
-
const {
|
|
809
|
+
const { userId, username } = session;
|
|
931
810
|
await updateNameInPlayerRecord(userId, username);
|
|
932
811
|
if (typeof number !== "number" || isNaN(number) || number < 0) {
|
|
933
812
|
return "请输入大于等于 0 的数字作为排行榜的参数。";
|
|
934
813
|
}
|
|
935
|
-
return await getLeaderboard(session, "win", "
|
|
814
|
+
return await getLeaderboard(session, "win", "玩家总胜场排行榜", number);
|
|
936
815
|
});
|
|
937
816
|
ctx.command("cchess.排行榜.总输场 [number:number]", "查看玩家总输场排行榜").action(async ({ session }, number = config.defaultMaxLeaderboardEntries) => {
|
|
938
|
-
const {
|
|
817
|
+
const { userId, username } = session;
|
|
939
818
|
await updateNameInPlayerRecord(userId, username);
|
|
940
819
|
if (typeof number !== "number" || isNaN(number) || number < 0) {
|
|
941
820
|
return "请输入大于等于 0 的数字作为排行榜的参数。";
|
|
942
821
|
}
|
|
943
|
-
return await getLeaderboard(session, "lose", "
|
|
822
|
+
return await getLeaderboard(session, "lose", "查看玩家总输场排行榜", number);
|
|
944
823
|
});
|
|
945
824
|
ctx.command("cchess.查询玩家记录 [targetUser:text]", "查询玩家记录").action(async ({ session }, targetUser) => {
|
|
946
|
-
let {
|
|
825
|
+
let { userId, username } = session;
|
|
947
826
|
await updateNameInPlayerRecord(userId, username);
|
|
948
827
|
if (targetUser) {
|
|
949
828
|
targetUser = await replaceAtTags(session, targetUser);
|
|
@@ -960,12 +839,12 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
960
839
|
lose: 0,
|
|
961
840
|
win: 0
|
|
962
841
|
});
|
|
963
|
-
return sendMessage(session,
|
|
842
|
+
return sendMessage(session, `**【@${session.username}】**
|
|
964
843
|
查询对象:${username}
|
|
965
844
|
无任何游戏记录。`);
|
|
966
845
|
}
|
|
967
846
|
const { win, lose } = targetUserRecord[0];
|
|
968
|
-
return sendMessage(session,
|
|
847
|
+
return sendMessage(session, `**【@${session.username}】**
|
|
969
848
|
查询对象:${username}
|
|
970
849
|
总胜场次数为:${win} 次
|
|
971
850
|
总输场次数为:${lose} 次
|
|
@@ -982,11 +861,7 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
982
861
|
try {
|
|
983
862
|
guildMember = await session.bot.getGuildMember(session.guildId, userId);
|
|
984
863
|
} catch (error) {
|
|
985
|
-
guildMember = {
|
|
986
|
-
user: {
|
|
987
|
-
name: "未知用户"
|
|
988
|
-
}
|
|
989
|
-
};
|
|
864
|
+
guildMember = { user: { name: "未知用户" } };
|
|
990
865
|
}
|
|
991
866
|
const newAtTag = `<at id="${userId}" name="${guildMember.user.name}"/>`;
|
|
992
867
|
content = content.replace(match[0], newAtTag);
|
|
@@ -1014,11 +889,11 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1014
889
|
}
|
|
1015
890
|
}
|
|
1016
891
|
__name(updatePlayerRecords, "updatePlayerRecords");
|
|
1017
|
-
async function getLeaderboard(session,
|
|
892
|
+
async function getLeaderboard(session, sortField, title, number) {
|
|
1018
893
|
const getPlayers = await ctx.database.get("cchess_player_records", {});
|
|
1019
894
|
const sortedPlayers = getPlayers.sort((a, b) => b[sortField] - a[sortField]);
|
|
1020
895
|
const topPlayers = sortedPlayers.slice(0, number);
|
|
1021
|
-
let result =
|
|
896
|
+
let result = `### ${title}:
|
|
1022
897
|
`;
|
|
1023
898
|
topPlayers.forEach((player, index) => {
|
|
1024
899
|
result += `${index + 1}. ${player.username}:${player[sortField]} 次
|
|
@@ -1030,53 +905,36 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1030
905
|
async function updateNameInPlayerRecord(userId, username) {
|
|
1031
906
|
const userRecord = await ctx.database.get("cchess_player_records", { userId });
|
|
1032
907
|
if (userRecord.length === 0) {
|
|
1033
|
-
await ctx.database.create("cchess_player_records", {
|
|
1034
|
-
userId,
|
|
1035
|
-
username
|
|
1036
|
-
});
|
|
908
|
+
await ctx.database.create("cchess_player_records", { userId, username });
|
|
1037
909
|
return;
|
|
1038
910
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
if (username !== existingRecord.username) {
|
|
1042
|
-
existingRecord.username = username;
|
|
1043
|
-
isChange = true;
|
|
1044
|
-
}
|
|
1045
|
-
if (isChange) {
|
|
1046
|
-
await ctx.database.set("cchess_player_records", { userId }, {
|
|
1047
|
-
username: existingRecord.username
|
|
1048
|
-
});
|
|
911
|
+
if (username !== userRecord[0].username) {
|
|
912
|
+
await ctx.database.set("cchess_player_records", { userId }, { username });
|
|
1049
913
|
}
|
|
1050
914
|
}
|
|
1051
915
|
__name(updateNameInPlayerRecord, "updateNameInPlayerRecord");
|
|
1052
916
|
async function checkEngine(channelId) {
|
|
1053
917
|
if (!engines[channelId]) {
|
|
1054
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
1055
|
-
isEngineReady: false
|
|
1056
|
-
});
|
|
918
|
+
await ctx.database.set("cchess_game_records", { channelId }, { isEngineReady: false });
|
|
1057
919
|
await initEngine(channelId);
|
|
1058
920
|
while (true) {
|
|
1059
921
|
const gameRecord = await getGameRecord(channelId);
|
|
1060
|
-
if (gameRecord.isEngineReady)
|
|
1061
|
-
break;
|
|
1062
|
-
}
|
|
922
|
+
if (gameRecord.isEngineReady) break;
|
|
1063
923
|
await (0, import_koishi.sleep)(500);
|
|
1064
924
|
}
|
|
1065
925
|
}
|
|
1066
926
|
}
|
|
1067
927
|
__name(checkEngine, "checkEngine");
|
|
1068
|
-
async function getFenWithFullMove(channelId,
|
|
928
|
+
async function getFenWithFullMove(channelId, separator = " ") {
|
|
1069
929
|
const gameRecord = await getGameRecord(channelId);
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
let n = startFen;
|
|
1079
|
-
return e.length > 0 && (n += " moves " + e.join(t)), n;
|
|
930
|
+
const { startFen, moveList } = gameRecord;
|
|
931
|
+
let moves = [];
|
|
932
|
+
for (let move of moveList) moves.push(move.move);
|
|
933
|
+
let fullFen = startFen;
|
|
934
|
+
if (moves.length > 0) {
|
|
935
|
+
fullFen += " moves " + moves.join(separator);
|
|
936
|
+
}
|
|
937
|
+
return fullFen;
|
|
1080
938
|
}
|
|
1081
939
|
__name(getFenWithFullMove, "getFenWithFullMove");
|
|
1082
940
|
function fenToTurn(t) {
|
|
@@ -1084,57 +942,55 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1084
942
|
return "b" == e[1] ? "b" : "w";
|
|
1085
943
|
}
|
|
1086
944
|
__name(fenToTurn, "fenToTurn");
|
|
1087
|
-
function isValidateFen(
|
|
1088
|
-
let
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
for (let
|
|
1095
|
-
let
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
945
|
+
function isValidateFen(fen) {
|
|
946
|
+
let parts = fen.split(" ");
|
|
947
|
+
let boardPart = parts[0];
|
|
948
|
+
let turnPart = parts[1];
|
|
949
|
+
if (!"rwb".includes(turnPart.toLowerCase())) return false;
|
|
950
|
+
let rows = boardPart.split("/");
|
|
951
|
+
if (rows.length != 10) return false;
|
|
952
|
+
for (let i = 0; i < rows.length; i++) {
|
|
953
|
+
let row = rows[i];
|
|
954
|
+
let count = 0;
|
|
955
|
+
for (let j = 0; j < row.length; j++) {
|
|
956
|
+
let char = row.charAt(j);
|
|
957
|
+
if (char >= "0" && char <= "9") {
|
|
958
|
+
count += parseInt(char);
|
|
959
|
+
} else {
|
|
960
|
+
count++;
|
|
961
|
+
}
|
|
1099
962
|
}
|
|
1100
|
-
if (
|
|
1101
|
-
return false;
|
|
963
|
+
if (count != 9) return false;
|
|
1102
964
|
}
|
|
1103
965
|
return true;
|
|
1104
966
|
}
|
|
1105
967
|
__name(isValidateFen, "isValidateFen");
|
|
1106
|
-
async function parseFen(channelId,
|
|
1107
|
-
const
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
for (let i = 3; i < e.length; i++)
|
|
1122
|
-
await makeMoveByString(channelId, e[i]);
|
|
1123
|
-
else {
|
|
1124
|
-
let t2 = e[3];
|
|
1125
|
-
for (let e2 = 0; e2 < t2.length; e2 += 4)
|
|
1126
|
-
await makeMoveByString(channelId, t2.substring(e2, e2 + 4));
|
|
968
|
+
async function parseFen(channelId, fenString) {
|
|
969
|
+
const parts = fenString.split(" ");
|
|
970
|
+
const board = fenToBoard(parts[0]);
|
|
971
|
+
const turn = "b" == parts[1] ? "b" : "w";
|
|
972
|
+
await ctx.database.set("cchess_game_records", { channelId }, { turn, board });
|
|
973
|
+
if (parts.length > 3 && "moves" == parts[2]) {
|
|
974
|
+
if (parts[3].length == 4) {
|
|
975
|
+
for (let i = 3; i < parts.length; i++) {
|
|
976
|
+
await makeMoveByString(channelId, parts[i]);
|
|
977
|
+
}
|
|
978
|
+
} else {
|
|
979
|
+
let movesStr = parts[3];
|
|
980
|
+
for (let i = 0; i < movesStr.length; i += 4) {
|
|
981
|
+
await makeMoveByString(channelId, movesStr.substring(i, i + 4));
|
|
982
|
+
}
|
|
1127
983
|
}
|
|
984
|
+
}
|
|
1128
985
|
}
|
|
1129
986
|
__name(parseFen, "parseFen");
|
|
1130
|
-
function deepCopy(
|
|
1131
|
-
return JSON.parse(JSON.stringify(
|
|
987
|
+
function deepCopy(obj) {
|
|
988
|
+
return JSON.parse(JSON.stringify(obj));
|
|
1132
989
|
}
|
|
1133
990
|
__name(deepCopy, "deepCopy");
|
|
1134
|
-
async function gotoHistory(channelId,
|
|
991
|
+
async function gotoHistory(channelId, targetMove) {
|
|
1135
992
|
const gameRecord = await getGameRecord(channelId);
|
|
1136
993
|
let {
|
|
1137
|
-
fen,
|
|
1138
994
|
turn,
|
|
1139
995
|
board,
|
|
1140
996
|
moveList,
|
|
@@ -1145,19 +1001,35 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1145
1001
|
isHistoryMode,
|
|
1146
1002
|
originalState
|
|
1147
1003
|
} = gameRecord;
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1004
|
+
if (!isHistoryMode) {
|
|
1005
|
+
originalState = {
|
|
1006
|
+
fen: getFen(board, turn),
|
|
1007
|
+
turn,
|
|
1008
|
+
board: deepCopy(board),
|
|
1009
|
+
moveList: deepCopy(moveList),
|
|
1010
|
+
movesAfterLastEat: deepCopy(movesAfterLastEat),
|
|
1011
|
+
boardAfterLastEat: deepCopy(boardAfterLastEat),
|
|
1012
|
+
turnAfterLastEat,
|
|
1013
|
+
lastMove
|
|
1014
|
+
};
|
|
1015
|
+
isHistoryMode = true;
|
|
1016
|
+
}
|
|
1017
|
+
let moveId = targetMove.moveId;
|
|
1018
|
+
let idx = originalState.moveList.findIndex((m) => m.moveId == moveId);
|
|
1019
|
+
moveList = originalState.moveList.slice(0, idx + 1);
|
|
1020
|
+
idx = originalState.movesAfterLastEat.findIndex((m) => m.moveId == moveId);
|
|
1021
|
+
if (idx != -1) {
|
|
1022
|
+
movesAfterLastEat = originalState.movesAfterLastEat.slice(0, idx + 1);
|
|
1023
|
+
} else {
|
|
1024
|
+
boardAfterLastEat = fenToBoard(targetMove.fen);
|
|
1025
|
+
turnAfterLastEat = "w" == targetMove.side ? "b" : "w";
|
|
1026
|
+
movesAfterLastEat = [];
|
|
1027
|
+
}
|
|
1028
|
+
lastMove = targetMove.moveCord;
|
|
1029
|
+
turn = "w" == targetMove.side ? "b" : "w";
|
|
1030
|
+
board = fenToBoard(targetMove.fen);
|
|
1031
|
+
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
1032
|
+
fen: targetMove.fen,
|
|
1161
1033
|
turn,
|
|
1162
1034
|
board,
|
|
1163
1035
|
moveList,
|
|
@@ -1185,16 +1057,26 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1185
1057
|
originalState,
|
|
1186
1058
|
startFen
|
|
1187
1059
|
} = gameRecord;
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1060
|
+
if (!isHistoryMode) {
|
|
1061
|
+
originalState = {
|
|
1062
|
+
fen: getFen(board, turn),
|
|
1063
|
+
turn,
|
|
1064
|
+
board: deepCopy(board),
|
|
1065
|
+
moveList: deepCopy(moveList),
|
|
1066
|
+
movesAfterLastEat: deepCopy(movesAfterLastEat),
|
|
1067
|
+
boardAfterLastEat: deepCopy(boardAfterLastEat),
|
|
1068
|
+
turnAfterLastEat,
|
|
1069
|
+
lastMove
|
|
1070
|
+
};
|
|
1071
|
+
isHistoryMode = true;
|
|
1072
|
+
}
|
|
1073
|
+
await parseFen(channelId, startFen);
|
|
1074
|
+
moveList = [];
|
|
1075
|
+
movesAfterLastEat = [];
|
|
1076
|
+
boardAfterLastEat = deepCopy(fenToBoard(fen));
|
|
1077
|
+
turnAfterLastEat = turn;
|
|
1078
|
+
lastMove = null;
|
|
1079
|
+
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
1198
1080
|
moveList,
|
|
1199
1081
|
movesAfterLastEat,
|
|
1200
1082
|
boardAfterLastEat,
|
|
@@ -1208,7 +1090,6 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1208
1090
|
async function undoMove(channelId) {
|
|
1209
1091
|
const gameRecord = await getGameRecord(channelId);
|
|
1210
1092
|
let {
|
|
1211
|
-
fen,
|
|
1212
1093
|
turn,
|
|
1213
1094
|
board,
|
|
1214
1095
|
moveList,
|
|
@@ -1219,23 +1100,26 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1219
1100
|
isHistoryMode,
|
|
1220
1101
|
originalState
|
|
1221
1102
|
} = gameRecord;
|
|
1222
|
-
if (isHistoryMode
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
isHistoryMode
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1103
|
+
if (!isHistoryMode) {
|
|
1104
|
+
originalState = {
|
|
1105
|
+
fen: getFen(board, turn),
|
|
1106
|
+
turn,
|
|
1107
|
+
board: deepCopy(board),
|
|
1108
|
+
moveList: deepCopy(moveList),
|
|
1109
|
+
movesAfterLastEat: deepCopy(movesAfterLastEat),
|
|
1110
|
+
boardAfterLastEat: deepCopy(boardAfterLastEat),
|
|
1111
|
+
turnAfterLastEat,
|
|
1112
|
+
lastMove
|
|
1113
|
+
};
|
|
1114
|
+
isHistoryMode = true;
|
|
1115
|
+
}
|
|
1116
|
+
await ctx.database.set("cchess_game_records", { channelId }, { originalState, isHistoryMode });
|
|
1117
|
+
if (moveList.length > 1) {
|
|
1118
|
+
let target = moveList[moveList.length - 2];
|
|
1119
|
+
await gotoHistory(channelId, target);
|
|
1120
|
+
} else {
|
|
1238
1121
|
await gotoStart(channelId);
|
|
1122
|
+
}
|
|
1239
1123
|
}
|
|
1240
1124
|
__name(undoMove, "undoMove");
|
|
1241
1125
|
async function endGame(channelId) {
|
|
@@ -1243,22 +1127,14 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1243
1127
|
await ctx.database.remove("cchess_gaming_player_records", { channelId });
|
|
1244
1128
|
}
|
|
1245
1129
|
__name(endGame, "endGame");
|
|
1246
|
-
async function onEngineBestMove(channelId,
|
|
1130
|
+
async function onEngineBestMove(channelId, move, ponder) {
|
|
1247
1131
|
const gameRecord = await getGameRecord(channelId);
|
|
1248
|
-
let {
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
// bestMoveHint,
|
|
1252
|
-
board,
|
|
1253
|
-
turn
|
|
1254
|
-
} = gameRecord;
|
|
1255
|
-
if (!t) {
|
|
1256
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
1257
|
-
isAnalyzing: false
|
|
1258
|
-
});
|
|
1132
|
+
let { isEnginePlayRed, isEnginePlayBlack, board, turn } = gameRecord;
|
|
1133
|
+
if (!move) {
|
|
1134
|
+
await ctx.database.set("cchess_game_records", { channelId }, { isAnalyzing: false });
|
|
1259
1135
|
return;
|
|
1260
1136
|
}
|
|
1261
|
-
if ("(none)" ===
|
|
1137
|
+
if ("(none)" === move) {
|
|
1262
1138
|
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
1263
1139
|
winSide: turn === "w" ? "b" : "w",
|
|
1264
1140
|
loseSide: turn,
|
|
@@ -1266,8 +1142,8 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1266
1142
|
});
|
|
1267
1143
|
return;
|
|
1268
1144
|
}
|
|
1269
|
-
let
|
|
1270
|
-
if (
|
|
1145
|
+
let side = getSideByMoveString(board, move);
|
|
1146
|
+
if (side !== turn && isEnginePlayRed || side !== turn && isEnginePlayBlack) {
|
|
1271
1147
|
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
1272
1148
|
winSide: turn,
|
|
1273
1149
|
loseSide: turn === "w" ? "b" : "w",
|
|
@@ -1275,213 +1151,87 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1275
1151
|
});
|
|
1276
1152
|
return;
|
|
1277
1153
|
}
|
|
1278
|
-
("w" ===
|
|
1279
|
-
|
|
1280
|
-
// bestMoveHint,
|
|
1281
|
-
isAnalyzing: false
|
|
1282
|
-
});
|
|
1283
|
-
}
|
|
1284
|
-
__name(onEngineBestMove, "onEngineBestMove");
|
|
1285
|
-
function getLocalMoveName(t, e) {
|
|
1286
|
-
let i = {};
|
|
1287
|
-
if ("zh-CN" === e)
|
|
1288
|
-
return t;
|
|
1289
|
-
if ("en-US" === e)
|
|
1290
|
-
i = {
|
|
1291
|
-
"进": "+",
|
|
1292
|
-
"退": "-",
|
|
1293
|
-
"平": "=",
|
|
1294
|
-
"前": "+",
|
|
1295
|
-
"后": "-",
|
|
1296
|
-
"中": "=",
|
|
1297
|
-
"将": "k",
|
|
1298
|
-
"帅": "K",
|
|
1299
|
-
"士": "a",
|
|
1300
|
-
"仕": "A",
|
|
1301
|
-
"象": "b",
|
|
1302
|
-
"相": "B",
|
|
1303
|
-
"车": "r",
|
|
1304
|
-
"马": "n",
|
|
1305
|
-
"炮": "c",
|
|
1306
|
-
"卒": "p",
|
|
1307
|
-
"兵": "P"
|
|
1308
|
-
};
|
|
1309
|
-
else {
|
|
1310
|
-
if ("vi-VN" !== e)
|
|
1311
|
-
return t;
|
|
1312
|
-
i = {
|
|
1313
|
-
"进": ".",
|
|
1314
|
-
"退": "/",
|
|
1315
|
-
"平": "-",
|
|
1316
|
-
"前": ".",
|
|
1317
|
-
"后": "/",
|
|
1318
|
-
"中": "-",
|
|
1319
|
-
"将": "Tg",
|
|
1320
|
-
"帅": "Tg",
|
|
1321
|
-
"士": "S",
|
|
1322
|
-
"仕": "S",
|
|
1323
|
-
"象": "T",
|
|
1324
|
-
"相": "T",
|
|
1325
|
-
"车": "X",
|
|
1326
|
-
"马": "M",
|
|
1327
|
-
"炮": "P",
|
|
1328
|
-
"卒": "B",
|
|
1329
|
-
"兵": "B"
|
|
1330
|
-
};
|
|
1331
|
-
}
|
|
1332
|
-
let n = "一二三四五六七八九", A = "123456789", s = "123456789", a = "", o = -1 !== A.indexOf(t[3]) ? "b" : "w";
|
|
1333
|
-
for (let r of t) {
|
|
1334
|
-
let t2 = n.indexOf(r), e2 = A.indexOf(r);
|
|
1335
|
-
a += r in i ? i[r] : -1 !== t2 ? s[t2] : -1 !== e2 ? s[e2] : r;
|
|
1154
|
+
if ("w" === side && isEnginePlayRed || "b" === side && isEnginePlayBlack) {
|
|
1155
|
+
await makeMoveByString(channelId, move);
|
|
1336
1156
|
}
|
|
1337
|
-
|
|
1157
|
+
await ctx.database.set("cchess_game_records", { channelId }, { isAnalyzing: false });
|
|
1338
1158
|
}
|
|
1339
|
-
__name(
|
|
1340
|
-
async function
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
return i.join(" ");
|
|
1348
|
-
}
|
|
1349
|
-
__name(getSerialMoveName, "getSerialMoveName");
|
|
1350
|
-
async function go(channelId, t = -1, e = -1, i = -1) {
|
|
1351
|
-
{
|
|
1352
|
-
let n = "go";
|
|
1353
|
-
return t > 0 && (n += " movetime " + t), e > 0 && (n += " depth " + e), i > 0 && (n += " nodes " + i), await sendCommand(channelId, n), true;
|
|
1354
|
-
}
|
|
1159
|
+
__name(onEngineBestMove, "onEngineBestMove");
|
|
1160
|
+
async function go(channelId, movetime = -1, depth = -1, nodes = -1) {
|
|
1161
|
+
let cmd = "go";
|
|
1162
|
+
if (movetime > 0) cmd += " movetime " + movetime;
|
|
1163
|
+
if (depth > 0) cmd += " depth " + depth;
|
|
1164
|
+
if (nodes > 0) cmd += " nodes " + nodes;
|
|
1165
|
+
await sendCommand(channelId, cmd);
|
|
1166
|
+
return true;
|
|
1355
1167
|
}
|
|
1356
1168
|
__name(go, "go");
|
|
1357
1169
|
async function engineAction(channelId) {
|
|
1358
1170
|
const gameRecord = await getGameRecord(channelId);
|
|
1359
|
-
let {
|
|
1360
|
-
|
|
1361
|
-
isEnginePlayBlack,
|
|
1362
|
-
turn,
|
|
1363
|
-
movesAfterLastEat,
|
|
1364
|
-
boardAfterLastEat,
|
|
1365
|
-
turnAfterLastEat
|
|
1366
|
-
// multiPvInfoBuffer
|
|
1367
|
-
} = gameRecord;
|
|
1368
|
-
await sendCommand(channelId, "fen " + getFenWithMove(movesAfterLastEat, boardAfterLastEat, turnAfterLastEat)), // multiPvInfoBuffer = {},
|
|
1171
|
+
let { movesAfterLastEat, boardAfterLastEat, turnAfterLastEat } = gameRecord;
|
|
1172
|
+
await sendCommand(channelId, "fen " + getFenWithMove(movesAfterLastEat, boardAfterLastEat, turnAfterLastEat));
|
|
1369
1173
|
await go(channelId, 1e3 * thinkingSettings["movetime"], thinkingSettings["depth"], -1);
|
|
1370
1174
|
}
|
|
1371
1175
|
__name(engineAction, "engineAction");
|
|
1372
|
-
function moveStringToPos(
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
e: 4,
|
|
1379
|
-
f: 5,
|
|
1380
|
-
g: 6,
|
|
1381
|
-
h: 7,
|
|
1382
|
-
i: 8
|
|
1383
|
-
}, i = {
|
|
1384
|
-
0: 9,
|
|
1385
|
-
1: 8,
|
|
1386
|
-
2: 7,
|
|
1387
|
-
3: 6,
|
|
1388
|
-
4: 5,
|
|
1389
|
-
5: 4,
|
|
1390
|
-
6: 3,
|
|
1391
|
-
7: 2,
|
|
1392
|
-
8: 1,
|
|
1393
|
-
9: 0
|
|
1394
|
-
}, n = [e[t[0]], i[t[1]]], A = [e[t[2]], i[t[3]]];
|
|
1395
|
-
return [n, A];
|
|
1176
|
+
function moveStringToPos(moveStr) {
|
|
1177
|
+
const xMap = { a: 0, b: 1, c: 2, d: 3, e: 4, f: 5, g: 6, h: 7, i: 8 };
|
|
1178
|
+
const yMap = { 0: 9, 1: 8, 2: 7, 3: 6, 4: 5, 5: 4, 6: 3, 7: 2, 8: 1, 9: 0 };
|
|
1179
|
+
const from = [xMap[moveStr[0]], yMap[moveStr[1]]];
|
|
1180
|
+
const to = [xMap[moveStr[2]], yMap[moveStr[3]]];
|
|
1181
|
+
return [from, to];
|
|
1396
1182
|
}
|
|
1397
1183
|
__name(moveStringToPos, "moveStringToPos");
|
|
1398
|
-
async function makeMoveByString(channelId,
|
|
1399
|
-
let
|
|
1400
|
-
await makeMove(channelId,
|
|
1184
|
+
async function makeMoveByString(channelId, moveStr) {
|
|
1185
|
+
let [from, to] = moveStringToPos(moveStr);
|
|
1186
|
+
await makeMove(channelId, from, to);
|
|
1401
1187
|
}
|
|
1402
1188
|
__name(makeMoveByString, "makeMoveByString");
|
|
1403
|
-
function getSideByMoveString(board,
|
|
1404
|
-
let
|
|
1405
|
-
return getSide(board[
|
|
1189
|
+
function getSideByMoveString(board, moveStr) {
|
|
1190
|
+
let [from, to] = moveStringToPos(moveStr);
|
|
1191
|
+
return getSide(board[from[1]][from[0]]);
|
|
1406
1192
|
}
|
|
1407
1193
|
__name(getSideByMoveString, "getSideByMoveString");
|
|
1408
1194
|
function getFenWithMove(movesAfterLastEat, boardAfterLastEat, turnAfterLastEat) {
|
|
1409
|
-
let
|
|
1410
|
-
for (let
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
return
|
|
1195
|
+
let moves = [];
|
|
1196
|
+
for (let m of movesAfterLastEat) moves.push(m.move);
|
|
1197
|
+
let fen = boardToFen(boardAfterLastEat, "w", turnAfterLastEat);
|
|
1198
|
+
if (moves.length > 0) fen += " moves " + moves.join(" ");
|
|
1199
|
+
return fen;
|
|
1414
1200
|
}
|
|
1415
1201
|
__name(getFenWithMove, "getFenWithMove");
|
|
1416
|
-
async function setOption(channelId,
|
|
1417
|
-
await sendCommand(channelId, "setoption name " +
|
|
1202
|
+
async function setOption(channelId, name2, value) {
|
|
1203
|
+
await sendCommand(channelId, "setoption name " + name2 + " value " + value);
|
|
1418
1204
|
}
|
|
1419
1205
|
__name(setOption, "setOption");
|
|
1420
|
-
async function setOptionList(channelId,
|
|
1421
|
-
for (let
|
|
1422
|
-
await setOption(channelId,
|
|
1206
|
+
async function setOptionList(channelId, options) {
|
|
1207
|
+
for (let key in options)
|
|
1208
|
+
await setOption(channelId, key, options[key]);
|
|
1423
1209
|
}
|
|
1424
1210
|
__name(setOptionList, "setOptionList");
|
|
1425
|
-
async function sendCommand(channelId,
|
|
1211
|
+
async function sendCommand(channelId, cmd) {
|
|
1426
1212
|
const gameRecord = await getGameRecord(channelId);
|
|
1427
1213
|
await checkEngine(channelId);
|
|
1428
1214
|
if (engines[channelId] && !gameRecord.isEngineReady) {
|
|
1429
|
-
await ctx.database.set("cchess_game_records", { channelId }, {
|
|
1430
|
-
isEngineReady: true
|
|
1431
|
-
});
|
|
1215
|
+
await ctx.database.set("cchess_game_records", { channelId }, { isEngineReady: true });
|
|
1432
1216
|
}
|
|
1433
|
-
if (
|
|
1434
|
-
engines[channelId].send_command(
|
|
1217
|
+
if (engines[channelId] || "uci" == cmd) {
|
|
1218
|
+
engines[channelId].send_command(cmd);
|
|
1435
1219
|
} else {
|
|
1436
|
-
await sendCommand(channelId,
|
|
1220
|
+
await sendCommand(channelId, cmd);
|
|
1437
1221
|
}
|
|
1438
1222
|
}
|
|
1439
1223
|
__name(sendCommand, "sendCommand");
|
|
1440
|
-
async function receiveOutput(channelId,
|
|
1224
|
+
async function receiveOutput(channelId, output) {
|
|
1441
1225
|
try {
|
|
1442
|
-
if (!
|
|
1443
|
-
|
|
1444
|
-
let
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
let infoObj = {};
|
|
1449
|
-
for (let j = 1; j < e.length; j++) {
|
|
1450
|
-
switch (e[j]) {
|
|
1451
|
-
case "pv":
|
|
1452
|
-
case "string":
|
|
1453
|
-
infoObj[e[j]] = e.slice(j + 1).join(" ");
|
|
1454
|
-
break;
|
|
1455
|
-
case "score":
|
|
1456
|
-
if (e[j + 1] === "cp") {
|
|
1457
|
-
infoObj[e[j]] = { cp: parseInt(e[j + 2]) };
|
|
1458
|
-
j += 2;
|
|
1459
|
-
} else if (e[j + 1] === "mate") {
|
|
1460
|
-
infoObj[e[j]] = { mate: parseInt(e[j + 2]) };
|
|
1461
|
-
j += 2;
|
|
1462
|
-
} else {
|
|
1463
|
-
infoObj[e[j]] = { cp: parseInt(e[j + 1]) };
|
|
1464
|
-
j++;
|
|
1465
|
-
}
|
|
1466
|
-
break;
|
|
1467
|
-
default:
|
|
1468
|
-
if (e.length > j + 1 && keywords.includes(e[j])) {
|
|
1469
|
-
infoObj[e[j]] = parseInt(e[j + 1]);
|
|
1470
|
-
j++;
|
|
1471
|
-
} else {
|
|
1472
|
-
infoObj[e[j]] = "";
|
|
1473
|
-
}
|
|
1474
|
-
}
|
|
1475
|
-
}
|
|
1476
|
-
if ("pv" in infoObj) {
|
|
1477
|
-
}
|
|
1478
|
-
} else if (i === "bestmove") {
|
|
1479
|
-
if (e.length === 4 && e[2] === "ponder") {
|
|
1480
|
-
await onEngineBestMove(channelId, e[1], e[3]);
|
|
1226
|
+
if (!output) return;
|
|
1227
|
+
let parts = output.split(" ");
|
|
1228
|
+
let command = parts[0];
|
|
1229
|
+
if (command === "bestmove") {
|
|
1230
|
+
if (parts.length === 4 && parts[2] === "ponder") {
|
|
1231
|
+
await onEngineBestMove(channelId, parts[1], parts[3]);
|
|
1481
1232
|
} else {
|
|
1482
|
-
await onEngineBestMove(channelId,
|
|
1233
|
+
await onEngineBestMove(channelId, parts[1], null);
|
|
1483
1234
|
}
|
|
1484
|
-
} else if (i === "option" || i === "uciok") {
|
|
1485
1235
|
}
|
|
1486
1236
|
} catch (error) {
|
|
1487
1237
|
logger.error(error);
|
|
@@ -1498,17 +1248,36 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1498
1248
|
} else if (wasmType !== void 0) {
|
|
1499
1249
|
const wasmOrigin = origin;
|
|
1500
1250
|
const wasmScriptPath = path.join(wasmOrigin, "assets", "wasm", "pikafish.js");
|
|
1251
|
+
const wasmBinaryPath = path.join(wasmOrigin, "assets", "wasm", "pikafish.wasm");
|
|
1501
1252
|
const Pikafish = require(wasmScriptPath);
|
|
1253
|
+
let wasmBinary;
|
|
1254
|
+
try {
|
|
1255
|
+
wasmBinary = fs.readFileSync(wasmBinaryPath);
|
|
1256
|
+
} catch (e) {
|
|
1257
|
+
console.error(`无法读取 WASM 文件,请检查路径: ${wasmBinaryPath}`, e);
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1502
1260
|
const instance = await Pikafish({
|
|
1503
|
-
|
|
1504
|
-
|
|
1261
|
+
// 显式传入二进制数据
|
|
1262
|
+
wasmBinary,
|
|
1263
|
+
locateFile: /* @__PURE__ */ __name((file) => {
|
|
1264
|
+
if (file.endsWith(".data")) {
|
|
1505
1265
|
return path.join(wasmOrigin, "assets", "wasm", "data", file);
|
|
1266
|
+
} else if (file.endsWith(".wasm")) {
|
|
1267
|
+
return path.join(wasmOrigin, "assets", "wasm", file);
|
|
1506
1268
|
} else {
|
|
1507
1269
|
return path.join(wasmOrigin, "assets", "wasm", file);
|
|
1508
1270
|
}
|
|
1509
|
-
},
|
|
1510
|
-
|
|
1511
|
-
|
|
1271
|
+
}, "locateFile"),
|
|
1272
|
+
// 打印错误日志以便调试
|
|
1273
|
+
print: /* @__PURE__ */ __name((text) => {
|
|
1274
|
+
console.log("[Pikafish stdout]:", text);
|
|
1275
|
+
}, "print"),
|
|
1276
|
+
printErr: /* @__PURE__ */ __name((text) => {
|
|
1277
|
+
console.error("[Pikafish stderr]:", text);
|
|
1278
|
+
}, "printErr"),
|
|
1279
|
+
setStatus: /* @__PURE__ */ __name((status) => {
|
|
1280
|
+
}, "setStatus")
|
|
1512
1281
|
});
|
|
1513
1282
|
engines[channelId] = instance;
|
|
1514
1283
|
engines[channelId].read_stdout = async (stdout) => {
|
|
@@ -1594,13 +1363,11 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1594
1363
|
const figureNamesRegex = new RegExp(possibleFigureNames.join("|"));
|
|
1595
1364
|
const actionRegex = new RegExp(possibleActions.join("|"));
|
|
1596
1365
|
const positionRegex = new RegExp(possiblePositions.join("|"), "g");
|
|
1597
|
-
if (move.length !== 4)
|
|
1598
|
-
return false;
|
|
1599
|
-
}
|
|
1366
|
+
if (move.length !== 4) return false;
|
|
1600
1367
|
const figureNameMatches = move.match(figureNamesRegex);
|
|
1601
1368
|
const actionMatches = move.match(actionRegex);
|
|
1602
1369
|
const positionMatches = move.match(positionRegex);
|
|
1603
|
-
return figureNameMatches && figureNameMatches.length === 1 && actionMatches && actionMatches.length === 1 && positionMatches && positionMatches.length >= 1 && positionMatches.length <= 2;
|
|
1370
|
+
return !!(figureNameMatches && figureNameMatches.length === 1 && actionMatches && actionMatches.length === 1 && positionMatches && positionMatches.length >= 1 && positionMatches.length <= 2);
|
|
1604
1371
|
}
|
|
1605
1372
|
__name(isValidFigureMoveName, "isValidFigureMoveName");
|
|
1606
1373
|
function convertTurnToString(turn) {
|
|
@@ -1609,6 +1376,7 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1609
1376
|
} else if (turn === "b") {
|
|
1610
1377
|
return "黑方";
|
|
1611
1378
|
}
|
|
1379
|
+
return "";
|
|
1612
1380
|
}
|
|
1613
1381
|
__name(convertTurnToString, "convertTurnToString");
|
|
1614
1382
|
async function createImage(buffer) {
|
|
@@ -1623,9 +1391,7 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1623
1391
|
__name(createImage, "createImage");
|
|
1624
1392
|
function isMoveString(str) {
|
|
1625
1393
|
const boardLetters = ["a", "b", "c", "d", "e", "f", "g", "h", "i"];
|
|
1626
|
-
if (str.length !== 4)
|
|
1627
|
-
return false;
|
|
1628
|
-
}
|
|
1394
|
+
if (str.length !== 4) return false;
|
|
1629
1395
|
return boardLetters.includes(str[0]) && boardLetters.includes(str[2]) && parseInt(str[1]) >= 0 && parseInt(str[1]) <= 9 && parseInt(str[3]) >= 0 && parseInt(str[3]) <= 9;
|
|
1630
1396
|
}
|
|
1631
1397
|
__name(isMoveString, "isMoveString");
|
|
@@ -1633,168 +1399,155 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1633
1399
|
return t.toLowerCase();
|
|
1634
1400
|
}
|
|
1635
1401
|
__name(getType, "getType");
|
|
1636
|
-
function getValidMoveMapKings(board,
|
|
1637
|
-
let
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1402
|
+
function getValidMoveMapKings(board, side, pos) {
|
|
1403
|
+
let minRow, maxRow, map = getEmptyBoard(), col = pos[0], row = pos[1];
|
|
1404
|
+
if (row <= 2) {
|
|
1405
|
+
minRow = 0;
|
|
1406
|
+
maxRow = 2;
|
|
1407
|
+
} else if (row >= 7) {
|
|
1408
|
+
minRow = 7;
|
|
1409
|
+
maxRow = 9;
|
|
1410
|
+
} else {
|
|
1411
|
+
minRow = row - 1;
|
|
1412
|
+
maxRow = row + 1;
|
|
1413
|
+
}
|
|
1414
|
+
const minCol = 3, maxCol = 5;
|
|
1415
|
+
const checkAndSet = /* @__PURE__ */ __name((c, r) => {
|
|
1416
|
+
let type = getValidType(board, side, [c, r]);
|
|
1417
|
+
if (type === "eat" || type === "go") setMoveMap(map, [c, r], type);
|
|
1418
|
+
}, "checkAndSet");
|
|
1419
|
+
if (col + 1 <= maxCol) checkAndSet(col + 1, row);
|
|
1420
|
+
if (col - 1 >= minCol) checkAndSet(col - 1, row);
|
|
1421
|
+
if (row + 1 <= maxRow) checkAndSet(col, row + 1);
|
|
1422
|
+
if (row - 1 >= minRow) checkAndSet(col, row - 1);
|
|
1423
|
+
return map;
|
|
1657
1424
|
}
|
|
1658
1425
|
__name(getValidMoveMapKings, "getValidMoveMapKings");
|
|
1659
|
-
function getValidMoveMapCannons(board,
|
|
1660
|
-
let
|
|
1661
|
-
for (let
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
if (s && "eat" == e2) {
|
|
1667
|
-
setMoveMap(i, [a, A], "eat");
|
|
1426
|
+
function getValidMoveMapCannons(board, side, pos) {
|
|
1427
|
+
let map = getEmptyBoard(), col = pos[0], row = pos[1], flag = false;
|
|
1428
|
+
for (let c = col + 1; c < 9; c++) {
|
|
1429
|
+
let type = getValidType(board, side, [c, row]);
|
|
1430
|
+
if (flag) {
|
|
1431
|
+
if (type === "eat") {
|
|
1432
|
+
setMoveMap(map, [c, row], "eat");
|
|
1668
1433
|
break;
|
|
1669
1434
|
}
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
let
|
|
1679
|
-
if (
|
|
1680
|
-
if (
|
|
1681
|
-
setMoveMap(
|
|
1435
|
+
} else {
|
|
1436
|
+
if (type === "block" || type === "eat") {
|
|
1437
|
+
flag = true;
|
|
1438
|
+
} else if (type === "go") setMoveMap(map, [c, row], "go");
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
flag = false;
|
|
1442
|
+
for (let c = col - 1; c >= 0; c--) {
|
|
1443
|
+
let type = getValidType(board, side, [c, row]);
|
|
1444
|
+
if (flag) {
|
|
1445
|
+
if (type === "eat") {
|
|
1446
|
+
setMoveMap(map, [c, row], "eat");
|
|
1682
1447
|
break;
|
|
1683
1448
|
}
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
let
|
|
1693
|
-
if (
|
|
1694
|
-
if (
|
|
1695
|
-
setMoveMap(
|
|
1449
|
+
} else {
|
|
1450
|
+
if (type === "block" || type === "eat") {
|
|
1451
|
+
flag = true;
|
|
1452
|
+
} else if (type === "go") setMoveMap(map, [c, row], "go");
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
flag = false;
|
|
1456
|
+
for (let r = row + 1; r < 10; r++) {
|
|
1457
|
+
let type = getValidType(board, side, [col, r]);
|
|
1458
|
+
if (flag) {
|
|
1459
|
+
if (type === "eat") {
|
|
1460
|
+
setMoveMap(map, [col, r], "eat");
|
|
1696
1461
|
break;
|
|
1697
1462
|
}
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
let
|
|
1707
|
-
if (
|
|
1708
|
-
if (
|
|
1709
|
-
setMoveMap(
|
|
1463
|
+
} else {
|
|
1464
|
+
if (type === "block" || type === "eat") {
|
|
1465
|
+
flag = true;
|
|
1466
|
+
} else if (type === "go") setMoveMap(map, [col, r], "go");
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
flag = false;
|
|
1470
|
+
for (let r = row - 1; r >= 0; r--) {
|
|
1471
|
+
let type = getValidType(board, side, [col, r]);
|
|
1472
|
+
if (flag) {
|
|
1473
|
+
if (type === "eat") {
|
|
1474
|
+
setMoveMap(map, [col, r], "eat");
|
|
1710
1475
|
break;
|
|
1711
1476
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1477
|
+
} else {
|
|
1478
|
+
if (type === "block" || type === "eat") {
|
|
1479
|
+
flag = true;
|
|
1480
|
+
} else if (type === "go") setMoveMap(map, [col, r], "go");
|
|
1481
|
+
}
|
|
1715
1482
|
}
|
|
1716
|
-
return
|
|
1483
|
+
return map;
|
|
1717
1484
|
}
|
|
1718
1485
|
__name(getValidMoveMapCannons, "getValidMoveMapCannons");
|
|
1719
|
-
function getSideByKing(board,
|
|
1720
|
-
|
|
1721
|
-
let
|
|
1722
|
-
|
|
1723
|
-
for (let
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
return "b";
|
|
1730
|
-
}
|
|
1731
|
-
else
|
|
1732
|
-
for (let i = 3; i <= 5; i++)
|
|
1733
|
-
for (let t2 = 7; t2 <= 9; t2++) {
|
|
1734
|
-
let e2 = getPiece(board, [i, t2]);
|
|
1735
|
-
if ("K" == e2)
|
|
1736
|
-
return "w";
|
|
1737
|
-
if ("k" == e2)
|
|
1738
|
-
return "b";
|
|
1739
|
-
}
|
|
1486
|
+
function getSideByKing(board, pos) {
|
|
1487
|
+
let row = pos[1];
|
|
1488
|
+
let searchRange = row <= 4 ? { min: 0, max: 2 } : { min: 7, max: 9 };
|
|
1489
|
+
for (let r = searchRange.min; r <= searchRange.max; r++) {
|
|
1490
|
+
for (let c = 3; c <= 5; c++) {
|
|
1491
|
+
let p = getPiece(board, [c, r]);
|
|
1492
|
+
if (p === "K") return "w";
|
|
1493
|
+
if (p === "k") return "b";
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1740
1496
|
return "";
|
|
1741
1497
|
}
|
|
1742
1498
|
__name(getSideByKing, "getSideByKing");
|
|
1743
|
-
function getValidMoveMapPawns(board,
|
|
1744
|
-
let
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1499
|
+
function getValidMoveMapPawns(board, side, pos) {
|
|
1500
|
+
let map = getEmptyBoard(), col = pos[0], row = pos[1];
|
|
1501
|
+
let kingSide = getSideByKing(board, pos);
|
|
1502
|
+
if (side === kingSide) {
|
|
1503
|
+
if (row <= 4) {
|
|
1504
|
+
let type = getValidType(board, side, [col, row + 1]);
|
|
1505
|
+
if (type === "eat" || type === "go") setMoveMap(map, [col, row + 1], type);
|
|
1749
1506
|
} else {
|
|
1750
|
-
let
|
|
1751
|
-
"eat"
|
|
1507
|
+
let type = getValidType(board, side, [col, row - 1]);
|
|
1508
|
+
if (type === "eat" || type === "go") setMoveMap(map, [col, row - 1], type);
|
|
1752
1509
|
}
|
|
1753
|
-
else {
|
|
1754
|
-
if (
|
|
1755
|
-
let
|
|
1756
|
-
"eat"
|
|
1510
|
+
} else {
|
|
1511
|
+
if (row <= 4) {
|
|
1512
|
+
let type = getValidType(board, side, [col, row - 1]);
|
|
1513
|
+
if (type === "eat" || type === "go") setMoveMap(map, [col, row - 1], type);
|
|
1757
1514
|
} else {
|
|
1758
|
-
let
|
|
1759
|
-
"eat"
|
|
1515
|
+
let type = getValidType(board, side, [col, row + 1]);
|
|
1516
|
+
if (type === "eat" || type === "go") setMoveMap(map, [col, row + 1], type);
|
|
1760
1517
|
}
|
|
1761
|
-
let
|
|
1762
|
-
|
|
1518
|
+
let typeL = getValidType(board, side, [col - 1, row]);
|
|
1519
|
+
if (typeL === "eat" || typeL === "go") setMoveMap(map, [col - 1, row], typeL);
|
|
1520
|
+
let typeR = getValidType(board, side, [col + 1, row]);
|
|
1521
|
+
if (typeR === "eat" || typeR === "go") setMoveMap(map, [col + 1, row], typeR);
|
|
1763
1522
|
}
|
|
1764
|
-
return
|
|
1523
|
+
return map;
|
|
1765
1524
|
}
|
|
1766
1525
|
__name(getValidMoveMapPawns, "getValidMoveMapPawns");
|
|
1767
|
-
function getValidMoveMapBishops(board,
|
|
1768
|
-
let
|
|
1769
|
-
if (
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
}
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
if ("" == e2) {
|
|
1786
|
-
let e3 = getValidType(board, t, [s + 2, a - 2]);
|
|
1787
|
-
"eat" != e3 && "go" != e3 || setMoveMap(A, [s + 2, a - 2], e3);
|
|
1788
|
-
}
|
|
1789
|
-
}
|
|
1790
|
-
if (s - 2 >= 0 && a - 2 >= i) {
|
|
1791
|
-
let e2 = getPiece(board, [s - 1, a - 1]);
|
|
1792
|
-
if ("" == e2) {
|
|
1793
|
-
let e3 = getValidType(board, t, [s - 2, a - 2]);
|
|
1794
|
-
"eat" != e3 && "go" != e3 || setMoveMap(A, [s - 2, a - 2], e3);
|
|
1526
|
+
function getValidMoveMapBishops(board, side, pos) {
|
|
1527
|
+
let minRow, maxRow, map = getEmptyBoard(), col = pos[0], row = pos[1];
|
|
1528
|
+
if (row <= 4) {
|
|
1529
|
+
minRow = 0;
|
|
1530
|
+
maxRow = 4;
|
|
1531
|
+
} else if (row >= 5) {
|
|
1532
|
+
minRow = 5;
|
|
1533
|
+
maxRow = 9;
|
|
1534
|
+
} else {
|
|
1535
|
+
minRow = row - 1;
|
|
1536
|
+
maxRow = row + 1;
|
|
1537
|
+
}
|
|
1538
|
+
const check = /* @__PURE__ */ __name((cOffset, rOffset) => {
|
|
1539
|
+
if (col + cOffset * 2 >= 0 && col + cOffset * 2 <= 8 && row + rOffset * 2 >= minRow && row + rOffset * 2 <= maxRow) {
|
|
1540
|
+
if (getPiece(board, [col + cOffset, row + rOffset]) === "") {
|
|
1541
|
+
let type = getValidType(board, side, [col + cOffset * 2, row + rOffset * 2]);
|
|
1542
|
+
if (type === "eat" || type === "go") setMoveMap(map, [col + cOffset * 2, row + rOffset * 2], type);
|
|
1543
|
+
}
|
|
1795
1544
|
}
|
|
1796
|
-
}
|
|
1797
|
-
|
|
1545
|
+
}, "check");
|
|
1546
|
+
check(1, 1);
|
|
1547
|
+
check(-1, 1);
|
|
1548
|
+
check(1, -1);
|
|
1549
|
+
check(-1, -1);
|
|
1550
|
+
return map;
|
|
1798
1551
|
}
|
|
1799
1552
|
__name(getValidMoveMapBishops, "getValidMoveMapBishops");
|
|
1800
1553
|
function getValidMoveMap(board, type, side, position) {
|
|
@@ -1818,115 +1571,100 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1818
1571
|
}
|
|
1819
1572
|
}
|
|
1820
1573
|
__name(getValidMoveMap, "getValidMoveMap");
|
|
1821
|
-
function getValidMoveMapRooks(board,
|
|
1822
|
-
let
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
continue;
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
}
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
break;
|
|
1838
|
-
if (i[A][s] = e2, "eat" == e2)
|
|
1839
|
-
break;
|
|
1840
|
-
}
|
|
1841
|
-
for (let s = A; s < 10; s++) {
|
|
1842
|
-
if (s == A)
|
|
1843
|
-
continue;
|
|
1844
|
-
let e2 = getValidType(board, t, [n, s]);
|
|
1845
|
-
if ("block" == e2)
|
|
1846
|
-
break;
|
|
1847
|
-
if (i[s][n] = e2, "eat" == e2)
|
|
1848
|
-
break;
|
|
1849
|
-
}
|
|
1850
|
-
for (let s = A; s >= 0; s--) {
|
|
1851
|
-
if (s == A)
|
|
1852
|
-
continue;
|
|
1853
|
-
let e2 = getValidType(board, t, [n, s]);
|
|
1854
|
-
if ("block" == e2)
|
|
1855
|
-
break;
|
|
1856
|
-
if (i[s][n] = e2, "eat" == e2)
|
|
1857
|
-
break;
|
|
1858
|
-
}
|
|
1859
|
-
return i;
|
|
1574
|
+
function getValidMoveMapRooks(board, side, pos) {
|
|
1575
|
+
let map = getEmptyBoard(), col = pos[0], row = pos[1];
|
|
1576
|
+
const scan = /* @__PURE__ */ __name((cStart, cEnd, rStart, rEnd, cStep, rStep) => {
|
|
1577
|
+
for (let c = cStart, r = rStart; c !== cEnd && r !== rEnd; c += cStep, r += rStep) {
|
|
1578
|
+
if (c === col && r === row) continue;
|
|
1579
|
+
let type = getValidType(board, side, [c, r]);
|
|
1580
|
+
if (type === "block") break;
|
|
1581
|
+
map[r][c] = type;
|
|
1582
|
+
if (type === "eat") break;
|
|
1583
|
+
}
|
|
1584
|
+
}, "scan");
|
|
1585
|
+
scan(col, 9, row, row + 1, 1, 0);
|
|
1586
|
+
scan(col, -1, row, row + 1, -1, 0);
|
|
1587
|
+
scan(col, col + 1, row, 10, 0, 1);
|
|
1588
|
+
scan(col, col + 1, row, -1, 0, -1);
|
|
1589
|
+
return map;
|
|
1860
1590
|
}
|
|
1861
1591
|
__name(getValidMoveMapRooks, "getValidMoveMapRooks");
|
|
1862
|
-
function getValidType(board,
|
|
1863
|
-
let
|
|
1864
|
-
return void 0
|
|
1592
|
+
function getValidType(board, side, pos) {
|
|
1593
|
+
let p = getPiece(board, pos);
|
|
1594
|
+
return p === void 0 ? "invalid" : p === "" ? "go" : side !== getSide(p) ? "eat" : "block";
|
|
1865
1595
|
}
|
|
1866
1596
|
__name(getValidType, "getValidType");
|
|
1867
|
-
function getValidMoveMapAdvisors(board,
|
|
1868
|
-
let
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
}
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
}
|
|
1887
|
-
|
|
1597
|
+
function getValidMoveMapAdvisors(board, side, pos) {
|
|
1598
|
+
let minRow, maxRow, map = getEmptyBoard(), col = pos[0], row = pos[1];
|
|
1599
|
+
if (row <= 2) {
|
|
1600
|
+
minRow = 0;
|
|
1601
|
+
maxRow = 2;
|
|
1602
|
+
} else if (row >= 7) {
|
|
1603
|
+
minRow = 7;
|
|
1604
|
+
maxRow = 9;
|
|
1605
|
+
} else {
|
|
1606
|
+
minRow = row - 1;
|
|
1607
|
+
maxRow = row + 1;
|
|
1608
|
+
}
|
|
1609
|
+
const minCol = 3, maxCol = 5;
|
|
1610
|
+
const check = /* @__PURE__ */ __name((cOffset, rOffset) => {
|
|
1611
|
+
let c = col + cOffset, r = row + rOffset;
|
|
1612
|
+
if (c >= minCol && c <= maxCol && r >= minRow && r <= maxRow) {
|
|
1613
|
+
let type = getValidType(board, side, [c, r]);
|
|
1614
|
+
if (type === "eat" || type === "go") setMoveMap(map, [c, r], type);
|
|
1615
|
+
}
|
|
1616
|
+
}, "check");
|
|
1617
|
+
check(1, 1);
|
|
1618
|
+
check(-1, 1);
|
|
1619
|
+
check(1, -1);
|
|
1620
|
+
check(-1, -1);
|
|
1621
|
+
return map;
|
|
1888
1622
|
}
|
|
1889
1623
|
__name(getValidMoveMapAdvisors, "getValidMoveMapAdvisors");
|
|
1890
|
-
function setMoveMap(
|
|
1891
|
-
|
|
1624
|
+
function setMoveMap(map, pos, type) {
|
|
1625
|
+
if (pos[0] >= 0 && pos[0] <= 8 && pos[1] >= 0 && pos[1] <= 9) {
|
|
1626
|
+
map[pos[1]][pos[0]] = type;
|
|
1627
|
+
}
|
|
1892
1628
|
}
|
|
1893
1629
|
__name(setMoveMap, "setMoveMap");
|
|
1894
|
-
function getValidMoveMapKnights(board,
|
|
1895
|
-
let
|
|
1896
|
-
|
|
1630
|
+
function getValidMoveMapKnights(board, side, pos) {
|
|
1631
|
+
let map = getEmptyBoard(), col = pos[0], row = pos[1];
|
|
1632
|
+
const check = /* @__PURE__ */ __name((legC, legR, targetC, targetR) => {
|
|
1633
|
+
if (getPiece(board, [col + legC, row + legR]) === "") {
|
|
1634
|
+
let type = getValidType(board, side, [col + targetC, row + targetR]);
|
|
1635
|
+
setMoveMap(map, [col + targetC, row + targetR], type);
|
|
1636
|
+
}
|
|
1637
|
+
}, "check");
|
|
1638
|
+
check(1, 0, 2, 1);
|
|
1639
|
+
check(1, 0, 2, -1);
|
|
1640
|
+
check(-1, 0, -2, 1);
|
|
1641
|
+
check(-1, 0, -2, -1);
|
|
1642
|
+
check(0, 1, 1, 2);
|
|
1643
|
+
check(0, 1, -1, 2);
|
|
1644
|
+
check(0, -1, 1, -2);
|
|
1645
|
+
check(0, -1, -1, -2);
|
|
1646
|
+
return map;
|
|
1897
1647
|
}
|
|
1898
1648
|
__name(getValidMoveMapKnights, "getValidMoveMapKnights");
|
|
1899
|
-
var
|
|
1900
|
-
|
|
1901
|
-
var
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
Qt["w"] = [], Qt["b"] = [], Qt["w"][0] = "一", Qt["w"][1] = "二", Qt["w"][2] = "三", Qt["w"][3] = "四", Qt["w"][4] = "五", Qt["w"][5] = "六", Qt["w"][6] = "七", Qt["w"][7] = "八", Qt["w"][8] = "九", // Qt["b"][0] = "9",
|
|
1907
|
-
// Qt["b"][1] = "8",
|
|
1908
|
-
// Qt["b"][2] = "7",
|
|
1909
|
-
// Qt["b"][3] = "6",
|
|
1910
|
-
// Qt["b"][4] = "5",
|
|
1911
|
-
// Qt["b"][5] = "4",
|
|
1912
|
-
// Qt["b"][6] = "3",
|
|
1913
|
-
// Qt["b"][7] = "2",
|
|
1914
|
-
// Qt["b"][8] = "1";
|
|
1915
|
-
Qt["b"][0] = "9", Qt["b"][1] = "8", Qt["b"][2] = "7", Qt["b"][3] = "6", Qt["b"][4] = "5", Qt["b"][5] = "4", Qt["b"][6] = "3", Qt["b"][7] = "2", Qt["b"][8] = "1";
|
|
1649
|
+
var ActionMap = { w: ["进", "退"], b: ["退", "进"], p: "平" };
|
|
1650
|
+
var PositionMap = { w: ["前", "后"], b: ["后", "前"], m: "中" };
|
|
1651
|
+
var PieceNameMap = { k: "将", a: "士", b: "象", r: "车", n: "马", c: "炮", p: "卒", K: "帅", A: "仕", B: "相", R: "车", N: "马", C: "炮", P: "兵" };
|
|
1652
|
+
var NumberMap = {
|
|
1653
|
+
w: ["一", "二", "三", "四", "五", "六", "七", "八", "九"],
|
|
1654
|
+
b: ["9", "8", "7", "6", "5", "4", "3", "2", "1"]
|
|
1655
|
+
};
|
|
1916
1656
|
function parseChessNotation(fen) {
|
|
1917
1657
|
let e = [];
|
|
1918
1658
|
for (let s = 0; s < 9; s++) {
|
|
1919
1659
|
e[s] = [];
|
|
1920
|
-
for (let t = 0; t < 10; t++)
|
|
1921
|
-
e[s][t] = 0;
|
|
1660
|
+
for (let t = 0; t < 10; t++) e[s][t] = 0;
|
|
1922
1661
|
}
|
|
1923
1662
|
let i = fen.split(" ")[0].split("/"), n = ["k", "a", "b", "r", "n", "c", "p", "K", "A", "B", "R", "N", "C", "P"], A = 0;
|
|
1924
1663
|
for (let s = 0; s < 10; s++) {
|
|
1925
1664
|
let t = i[s], a = 0;
|
|
1926
|
-
for (let
|
|
1927
|
-
let o = t[
|
|
1928
|
-
if (o >= "0" && o <= "9")
|
|
1929
|
-
a += parseInt(o);
|
|
1665
|
+
for (let k = 0; k < t.length; k++) {
|
|
1666
|
+
let o = t[k];
|
|
1667
|
+
if (o >= "0" && o <= "9") a += parseInt(o);
|
|
1930
1668
|
else if (-1 != n.indexOf(o)) {
|
|
1931
1669
|
let t2 = o >= "a" && o <= "z" ? "b" : "w";
|
|
1932
1670
|
e[a][s] = t2 + o + A, a++, A++;
|
|
@@ -1940,19 +1678,15 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1940
1678
|
let i = parseChessNotation(fen);
|
|
1941
1679
|
var n = moveString.charCodeAt(0) - 97, A = 9 - (moveString.charCodeAt(1) - 48), s = moveString.charCodeAt(2) - 97, a = 9 - (moveString.charCodeAt(3) - 48), o = 0, r = 0, l = 0, d = "";
|
|
1942
1680
|
if ("A" != i[n][A].charAt(1) && "B" != i[n][A].charAt(1) && "a" != i[n][A].charAt(1) && "b" != i[n][A].charAt(1)) {
|
|
1943
|
-
for (var g = 0; g < 9; g++)
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
0 != i[n][f] && i[n][f].substring(0, 2) == i[n][A].substring(0, 2) && (f < A ? 0 == l ? (d = Ht[i[n][A].charAt(0)][1] + qt[i[n][A].charAt(1)], o++) : (d = Ht[i[n][A].charAt(0)][1] + Qt[i[n][A].charAt(0)][9 - n - 1], o++) : f == A ? 0 == o ? d = qt[i[n][A].charAt(1)] + Qt[i[n][A].charAt(0)][9 - n - 1] : r = o : 0 == l ? 0 == o ? d = Ht[i[n][A].charAt(0)][0] + qt[i[n][A].charAt(1)] : (d = Ht["m"] + qt[i[n][A].charAt(1)], o++) : 0 == o ? d = Ht[i[n][A].charAt(0)][0] + Qt[i[n][A].charAt(0)][9 - n - 1] : (d = Ht["m"] + Qt[i[n][A].charAt(0)][9 - n - 1], o++));
|
|
1951
|
-
o > 2 && o != r && (d = "w" == i[n][A].charAt(0) ? Qt["w"][r] + qt[i[n][A].charAt(1)] : Qt["w"][o - r] + qt[i[n][A].charAt(1)]);
|
|
1952
|
-
} else
|
|
1953
|
-
d = qt[i[n][A].charAt(1)] + Qt[i[n][A].charAt(0)][9 - n - 1];
|
|
1681
|
+
for (var g = 0; g < 9; g++) if (g != n) {
|
|
1682
|
+
for (var f = 0; f < 10; f++) 0 != i[g][f] && i[g][f].substring(0, 2) == i[n][A].substring(0, 2) && l++;
|
|
1683
|
+
l < 2 && (l = 0);
|
|
1684
|
+
}
|
|
1685
|
+
for (f = 0; f < 10; f++) 0 != i[n][f] && i[n][f].substring(0, 2) == i[n][A].substring(0, 2) && (f < A ? 0 == l ? (d = PositionMap[i[n][A].charAt(0)][1] + PieceNameMap[i[n][A].charAt(1)], o++) : (d = PositionMap[i[n][A].charAt(0)][1] + NumberMap[i[n][A].charAt(0)][9 - n - 1], o++) : f == A ? 0 == o ? d = PieceNameMap[i[n][A].charAt(1)] + NumberMap[i[n][A].charAt(0)][9 - n - 1] : r = o : 0 == l ? 0 == o ? d = PositionMap[i[n][A].charAt(0)][0] + PieceNameMap[i[n][A].charAt(1)] : (d = PositionMap["m"] + PieceNameMap[i[n][A].charAt(1)], o++) : 0 == o ? d = PositionMap[i[n][A].charAt(0)][0] + NumberMap[i[n][A].charAt(0)][9 - n - 1] : (d = PositionMap["m"] + NumberMap[i[n][A].charAt(0)][9 - n - 1], o++));
|
|
1686
|
+
o > 2 && o != r && (d = "w" == i[n][A].charAt(0) ? NumberMap["w"][r] + PieceNameMap[i[n][A].charAt(1)] : NumberMap["w"][o - r] + PieceNameMap[i[n][A].charAt(1)]);
|
|
1687
|
+
} else d = PieceNameMap[i[n][A].charAt(1)] + NumberMap[i[n][A].charAt(0)][9 - n - 1];
|
|
1954
1688
|
var h2 = "";
|
|
1955
|
-
return h2 = A > a ? n == s ? "w" == i[n][A].charAt(0) ?
|
|
1689
|
+
return h2 = A > a ? n == s ? "w" == i[n][A].charAt(0) ? ActionMap[i[n][A].charAt(0)][0] + NumberMap[i[n][A].charAt(0)][A - a - 1] : ActionMap[i[n][A].charAt(0)][0] + NumberMap[i[n][A].charAt(0)][9 - (A - a - 1) - 1] : ActionMap[i[n][A].charAt(0)][0] + NumberMap[i[n][A].charAt(0)][9 - s - 1] : A == a ? ActionMap["p"] + NumberMap[i[n][A].charAt(0)][9 - s - 1] : n == s ? "w" == i[n][A].charAt(0) ? ActionMap[i[n][A].charAt(0)][1] + NumberMap[i[n][A].charAt(0)][a - A - 1] : ActionMap[i[n][A].charAt(0)][1] + NumberMap[i[n][A].charAt(0)][9 - (a - A - 1) - 1] : ActionMap[i[n][A].charAt(0)][1] + NumberMap[i[n][A].charAt(0)][9 - s - 1], d + h2;
|
|
1956
1690
|
}
|
|
1957
1691
|
__name(getFigureMoveName, "getFigureMoveName");
|
|
1958
1692
|
function moveToMoveString(move) {
|
|
@@ -1976,9 +1710,7 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1976
1710
|
function genRandomId(length) {
|
|
1977
1711
|
let id = "";
|
|
1978
1712
|
const characters = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
1979
|
-
for (let i = 0; i < length; i++)
|
|
1980
|
-
id += characters.charAt(Math.floor(Math.random() * characters.length));
|
|
1981
|
-
}
|
|
1713
|
+
for (let i = 0; i < length; i++) id += characters.charAt(Math.floor(Math.random() * characters.length));
|
|
1982
1714
|
return id;
|
|
1983
1715
|
}
|
|
1984
1716
|
__name(genRandomId, "genRandomId");
|
|
@@ -1994,9 +1726,7 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
1994
1726
|
turnAfterLastEat,
|
|
1995
1727
|
movesAfterLastEat
|
|
1996
1728
|
} = gameRecord;
|
|
1997
|
-
if (isHistoryMode)
|
|
1998
|
-
gameRecord.isHistoryMode = false;
|
|
1999
|
-
}
|
|
1729
|
+
if (isHistoryMode) gameRecord.isHistoryMode = false;
|
|
2000
1730
|
const lastMove = [fromPos, toPos];
|
|
2001
1731
|
const moveId = genRandomId(8);
|
|
2002
1732
|
const pieceAtDestination = board[toPos[1]][toPos[0]] !== "";
|
|
@@ -2052,9 +1782,7 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
2052
1782
|
}
|
|
2053
1783
|
__name(getFen, "getFen");
|
|
2054
1784
|
function boardToFen(board, side, turn = "w") {
|
|
2055
|
-
if (board === null)
|
|
2056
|
-
return "";
|
|
2057
|
-
}
|
|
1785
|
+
if (board === null) return "";
|
|
2058
1786
|
const ranks = side === "w" ? [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] : [9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
|
|
2059
1787
|
let fenString = "";
|
|
2060
1788
|
for (const row of ranks) {
|
|
@@ -2071,9 +1799,7 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
2071
1799
|
fenString += piece;
|
|
2072
1800
|
}
|
|
2073
1801
|
}
|
|
2074
|
-
if (emptyCount > 0)
|
|
2075
|
-
fenString += emptyCount;
|
|
2076
|
-
}
|
|
1802
|
+
if (emptyCount > 0) fenString += emptyCount;
|
|
2077
1803
|
fenString += "/";
|
|
2078
1804
|
}
|
|
2079
1805
|
fenString = fenString.substring(0, fenString.length - 1) + " " + turn;
|
|
@@ -2081,16 +1807,13 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
2081
1807
|
}
|
|
2082
1808
|
__name(boardToFen, "boardToFen");
|
|
2083
1809
|
function getSide(piece) {
|
|
2084
|
-
|
|
2085
|
-
return e.includes(piece) ? "w" : "b";
|
|
1810
|
+
return "RNBAKCP".includes(piece) ? "w" : "b";
|
|
2086
1811
|
}
|
|
2087
1812
|
__name(getSide, "getSide");
|
|
2088
1813
|
async function getGameRecord(channelId) {
|
|
2089
1814
|
let gameRecord = await ctx.database.get("cchess_game_records", { channelId });
|
|
2090
1815
|
if (gameRecord.length === 0) {
|
|
2091
|
-
await ctx.database.create("cchess_game_records", {
|
|
2092
|
-
channelId
|
|
2093
|
-
});
|
|
1816
|
+
await ctx.database.create("cchess_game_records", { channelId });
|
|
2094
1817
|
gameRecord = await ctx.database.get("cchess_game_records", { channelId });
|
|
2095
1818
|
}
|
|
2096
1819
|
return gameRecord[0];
|
|
@@ -2098,11 +1821,11 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
2098
1821
|
__name(getGameRecord, "getGameRecord");
|
|
2099
1822
|
async function drawChessBoard(channelId) {
|
|
2100
1823
|
const gameRecord = await getGameRecord(channelId);
|
|
2101
|
-
let { board, lastMove, moveList
|
|
2102
|
-
const canvas = await ctx.canvas.createCanvas(
|
|
1824
|
+
let { board, lastMove, moveList } = gameRecord;
|
|
1825
|
+
const canvas = await ctx.canvas.createCanvas(BOARD_WIDTH * scale, BOARD_HEIGHT * scale);
|
|
2103
1826
|
const context = canvas.getContext("2d");
|
|
2104
1827
|
const boardImg = await ctx.canvas.loadImage(boardSkinImg);
|
|
2105
|
-
context.drawImage(boardImg, 0, 0,
|
|
1828
|
+
context.drawImage(boardImg, 0, 0, BOARD_WIDTH * scale, BOARD_HEIGHT * scale);
|
|
2106
1829
|
const pieceImages = {};
|
|
2107
1830
|
const pieceImagePromises = Object.keys(piecesImgResources).map((piece) => {
|
|
2108
1831
|
return ctx.canvas.loadImage(piecesImgResources[piece]).then((img) => {
|
|
@@ -2113,21 +1836,27 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
2113
1836
|
for (let row = 0; row < 10; row++) {
|
|
2114
1837
|
for (let col = 0; col < 9; col++) {
|
|
2115
1838
|
const piece = getPiece(board, [col, row]);
|
|
2116
|
-
if (piece === "")
|
|
2117
|
-
|
|
2118
|
-
const
|
|
2119
|
-
const yPos = ((isFlipBoard ? 9 - row : row) * we + Pe - Be / 2) * scale;
|
|
1839
|
+
if (piece === "") continue;
|
|
1840
|
+
const xPos = ((isFlipBoard ? 8 - col : col) * CELL_WIDTH + BOARD_PADDING - PIECE_SIZE / 2) * scale;
|
|
1841
|
+
const yPos = ((isFlipBoard ? 9 - row : row) * CELL_WIDTH + BOARD_PADDING - PIECE_SIZE / 2) * scale;
|
|
2120
1842
|
const pieceImg = pieceImages[piece];
|
|
2121
|
-
context.drawImage(pieceImg, xPos, yPos,
|
|
1843
|
+
context.drawImage(pieceImg, xPos, yPos, PIECE_SIZE * scale, PIECE_SIZE * scale);
|
|
2122
1844
|
}
|
|
2123
1845
|
}
|
|
2124
1846
|
if (lastMove !== null) {
|
|
2125
|
-
let
|
|
2126
|
-
let
|
|
2127
|
-
|
|
1847
|
+
let [start, end] = lastMove;
|
|
1848
|
+
let sx = start[0], sy = start[1], ex = end[0], ey = end[1];
|
|
1849
|
+
let startX = sx * CELL_WIDTH + ARROW_OFFSET;
|
|
1850
|
+
let startY = sy * CELL_WIDTH + BOARD_PADDING;
|
|
1851
|
+
let endX = ex * CELL_WIDTH + ARROW_OFFSET;
|
|
1852
|
+
let endY = ey * CELL_WIDTH + BOARD_PADDING;
|
|
1853
|
+
let dist = Math.sqrt((startX - endX) ** 2 + (startY - endY) ** 2);
|
|
1854
|
+
let adjustedEndX = startX + (endX - startX) * (dist - 30) / dist;
|
|
1855
|
+
let adjustedEndY = startY + (endY - startY) * (dist - 30) / dist;
|
|
1856
|
+
await drawLineArrow(context, startX * scale, startY * scale, adjustedEndX * scale, adjustedEndY * scale, "rgba(128, 171, 69, 0.7)", 15 * scale);
|
|
2128
1857
|
}
|
|
2129
1858
|
if (moveList.length > 0) {
|
|
2130
|
-
currentMoveId = moveList[moveList.length - 1].moveId;
|
|
1859
|
+
const currentMoveId = moveList[moveList.length - 1].moveId;
|
|
2131
1860
|
await ctx.database.set("cchess_game_records", { channelId }, { currentMoveId });
|
|
2132
1861
|
}
|
|
2133
1862
|
if (config.isChessImageWithOutlineEnabled) {
|
|
@@ -2137,13 +1866,29 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
2137
1866
|
}
|
|
2138
1867
|
}
|
|
2139
1868
|
__name(drawChessBoard, "drawChessBoard");
|
|
2140
|
-
async function drawLineArrow(
|
|
2141
|
-
var
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
var
|
|
2146
|
-
|
|
1869
|
+
async function drawLineArrow(ctx2, fromX, fromY, toX, toY, color = "black", headSize = 20) {
|
|
1870
|
+
var angle = Math.atan2(toY - fromY, toX - fromX);
|
|
1871
|
+
var headAngle = 45 * Math.PI / 180;
|
|
1872
|
+
ctx2.beginPath();
|
|
1873
|
+
ctx2.moveTo(fromX, fromY);
|
|
1874
|
+
var angle1 = angle + headAngle;
|
|
1875
|
+
var angle2 = angle - headAngle;
|
|
1876
|
+
var topX = toX - headSize * Math.cos(angle1);
|
|
1877
|
+
var topY = toY - headSize * Math.sin(angle1);
|
|
1878
|
+
var botX = toX - headSize * Math.cos(angle2);
|
|
1879
|
+
var botY = toY - headSize * Math.sin(angle2);
|
|
1880
|
+
ctx2.lineTo(toX, toY);
|
|
1881
|
+
ctx2.stroke();
|
|
1882
|
+
ctx2.beginPath();
|
|
1883
|
+
ctx2.moveTo(toX, toY);
|
|
1884
|
+
ctx2.lineTo(topX, topY);
|
|
1885
|
+
ctx2.lineTo(botX, botY);
|
|
1886
|
+
ctx2.lineTo(toX, toY);
|
|
1887
|
+
ctx2.closePath();
|
|
1888
|
+
ctx2.fillStyle = color;
|
|
1889
|
+
ctx2.strokeStyle = color;
|
|
1890
|
+
ctx2.fill();
|
|
1891
|
+
ctx2.stroke();
|
|
2147
1892
|
}
|
|
2148
1893
|
__name(drawLineArrow, "drawLineArrow");
|
|
2149
1894
|
function loadPiecesImageResources(folder, format = "webp") {
|
|
@@ -2211,31 +1956,21 @@ https://www.xqbase.com/protocol/cchess_fen.htm`);
|
|
|
2211
1956
|
const { bot, channelId } = session;
|
|
2212
1957
|
let messageId;
|
|
2213
1958
|
if (config.isTextToImageConversionEnabled) {
|
|
2214
|
-
const
|
|
2215
|
-
|
|
2216
|
-
if (isOnlyImgTag) {
|
|
2217
|
-
await session.send(message);
|
|
2218
|
-
} else {
|
|
2219
|
-
const modifiedMessage = lines.map((line) => {
|
|
2220
|
-
if (line.trim() !== "" && !line.includes("<img")) {
|
|
2221
|
-
return `# ${line}`;
|
|
2222
|
-
} else {
|
|
2223
|
-
return line + "\n";
|
|
2224
|
-
}
|
|
2225
|
-
}).join("\n");
|
|
2226
|
-
const imageBuffer = await ctx.markdownToImage.convertToImage(modifiedMessage);
|
|
2227
|
-
[messageId] = await session.send(import_koishi.h.image(imageBuffer, `image/${config.imageType}`));
|
|
2228
|
-
}
|
|
1959
|
+
const imageBuffer = await ctx.markdownToImage.convertToImage(message);
|
|
1960
|
+
[messageId] = await session.send(import_koishi.h.image(imageBuffer, `image/${config.imageType}`));
|
|
2229
1961
|
} else {
|
|
2230
|
-
|
|
1962
|
+
let plainText = message.replace(/\*\*(.*?)\*\*/g, "$1").replace(/^#+\s*/gm, "").replace(/\[([^\]]+)\]\([^\)]+\)/g, "$1");
|
|
1963
|
+
[messageId] = await session.send(plainText);
|
|
2231
1964
|
}
|
|
2232
|
-
if (config.retractDelay === 0)
|
|
2233
|
-
return;
|
|
1965
|
+
if (config.retractDelay === 0) return;
|
|
2234
1966
|
sentMessages.push(messageId);
|
|
2235
1967
|
if (sentMessages.length > 1) {
|
|
2236
1968
|
const oldestMessageId = sentMessages.shift();
|
|
2237
1969
|
setTimeout(async () => {
|
|
2238
|
-
|
|
1970
|
+
try {
|
|
1971
|
+
await bot.deleteMessage(channelId, oldestMessageId);
|
|
1972
|
+
} catch (e) {
|
|
1973
|
+
}
|
|
2239
1974
|
}, config.retractDelay * 1e3);
|
|
2240
1975
|
}
|
|
2241
1976
|
}
|