koishi-plugin-satori-ai 1.2.6 → 1.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/abstractGame.d.ts +52 -0
- package/lib/game.d.ts +10 -4
- package/lib/gamefencing.d.ts +1 -0
- package/lib/gamegobang.d.ts +43 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +99 -71
- package/lib/memory.d.ts +2 -0
- package/lib/types.d.ts +3 -2
- package/lib/userportrait.d.ts +1 -0
- package/lib/utils.d.ts +5 -1
- package/package.json +2 -1
- package/wasm/gobang.cpp +582 -0
- package/wasm/gobang.js +21 -0
- package/wasm/gobang.wasm +0 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Session, Context } from 'koishi';
|
|
2
|
+
declare module 'koishi' {
|
|
3
|
+
interface Events {
|
|
4
|
+
'game-result'(session: Session, result: gameResult): void;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export interface gameResult {
|
|
8
|
+
message: string;
|
|
9
|
+
gameName: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 单局游戏的抽象基类,提供通用生命周期方法
|
|
13
|
+
*/
|
|
14
|
+
export declare abstract class abstractGameSingleGame {
|
|
15
|
+
protected session: Session;
|
|
16
|
+
protected disposeListener: () => boolean;
|
|
17
|
+
constructor(disposeListener: () => boolean, session: Session);
|
|
18
|
+
startGame(): string;
|
|
19
|
+
endGame(): {
|
|
20
|
+
message: string;
|
|
21
|
+
gameName: string;
|
|
22
|
+
};
|
|
23
|
+
processInput(str: string): Promise<string>;
|
|
24
|
+
}
|
|
25
|
+
type Constructor<T> = new (...args: any[]) => T;
|
|
26
|
+
/**
|
|
27
|
+
* 游戏管理抽象基类,管理多个频道的游戏实例
|
|
28
|
+
*/
|
|
29
|
+
export declare abstract class abstractGame<T extends abstractGameSingleGame> {
|
|
30
|
+
protected gameClass: Constructor<T>;
|
|
31
|
+
protected channelGames: Map<string, T>;
|
|
32
|
+
constructor(GameClass: Constructor<T>);
|
|
33
|
+
/**
|
|
34
|
+
* 生成消息监听器,用于处理玩家输入
|
|
35
|
+
* @param userID 玩家ID
|
|
36
|
+
* @param guildID 服务器ID
|
|
37
|
+
* @returns 监听函数,将输入转发给对应频道的游戏实例
|
|
38
|
+
*/
|
|
39
|
+
protected listener: (userID: string, guildID: string) => ((session: Session) => void);
|
|
40
|
+
/**
|
|
41
|
+
* 启动游戏实例
|
|
42
|
+
* @param session 会话上下文
|
|
43
|
+
* @param ctx Koishi 上下文
|
|
44
|
+
* @param args 启动参数(如难度等级)
|
|
45
|
+
*/
|
|
46
|
+
startGame(session: Session, ctx: Context, args: string[]): abstractGameSingleGame | null;
|
|
47
|
+
/**
|
|
48
|
+
* 结束游戏实例,触发结果事件
|
|
49
|
+
*/
|
|
50
|
+
endGame(session: Session, ctx: Context): string;
|
|
51
|
+
}
|
|
52
|
+
export {};
|
package/lib/game.d.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { Session, Context } from 'koishi';
|
|
2
|
+
import { Sat } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* 游戏总控类,管理所有可用游戏和命令
|
|
5
|
+
*/
|
|
2
6
|
export declare class Game {
|
|
3
7
|
private GAMES;
|
|
4
8
|
private channelGames;
|
|
5
|
-
private
|
|
6
|
-
|
|
9
|
+
private availableGames;
|
|
10
|
+
private context;
|
|
11
|
+
private config;
|
|
12
|
+
constructor(ctx: Context, cfg: Sat.Config);
|
|
7
13
|
private registerCommands;
|
|
8
|
-
startGame(session: Session, gameName: string, args: string[]): Promise<
|
|
9
|
-
endGame(session: Session): Promise<
|
|
14
|
+
startGame(session: Session, gameName: string, args: string[]): Promise<"当前频道已经有游戏在进行中" | "没有这个游戏哦">;
|
|
15
|
+
endGame(session: Session): Promise<string>;
|
|
10
16
|
private selectGame;
|
|
11
17
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Session, Context } from 'koishi';
|
|
2
|
+
import { abstractGame, abstractGameSingleGame, gameResult } from './abstractGame';
|
|
3
|
+
export declare enum winFlag {
|
|
4
|
+
win = 1,
|
|
5
|
+
lose = 2,
|
|
6
|
+
draw = 3,
|
|
7
|
+
pending = 4
|
|
8
|
+
}
|
|
9
|
+
export interface goBangGameResult extends gameResult {
|
|
10
|
+
win: winFlag;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 五子棋单局实现类,继承自 abstractGameSingleGame
|
|
14
|
+
*/
|
|
15
|
+
declare class goBangSingleGame extends abstractGameSingleGame {
|
|
16
|
+
private playerFlag;
|
|
17
|
+
private winningFlag;
|
|
18
|
+
level: number;
|
|
19
|
+
private board;
|
|
20
|
+
constructor(disposeListener: () => boolean, session: Session);
|
|
21
|
+
startGame: () => string;
|
|
22
|
+
endGame: () => {
|
|
23
|
+
message: string;
|
|
24
|
+
win: winFlag;
|
|
25
|
+
gameName: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* 处理玩家输入(落子坐标)
|
|
29
|
+
* @param str 输入内容,格式为 "x y"
|
|
30
|
+
*/
|
|
31
|
+
processInput(str: string): Promise<string>;
|
|
32
|
+
private checkWin;
|
|
33
|
+
private checkDirection;
|
|
34
|
+
private printBoard;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 五子棋管理类,继承自 abstractGame
|
|
38
|
+
*/
|
|
39
|
+
export declare class goBang extends abstractGame<goBangSingleGame> {
|
|
40
|
+
constructor();
|
|
41
|
+
startGame(session: Session, ctx: Context, args: string[]): goBangSingleGame;
|
|
42
|
+
}
|
|
43
|
+
export {};
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -33,7 +33,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
33
33
|
// src/locales/zh.yml
|
|
34
34
|
var require_zh = __commonJS({
|
|
35
35
|
"src/locales/zh.yml"(exports, module2) {
|
|
36
|
-
module2.exports = { commands: { sat: { description: "AI聊天", messages: { "no-prompt": "你好。使用说明可以看我的空间哦。", tooManyEnglishLetters: "请不要用这么多英文......", err: "寄!{0}", tooLong: "你的话太多了。", content_tooLong: "这个问题有点复杂,你还是去问别人吧。", "duplicate-dialogue": "这个刚刚说过了吧......", online: "思考中,请再等一会!", "no-response": "我不知道你在说什么……", update_level_succeed: "用户等级已更新为{0}", exceeds: "已经差不多够了吧?请明天再来。", usage: "今日次数{0}/{1}", hatMan: "叮!帽子先生保护了你的好感度,快说谢谢帽子先生吧!", block1: "我讨厌你!", block2: "我讨厌你们!" } }, "sat.clear": { description: "清空当前频道会话", messages: { clean: "已清空当前会话。", Allclean: "已清空所有会话。" } }, "sat.common_sense": { description: "添加常识", messages: { "no-prompt": "你想让我知道什么?", succeed: "我知道了,{0}" } } } };
|
|
36
|
+
module2.exports = { commands: { sat: { description: "AI聊天", messages: { "no-prompt": "你好。使用说明可以看我的空间哦。", tooManyEnglishLetters: "请不要用这么多英文......", err: "寄!{0}", tooLong: "你的话太多了。", content_tooLong: "这个问题有点复杂,你还是去问别人吧。", "duplicate-dialogue": "这个刚刚说过了吧......", online: "思考中,请再等一会!", "no-response": "我不知道你在说什么……", update_level_succeed: "用户等级已更新为{0}", exceeds: "已经差不多够了吧?请明天再来。", usage: "今日次数{0}/{1}", hatMan: "叮!帽子先生保护了你的好感度,快说谢谢帽子先生吧!", rockBottom: "叮!谷底小石保护了你的好感度,快说谢谢谷底小石吧!", block1: "我讨厌你!", block2: "我讨厌你们!" } }, "sat.clear": { description: "清空当前频道会话", messages: { clean: "已清空当前会话。", Allclean: "已清空所有会话。" } }, "sat.common_sense": { description: "添加常识", messages: { "no-prompt": "你想让我知道什么?", succeed: "我知道了,{0}" } } } };
|
|
37
37
|
}
|
|
38
38
|
});
|
|
39
39
|
|
|
@@ -135,7 +135,10 @@ function processPrompt(prompt) {
|
|
|
135
135
|
return "";
|
|
136
136
|
if (prompt.includes(":poke"))
|
|
137
137
|
return "戳戳";
|
|
138
|
-
|
|
138
|
+
prompt = prompt.replace(/<[^>]*?img[^>]*>/g, "[图片]");
|
|
139
|
+
prompt = prompt.replace(/<[^>]*?avatar[^>]*>/g, "");
|
|
140
|
+
prompt = prompt.replace(/<[^>]*?name="([^\"]*)"[^>]*>/g, (_, name) => `@${name}`);
|
|
141
|
+
return prompt;
|
|
139
142
|
}
|
|
140
143
|
__name(processPrompt, "processPrompt");
|
|
141
144
|
function filterResponse(prompt, words) {
|
|
@@ -146,7 +149,11 @@ function filterResponse(prompt, words) {
|
|
|
146
149
|
}
|
|
147
150
|
return part;
|
|
148
151
|
}).join("");
|
|
149
|
-
const
|
|
152
|
+
const regex = /<think>[\s\S]*?<\/think>/g;
|
|
153
|
+
const filteredThink = filtered.replace(regex, "");
|
|
154
|
+
const regex2 = /<think>|<\/think>/g;
|
|
155
|
+
const filtered2 = filteredThink.replace(regex2, "");
|
|
156
|
+
const trimmedResult = filtered2.trim();
|
|
150
157
|
return trimmedResult === "" ? "……" : trimmedResult;
|
|
151
158
|
}
|
|
152
159
|
__name(filterResponse, "filterResponse");
|
|
@@ -415,6 +422,7 @@ __name(updateFavorability, "updateFavorability");
|
|
|
415
422
|
async function updateUserLevel(ctx, user, level) {
|
|
416
423
|
if (!user)
|
|
417
424
|
return;
|
|
425
|
+
level = level < 5 ? level : 4;
|
|
418
426
|
await ctx.database.set("p_system", { userid: user.userid }, { userlevel: level });
|
|
419
427
|
}
|
|
420
428
|
__name(updateUserLevel, "updateUserLevel");
|
|
@@ -483,6 +491,8 @@ var MemoryManager = class {
|
|
|
483
491
|
}
|
|
484
492
|
channelMemories = /* @__PURE__ */ new Map();
|
|
485
493
|
channelDialogues = /* @__PURE__ */ new Map();
|
|
494
|
+
charactersToRemove = ["的", "一", "是", "了", "什", "么", "我", "谁", "不", "人", "在", "他", "有", "这", "个", "上", "们", "来", "到", "时", "大", "地", "为", "子", "中", "你", "说", "生", "国", "年", "着", "就", "那", "和", "要", "她", "出", "也", "得", "里", "后", "自", "以", "会", "id=", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"];
|
|
495
|
+
MAX_MEMORY_LENGTH = 5e3;
|
|
486
496
|
// 更新记忆
|
|
487
497
|
async updateMemories(session, prompt, config, response) {
|
|
488
498
|
if (response.error)
|
|
@@ -602,7 +612,10 @@ var MemoryManager = class {
|
|
|
602
612
|
if (filtered.length === 0)
|
|
603
613
|
return;
|
|
604
614
|
const existing = await this.loadMemoryFile(filePath);
|
|
605
|
-
|
|
615
|
+
let updated = [...existing, ...filtered];
|
|
616
|
+
if (updated.length > this.MAX_MEMORY_LENGTH) {
|
|
617
|
+
updated = updated.slice(-this.MAX_MEMORY_LENGTH);
|
|
618
|
+
}
|
|
606
619
|
fs2.writeFileSync(filePath, JSON.stringify(updated, null, 2));
|
|
607
620
|
}
|
|
608
621
|
// 记忆检索
|
|
@@ -615,20 +628,24 @@ var MemoryManager = class {
|
|
|
615
628
|
"user": this.config.dailogues_topN,
|
|
616
629
|
"common": this.config.common_topN
|
|
617
630
|
};
|
|
618
|
-
|
|
619
|
-
|
|
631
|
+
const filePath = filePathMap[type];
|
|
632
|
+
if (!fs2.existsSync(filePath)) {
|
|
620
633
|
return "";
|
|
621
634
|
}
|
|
622
|
-
const
|
|
623
|
-
|
|
624
|
-
const
|
|
625
|
-
|
|
626
|
-
|
|
635
|
+
const keywords = prompt.split("").filter((word) => !this.charactersToRemove.includes(word));
|
|
636
|
+
let entries = await this.loadMemoryFile(filePath);
|
|
637
|
+
const matched = this.findBestMatches(entries, keywords).slice(0, topNMap[type] * 5);
|
|
638
|
+
if (type === "user") {
|
|
639
|
+
const remainingEntries = entries.filter((entry) => !matched.includes(entry));
|
|
640
|
+
let updatedEntries = [...remainingEntries, ...matched];
|
|
641
|
+
fs2.writeFileSync(filePath, JSON.stringify(updatedEntries, null, 2));
|
|
642
|
+
}
|
|
643
|
+
const result = this.formatMatches(matched, type, topNMap[type]);
|
|
627
644
|
return result;
|
|
628
645
|
}
|
|
629
646
|
// 记忆检索
|
|
630
|
-
findBestMatches(entries, keywords
|
|
631
|
-
return entries.map((entry) => ({ entry, ...this.calculateMatchScore(entry.content, keywords) })).filter(({ count }) => count > 1).sort((a, b) => b.score - a.score).
|
|
647
|
+
findBestMatches(entries, keywords) {
|
|
648
|
+
return entries.map((entry) => ({ entry, ...this.calculateMatchScore(entry.content, keywords) })).filter(({ count }) => count > 1).sort((a, b) => b.score - a.score).map(({ entry }) => entry);
|
|
632
649
|
}
|
|
633
650
|
// 匹配度计算
|
|
634
651
|
calculateMatchScore(content, keywords) {
|
|
@@ -637,6 +654,8 @@ var MemoryManager = class {
|
|
|
637
654
|
const escapedKeywords = keywords.map((k) => escapeRegExp(k));
|
|
638
655
|
const regex = new RegExp(escapedKeywords.join("|"), "gi");
|
|
639
656
|
const chineseRegex = /[\u4e00-\u9fa5]/g;
|
|
657
|
+
const regex2 = new RegExp(`[${this.charactersToRemove.join("")}]`, "g");
|
|
658
|
+
content = content.replace(regex2, "");
|
|
640
659
|
const matches = content.match(regex) || [];
|
|
641
660
|
let count = 0;
|
|
642
661
|
matches.forEach((match) => {
|
|
@@ -651,7 +670,7 @@ var MemoryManager = class {
|
|
|
651
670
|
return { score: ratio, count };
|
|
652
671
|
}
|
|
653
672
|
// 格式化匹配结果
|
|
654
|
-
formatMatches(matched, type) {
|
|
673
|
+
formatMatches(matched, type, topN = 5) {
|
|
655
674
|
const prefixMap = {
|
|
656
675
|
"common": "\n这是你可能用到的信息:",
|
|
657
676
|
"user": "\n以下是较久之前用户说过的话和对话时间:"
|
|
@@ -661,6 +680,7 @@ var MemoryManager = class {
|
|
|
661
680
|
const date = `
|
|
662
681
|
当前日期和时间:${(/* @__PURE__ */ new Date()).toLocaleString()} ${time}`;
|
|
663
682
|
if (matched.length > 0) {
|
|
683
|
+
matched = matched.slice(0, topN < matched.length ? topN : matched.length);
|
|
664
684
|
if (type === "common") {
|
|
665
685
|
const result = `${prefixMap[type]}{
|
|
666
686
|
${matched.map((entry) => entry.content).join("\n")} ${date}
|
|
@@ -917,6 +937,10 @@ async function ensureCensorFileExists(basePath) {
|
|
|
917
937
|
}
|
|
918
938
|
__name(ensureCensorFileExists, "ensureCensorFileExists");
|
|
919
939
|
async function applyFavorabilityEffect(ctx, user, effect, session) {
|
|
940
|
+
if (effect < 0 && user.items["谷底小石"]?.count > 0) {
|
|
941
|
+
session.send(session.text("commands.sat.messages.rockBottom"));
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
920
944
|
if (effect < 0 && user.items["帽子先生"]?.count > 0) {
|
|
921
945
|
user.items["帽子先生"].count--;
|
|
922
946
|
if (user.items["帽子先生"].count === 0)
|
|
@@ -942,9 +966,6 @@ function createMiddleware(ctx, sat, config) {
|
|
|
942
966
|
if (config.nick_name && await hasNickName(ctx, session, config)) {
|
|
943
967
|
return await handleNickNameMessage(sat, session);
|
|
944
968
|
}
|
|
945
|
-
if (config.mention && isMentionTriggered(session)) {
|
|
946
|
-
return await handleMentionMessage(sat, session);
|
|
947
|
-
}
|
|
948
969
|
if (shouldRandomTrigger(session, config)) {
|
|
949
970
|
return await sat.handleRandomMiddleware(session, session.content);
|
|
950
971
|
}
|
|
@@ -983,16 +1004,6 @@ async function handleNickNameMessage(SAT2, session) {
|
|
|
983
1004
|
return await SAT2.handleNickNameMiddleware(session, content);
|
|
984
1005
|
}
|
|
985
1006
|
__name(handleNickNameMessage, "handleNickNameMessage");
|
|
986
|
-
function isMentionTriggered(session) {
|
|
987
|
-
return !!session.stripped.appel;
|
|
988
|
-
}
|
|
989
|
-
__name(isMentionTriggered, "isMentionTriggered");
|
|
990
|
-
async function handleMentionMessage(SAT2, session) {
|
|
991
|
-
let message = session.elements.slice(1).filter((e) => e.type === "text").map((e) => e.attrs.content).join("").trim();
|
|
992
|
-
if (message)
|
|
993
|
-
return await SAT2.handleNickNameMiddleware(session, message);
|
|
994
|
-
}
|
|
995
|
-
__name(handleMentionMessage, "handleMentionMessage");
|
|
996
1007
|
function shouldRandomTrigger(session, config) {
|
|
997
1008
|
const { content } = session;
|
|
998
1009
|
return !isSpecialMessage(session) && content.length >= config.random_min_tokens && content.length < config.max_tokens && probabilisticCheck(config.randnum);
|
|
@@ -1070,10 +1081,10 @@ var Sat = class extends import_koishi5.Service {
|
|
|
1070
1081
|
import_koishi5.Schema.object({
|
|
1071
1082
|
max_usage: import_koishi5.Schema.tuple([Number, Number, Number, Number, Number]).default([40, 240, 3e3, 9999, 0]).description("每日最大使用次数(对应用户level0~level4)(0为不限制)"),
|
|
1072
1083
|
private: import_koishi5.Schema.boolean().default(false).description("开启后私聊AI可触发对话, 不需要使用指令"),
|
|
1073
|
-
mention: import_koishi5.Schema.boolean().default(false).description("开启后机器人被提及(at/引用)可触发对话"),
|
|
1074
1084
|
nick_name: import_koishi5.Schema.boolean().default(true).description("是否使用昵称触发对话(发言中含有昵称时)"),
|
|
1075
1085
|
nick_name_list: import_koishi5.Schema.array(String).default(["昵称1"]).description("昵称列表"),
|
|
1076
1086
|
nick_name_block_words: import_koishi5.Schema.array(String).default(["屏蔽词1"]).description("昵称屏蔽词(含有屏蔽词的消息不会触发昵称对话)"),
|
|
1087
|
+
input_prompt: import_koishi5.Schema.string().role("textarea").default("(注意专注对话主题,遵守对话要求,不要复读)").description("每轮对话前的补充提示(可用于强调要求)不需要时可不填"),
|
|
1077
1088
|
reasoner_filter: import_koishi5.Schema.boolean().default(true).description("是否启用返回内容过滤,开启后在对话时会过滤掉在括号内且含有过滤词的那一句,用于缓解思维链溢出问题"),
|
|
1078
1089
|
reasoner_filter_word: import_koishi5.Schema.string().role("textarea").default("系统-提示-用户-设定-回复").description("返回内容过滤词,使用“-”分隔,在括号内且含有过滤词的那一句会被过滤,用于缓解思维链溢出问题"),
|
|
1079
1090
|
duplicateDialogueCheck: import_koishi5.Schema.boolean().default(true).description("是否检查重复对话"),
|
|
@@ -1111,7 +1122,7 @@ var Sat = class extends import_koishi5.Service {
|
|
|
1111
1122
|
import_koishi5.Schema.object({
|
|
1112
1123
|
enable_game: import_koishi5.Schema.boolean().default(false).description("是否开启游戏模块"),
|
|
1113
1124
|
enable_gobang: import_koishi5.Schema.boolean().default(false).description("是否开启五子棋游戏")
|
|
1114
|
-
}).description("拓展模块-游戏设置(
|
|
1125
|
+
}).description("拓展模块-游戏设置(无效,开发中)")
|
|
1115
1126
|
]);
|
|
1116
1127
|
})(Sat || (Sat = {}));
|
|
1117
1128
|
|
|
@@ -1150,30 +1161,34 @@ var UserPortraitManager = class {
|
|
|
1150
1161
|
const memoryPath = path5.join(this.config.dataDir, "dialogues", `${userId}.txt`);
|
|
1151
1162
|
if (!fs5.existsSync(memoryPath))
|
|
1152
1163
|
return [];
|
|
1153
|
-
const
|
|
1154
|
-
const
|
|
1164
|
+
const memoryContent = JSON.parse(fs5.readFileSync(memoryPath, "utf-8"));
|
|
1165
|
+
const validEntries = memoryContent.filter((entry) => entry.role !== "user").map((entry) => {
|
|
1166
|
+
const timeMatch = entry.role.match(/对话日期和时间:(\d{4}\/\d{1,2}\/\d{1,2} \d{2}:\d{2}:\d{2})/);
|
|
1167
|
+
if (!timeMatch)
|
|
1168
|
+
return null;
|
|
1169
|
+
return {
|
|
1170
|
+
...entry,
|
|
1171
|
+
timestamp: new Date(timeMatch[1]).getTime()
|
|
1172
|
+
};
|
|
1173
|
+
}).filter((entry) => entry !== null).sort((a, b) => b.timestamp - a.timestamp);
|
|
1155
1174
|
const level = user.userlevel < 5 ? user.userlevel : 4;
|
|
1156
|
-
const usageLimit = this.config.max_usage[level]
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
}
|
|
1160
|
-
return dialoguesContent;
|
|
1175
|
+
const usageLimit = this.config.max_usage[level] === 0 ? this.config.max_portrait_dialogues : this.config.max_usage[level];
|
|
1176
|
+
const maxEntries = Math.min(usageLimit, this.config.max_portrait_dialogues);
|
|
1177
|
+
return validEntries.slice(0, maxEntries).map((entry) => `${entry.content}${entry.role}`);
|
|
1161
1178
|
}
|
|
1162
1179
|
// 生成提示词模板
|
|
1163
|
-
buildMessage(level, history, existingPortrait) {
|
|
1180
|
+
buildMessage(level, history, existingPortrait, user) {
|
|
1164
1181
|
return [{
|
|
1165
1182
|
role: "user",
|
|
1166
|
-
content:
|
|
1183
|
+
content: `你是一个角色扮演智能体工作流中的一环,请根据以下信息更新用户画像。需包括:
|
|
1167
1184
|
1. 基本信息(性别、年龄、生日等)
|
|
1168
|
-
2.
|
|
1169
|
-
3.
|
|
1170
|
-
4.
|
|
1171
|
-
5.
|
|
1172
|
-
6.
|
|
1173
|
-
7. 其他你认为需要记录的信息(如有)
|
|
1174
|
-
8. 用户希望记住的信息(如有)
|
|
1175
|
-
9. 需要短期记忆的信息(如有)
|
|
1185
|
+
2. 生活习惯(作息等)
|
|
1186
|
+
3. 反感点(如有)
|
|
1187
|
+
4. 对AI的期望(从对话历史中推断)
|
|
1188
|
+
5. 用户希望记住的信息(如有)
|
|
1189
|
+
6. 需要短期记忆的信息(如有)
|
|
1176
1190
|
|
|
1191
|
+
用户名:${user.usersname}
|
|
1177
1192
|
你与用户的关系:${level}
|
|
1178
1193
|
历史画像:
|
|
1179
1194
|
${existingPortrait ? "无" : existingPortrait}
|
|
@@ -1183,15 +1198,16 @@ ${history}
|
|
|
1183
1198
|
|
|
1184
1199
|
注意事项:
|
|
1185
1200
|
·用户一定是人类,其他情况是角色扮演
|
|
1186
|
-
|
|
1187
|
-
|
|
1201
|
+
·若有历史画像则需要根据发言修正历史画像内容,以发言为准,但是历史画像中不需要修改的部分可以保留
|
|
1202
|
+
·除非用户要求,否则尽量不要改动基本信息
|
|
1203
|
+
·及时删除历史画像中明确错误或多余的信息
|
|
1188
1204
|
·尤其注意用户说“记住”的部分,可能是用户希望记录的信息
|
|
1189
1205
|
·历史画像中的需要短期记忆的信息不需要保留,只需要删除旧的添加新的
|
|
1190
|
-
|
|
1191
|
-
|
|
1206
|
+
·因为在角色扮演中,用户可能会说出不符合事实的信息,需要根据事实推断,避免盲目相信或主观臆测
|
|
1207
|
+
·使用尽可能简洁的表达
|
|
1192
1208
|
·保留不确定性的表述(如"可能"、"似乎"、"用户自称")
|
|
1193
1209
|
·保持中立和客观,避免带有个人情感色彩的描述,不要添加评价或建议
|
|
1194
|
-
|
|
1210
|
+
·仅给出画像内容,不要添加额外的描述、建议、评价、注解等任何内容
|
|
1195
1211
|
·不使用markdown等标记语言,直接书写即可`
|
|
1196
1212
|
}];
|
|
1197
1213
|
}
|
|
@@ -1206,7 +1222,7 @@ ${history}
|
|
|
1206
1222
|
const dialogues = await this.getDialogues(user);
|
|
1207
1223
|
const existing = this.readPortrait(user.userid);
|
|
1208
1224
|
const userlevel = getFavorabilityLevel(user, this.getFavorabilityConfig());
|
|
1209
|
-
const messages = this.buildMessage(userlevel, dialogues.join("\n"), existing);
|
|
1225
|
+
const messages = this.buildMessage(userlevel, dialogues.join("\n"), existing, user);
|
|
1210
1226
|
logger5.info(`用户 ${user.userid} 画像生成中...`);
|
|
1211
1227
|
try {
|
|
1212
1228
|
const response = await apiClient.generateUserPortrait(user, messages);
|
|
@@ -1241,6 +1257,16 @@ ${history}
|
|
|
1241
1257
|
以下是当前用户的补充信息:{${portrait}
|
|
1242
1258
|
}
|
|
1243
1259
|
` : "";
|
|
1260
|
+
}
|
|
1261
|
+
getUserPortraitById(userId) {
|
|
1262
|
+
if (!this.config.enable_favorability)
|
|
1263
|
+
return "当前未启用好感度功能";
|
|
1264
|
+
if (!this.config.enable_user_portrait)
|
|
1265
|
+
return "当前未启用用户画像功能";
|
|
1266
|
+
const portrait = this.readPortrait(userId);
|
|
1267
|
+
return portrait ? `以下是用户${userId}的画像:{
|
|
1268
|
+
${portrait}
|
|
1269
|
+
}` : `用户${userId}没有画像`;
|
|
1244
1270
|
}
|
|
1245
1271
|
getFavorabilityConfig() {
|
|
1246
1272
|
return {
|
|
@@ -1290,6 +1316,7 @@ var SAT = class extends Sat {
|
|
|
1290
1316
|
portraitManager;
|
|
1291
1317
|
ChannelParallelCount = /* @__PURE__ */ new Map();
|
|
1292
1318
|
onlineUsers = [];
|
|
1319
|
+
game;
|
|
1293
1320
|
getAPIConfig() {
|
|
1294
1321
|
return {
|
|
1295
1322
|
baseURL: this.config.baseURL,
|
|
@@ -1327,7 +1354,6 @@ var SAT = class extends Sat {
|
|
|
1327
1354
|
getMiddlewareConfig() {
|
|
1328
1355
|
return {
|
|
1329
1356
|
private: this.config.private,
|
|
1330
|
-
mention: this.config.mention,
|
|
1331
1357
|
nick_name: this.config.nick_name,
|
|
1332
1358
|
nick_name_list: this.config.nick_name_list,
|
|
1333
1359
|
nick_name_block_words: this.config.nick_name_block_words,
|
|
@@ -1383,6 +1409,7 @@ var SAT = class extends Sat {
|
|
|
1383
1409
|
ctx.command("sat.update_user_level", "更新用户等级", { authority: 2 }).alias("更新用户").option("id", "-i <id:string>", { authority: 4 }).option("level", "-l <level:number>", { authority: 4 }).action(async ({ session, options }) => this.handleUserLevel(session, options));
|
|
1384
1410
|
ctx.command("sat.user_usage", "查看用户使用次数").alias("查询次数").action(async ({ session }) => this.handleUserUsage(session));
|
|
1385
1411
|
ctx.command("sat.add_output_censor <text:text>", "添加输出敏感词", { authority: 4 }).alias("添加输出屏蔽词").action(async ({ session }, word) => addOutputCensor(session, word, this.config.dataDir));
|
|
1412
|
+
ctx.command("sat.get_user_portrait <text:text>", "查看用户画像", { authority: 4 }).alias("查看画像").action(async ({}, userId) => this.portraitManager.getUserPortraitById(userId));
|
|
1386
1413
|
}
|
|
1387
1414
|
async handleSatCommand(session, prompt) {
|
|
1388
1415
|
const user = await ensureUserExists(this.ctx, session.userId, session.username);
|
|
@@ -1534,9 +1561,11 @@ var SAT = class extends Sat {
|
|
|
1534
1561
|
const messages = this.buildMessages(session, prompt);
|
|
1535
1562
|
logger6.info(`频道 ${session.channelId} 处理:${session.userId},剩余${this.getChannelParallelCount(session)}并发`);
|
|
1536
1563
|
const user = await getUser(this.ctx, session.userId);
|
|
1537
|
-
|
|
1564
|
+
let response = await this.apiClient.chat(user, await messages);
|
|
1538
1565
|
if (this.config.log_ask_response)
|
|
1539
1566
|
logger6.info(`Satori AI:${response.content}`);
|
|
1567
|
+
if (this.config.reasoner_filter)
|
|
1568
|
+
response.content = filterResponse(response.content, this.config.reasoner_filter_word.split("-"));
|
|
1540
1569
|
return response;
|
|
1541
1570
|
}
|
|
1542
1571
|
// 构建消息
|
|
@@ -1548,17 +1577,13 @@ var SAT = class extends Sat {
|
|
|
1548
1577
|
} else {
|
|
1549
1578
|
messages.push({ role: "system", content: await this.buildSystemPrompt(session, prompt) });
|
|
1550
1579
|
}
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
messages.push(...channelMemory);
|
|
1580
|
+
const userMemory = this.memoryManager.getChannelContext(this.config.personal_memory ? session.userId : session.channelId);
|
|
1581
|
+
messages.push(...userMemory);
|
|
1582
|
+
if (this.config.input_prompt && messages.length > 2) {
|
|
1583
|
+
messages.push({ role: "user", content: "system:" + this.config.input_prompt });
|
|
1584
|
+
messages.push({ role: "assistant", content: "好的,我会按您的要求进行回复。" });
|
|
1557
1585
|
}
|
|
1558
|
-
messages.push({
|
|
1559
|
-
role: "user",
|
|
1560
|
-
content: prompt
|
|
1561
|
-
});
|
|
1586
|
+
messages.push({ role: "user", content: prompt });
|
|
1562
1587
|
let payload = messages.map((msg) => msg.role + ":" + msg.content).join("\n");
|
|
1563
1588
|
if (this.config.log_system_prompt)
|
|
1564
1589
|
logger6.info(`系统提示:
|
|
@@ -1571,10 +1596,17 @@ ${payload}`);
|
|
|
1571
1596
|
const commonSense = await this.memoryManager.searchMemories(session, prompt, "common");
|
|
1572
1597
|
const channelDialogue = await this.memoryManager.getChannelDialogue(session);
|
|
1573
1598
|
const userMemory = await this.memoryManager.searchMemories(session, prompt);
|
|
1599
|
+
const user = await getUser(this.ctx, session.userId);
|
|
1600
|
+
if (user?.items?.["觉的衣柜"]?.count) {
|
|
1601
|
+
const clothes = user?.items?.["觉的衣柜"]?.metadata?.clothes;
|
|
1602
|
+
if (clothes)
|
|
1603
|
+
systemPrompt += `
|
|
1604
|
+
你当前的穿着(根据穿着进行对应的行为):${clothes}
|
|
1605
|
+
`;
|
|
1606
|
+
}
|
|
1574
1607
|
systemPrompt += commonSense;
|
|
1575
1608
|
systemPrompt += channelDialogue;
|
|
1576
1609
|
systemPrompt += userMemory;
|
|
1577
|
-
const user = await getUser(this.ctx, session.userId);
|
|
1578
1610
|
systemPrompt += this.portraitManager.getUserPortrait(session);
|
|
1579
1611
|
const nickName = user.items["情侣合照"]?.metadata?.userNickName;
|
|
1580
1612
|
systemPrompt += `用户的名字是:${session.username}, id是:${session.userId}`;
|
|
@@ -1594,8 +1626,6 @@ ${payload}`);
|
|
|
1594
1626
|
this.onlineUsers = this.onlineUsers.filter((id) => id !== session.userId);
|
|
1595
1627
|
if (!response)
|
|
1596
1628
|
return session.text("commands.sat.messages.no-response");
|
|
1597
|
-
if (this.config.reasoner_filter)
|
|
1598
|
-
response = filterResponse(response, this.config.reasoner_filter_word.split("-"));
|
|
1599
1629
|
const catEar = user?.items?.["猫耳发饰"]?.description && user.items["猫耳发饰"].description == "on";
|
|
1600
1630
|
const fumo = user?.items?.["觉fumo"]?.description && user.items["觉fumo"].description == "on";
|
|
1601
1631
|
const ring = user?.items?.["订婚戒指"]?.description && user.items["订婚戒指"].description == "已使用";
|
|
@@ -1648,10 +1678,10 @@ ${payload}`);
|
|
|
1648
1678
|
// 更新用户等级
|
|
1649
1679
|
async handleUserLevel(session, options) {
|
|
1650
1680
|
const userId = options.id || session.userId;
|
|
1651
|
-
const level = options.level || 1;
|
|
1652
1681
|
const user = await ensureUserExists(this.ctx, userId, session.username);
|
|
1682
|
+
const level = options.level || (user.userlevel > 1 ? user.userlevel : 1);
|
|
1653
1683
|
const enableUserKey = user?.items?.["地灵殿通行证"]?.description && user.items["地灵殿通行证"].description == "on";
|
|
1654
|
-
if (enableUserKey)
|
|
1684
|
+
if (enableUserKey || level > 3)
|
|
1655
1685
|
await this.portraitManager.generatePortrait(session, user, this.apiClient);
|
|
1656
1686
|
await updateUserLevel(this.ctx, user, level);
|
|
1657
1687
|
return session.text("commands.sat.messages.update_level_succeed", [level]);
|
|
@@ -1670,8 +1700,6 @@ ${payload}`);
|
|
|
1670
1700
|
result += session.text("commands.sat.messages.usage", [userUsage, maxUsage]) + "\n";
|
|
1671
1701
|
if (this.portraitManager.hasPortrait(user.userid))
|
|
1672
1702
|
result += "用户画像生效中\n";
|
|
1673
|
-
else
|
|
1674
|
-
result += "用户画像未生效\n";
|
|
1675
1703
|
return result;
|
|
1676
1704
|
}
|
|
1677
1705
|
// 随机中间件转接
|
package/lib/memory.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export declare class MemoryManager {
|
|
|
5
5
|
private config;
|
|
6
6
|
private channelMemories;
|
|
7
7
|
private channelDialogues;
|
|
8
|
+
private charactersToRemove;
|
|
9
|
+
private MAX_MEMORY_LENGTH;
|
|
8
10
|
constructor(ctx: Context, config: MemoryConfig);
|
|
9
11
|
updateMemories(session: Session, prompt: string, config: MemoryConfig, response: {
|
|
10
12
|
content: string;
|
package/lib/types.d.ts
CHANGED
|
@@ -76,7 +76,6 @@ export interface MemoryConfig {
|
|
|
76
76
|
}
|
|
77
77
|
export interface MiddlewareConfig {
|
|
78
78
|
private: boolean;
|
|
79
|
-
mention: boolean;
|
|
80
79
|
nick_name: boolean;
|
|
81
80
|
nick_name_list: string[];
|
|
82
81
|
nick_name_block_words: string[];
|
|
@@ -175,10 +174,10 @@ export declare namespace Sat {
|
|
|
175
174
|
enable_fixed_dialogues: boolean;
|
|
176
175
|
max_usage: number[];
|
|
177
176
|
private: boolean;
|
|
178
|
-
mention: boolean;
|
|
179
177
|
nick_name: boolean;
|
|
180
178
|
nick_name_list: string[];
|
|
181
179
|
nick_name_block_words: string[];
|
|
180
|
+
input_prompt: string;
|
|
182
181
|
reasoner_filter: boolean;
|
|
183
182
|
reasoner_filter_word: string;
|
|
184
183
|
duplicateDialogueCheck: boolean;
|
|
@@ -213,6 +212,8 @@ export declare namespace Sat {
|
|
|
213
212
|
prompt_4: string;
|
|
214
213
|
maxRetryTimes: number;
|
|
215
214
|
retry_delay_time: number;
|
|
215
|
+
enable_game: boolean;
|
|
216
|
+
enable_gobang: boolean;
|
|
216
217
|
}
|
|
217
218
|
const Config: Schema<Config>;
|
|
218
219
|
}
|
package/lib/userportrait.d.ts
CHANGED
package/lib/utils.d.ts
CHANGED
|
@@ -13,10 +13,14 @@ export declare function isErrorWithMessage(error: unknown): error is {
|
|
|
13
13
|
message: string;
|
|
14
14
|
};
|
|
15
15
|
/**
|
|
16
|
-
* 将包含 `<...name="[xxx]"...>` 格式的文本替换
|
|
17
16
|
* @param prompt 包含待处理标签的原始字符串
|
|
18
17
|
* @returns 处理后的字符串,所有匹配标签被替换为对应名称
|
|
19
18
|
*/
|
|
20
19
|
export declare function processPrompt(prompt: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* @param prompt 包含待处理标签的原始字符串
|
|
22
|
+
* @param words 需要过滤的关键词
|
|
23
|
+
* @returns 处理后的字符串,删除含有关键词的部分
|
|
24
|
+
*/
|
|
21
25
|
export declare function filterResponse(prompt: string, words: string[]): string;
|
|
22
26
|
export declare function addOutputCensor(session: Session, word: string, baseURL: string): void;
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-satori-ai",
|
|
3
3
|
"description": "基于 Satori 适配器的智能对话插件,具备用户画像、长期记忆、常识管理和好感度系统",
|
|
4
|
-
"version": "1.2.
|
|
4
|
+
"version": "1.2.8",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"types": "lib/index.d.ts",
|
|
8
8
|
"files": [
|
|
9
9
|
"lib",
|
|
10
10
|
"locales",
|
|
11
|
+
"wasm",
|
|
11
12
|
"README.md"
|
|
12
13
|
],
|
|
13
14
|
"license": "MIT",
|
package/wasm/gobang.cpp
ADDED
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
// emcc gobang.cpp -O3 -s EXPORTED_FUNCTIONS="['_decideMove', '_malloc', '_free']" -s MODULARIZE=1 -s ENVIRONMENT='node' -o gobang.js
|
|
2
|
+
|
|
3
|
+
#define _CRT_SECURE_NO_WARNINGS
|
|
4
|
+
#include <stdio.h>
|
|
5
|
+
#include <stdlib.h>
|
|
6
|
+
#include <string.h>
|
|
7
|
+
#include <string>
|
|
8
|
+
#define BOARD_SIZE 12 // 棋盘大小
|
|
9
|
+
#define EMPTY 0 // 空位
|
|
10
|
+
#define BLACK 1 // 黑棋
|
|
11
|
+
#define WHITE 2 // 白棋
|
|
12
|
+
#define INF 2147483647 // 正无穷
|
|
13
|
+
#define _INF -2147483647 // 负无穷
|
|
14
|
+
#define DEPTH 5 // 搜索深度
|
|
15
|
+
#define START "START" // 开始游戏
|
|
16
|
+
#define PLACE "PLACE" // 放置棋子
|
|
17
|
+
#define TURN "TURN" // 轮到我方下棋
|
|
18
|
+
#define END "END" // 游戏结束
|
|
19
|
+
const int BOARD_MIDDLE_1 = (BOARD_SIZE + 1) / 2 - 1;
|
|
20
|
+
const int BOARD_MIDDLE_2 = BOARD_SIZE / 2;
|
|
21
|
+
struct chessType {
|
|
22
|
+
int win5; // 连五
|
|
23
|
+
int alive4; // 活4
|
|
24
|
+
int conti4; // 冲4
|
|
25
|
+
int alive3; // 活3
|
|
26
|
+
int conti3; // 眠3
|
|
27
|
+
int jump3; // 跳3
|
|
28
|
+
int alive2; // 活2
|
|
29
|
+
int conti2; // 眠2
|
|
30
|
+
int jump2; // 跳2
|
|
31
|
+
int alive1; // 活1
|
|
32
|
+
int conti1; // 眠1
|
|
33
|
+
};
|
|
34
|
+
// 坐标结构体
|
|
35
|
+
struct coordinate {
|
|
36
|
+
int x = -1;
|
|
37
|
+
int y = -1;
|
|
38
|
+
int score = 0;
|
|
39
|
+
coordinate(int a = 0, int b = 0, int s = 0) :
|
|
40
|
+
x(a), y(b), score(s) {}
|
|
41
|
+
};
|
|
42
|
+
// 棋盘节点结构体
|
|
43
|
+
struct Nude {
|
|
44
|
+
int board[BOARD_SIZE][BOARD_SIZE] = {{0}}; // 棋盘状态
|
|
45
|
+
int state = 0; // 棋盘状态标识
|
|
46
|
+
Nude() {
|
|
47
|
+
// 初始化棋盘,放置初始棋子
|
|
48
|
+
board[BOARD_MIDDLE_1][BOARD_MIDDLE_1] = WHITE;
|
|
49
|
+
board[BOARD_MIDDLE_2][BOARD_MIDDLE_2] = WHITE;
|
|
50
|
+
board[BOARD_MIDDLE_2][BOARD_MIDDLE_1] = BLACK;
|
|
51
|
+
board[BOARD_MIDDLE_1][BOARD_MIDDLE_2] = BLACK;
|
|
52
|
+
}
|
|
53
|
+
void arr_input(int x, int y, int playerround) {
|
|
54
|
+
board[x][y] = playerround; // 在指定位置放置棋子
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
class Game {
|
|
58
|
+
public:
|
|
59
|
+
int myFlag;
|
|
60
|
+
int enemyFlag;
|
|
61
|
+
bool draw;
|
|
62
|
+
Nude MAP;
|
|
63
|
+
Game() :
|
|
64
|
+
myFlag(0), enemyFlag(0), draw(0) {}
|
|
65
|
+
};
|
|
66
|
+
// Game game;
|
|
67
|
+
void debug(const char *str) {
|
|
68
|
+
printf("DEBUG %s\n", str);
|
|
69
|
+
fflush(stdout);
|
|
70
|
+
}
|
|
71
|
+
// 判断坐标是否在棋盘范围内
|
|
72
|
+
bool judgeInRange(coordinate temp) {
|
|
73
|
+
if (temp.x < 0)
|
|
74
|
+
return false;
|
|
75
|
+
if (temp.y < 0)
|
|
76
|
+
return false;
|
|
77
|
+
if (temp.x >= BOARD_SIZE)
|
|
78
|
+
return false;
|
|
79
|
+
if (temp.y >= BOARD_SIZE)
|
|
80
|
+
return false;
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
// 获取指定位置的棋子颜色
|
|
84
|
+
int getColor(coordinate target, const Game &game) {
|
|
85
|
+
if (judgeInRange(target))
|
|
86
|
+
return game.MAP.board[target.x][target.y];
|
|
87
|
+
else
|
|
88
|
+
return game.enemyFlag;
|
|
89
|
+
}
|
|
90
|
+
// 在指定位置放置棋子
|
|
91
|
+
void place(coordinate target, int player, Game &game) {
|
|
92
|
+
game.MAP.board[target.x][target.y] = player;
|
|
93
|
+
}
|
|
94
|
+
// 快速排序函数
|
|
95
|
+
int partition(coordinate *s, int high, int low) {
|
|
96
|
+
int pi = s[high].score;
|
|
97
|
+
int i = low;
|
|
98
|
+
for (int j = low; j <= high - 1; j++) {
|
|
99
|
+
if (s[j].score >= pi) {
|
|
100
|
+
coordinate temp = s[i];
|
|
101
|
+
s[i] = s[j];
|
|
102
|
+
s[j] = temp;
|
|
103
|
+
i++;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
coordinate temp = s[i];
|
|
107
|
+
s[i] = s[high];
|
|
108
|
+
s[high] = temp;
|
|
109
|
+
return i;
|
|
110
|
+
}
|
|
111
|
+
void quickSort(coordinate *s, int high, int low = 0) {
|
|
112
|
+
if (low < high) {
|
|
113
|
+
int pi = partition(s, high, low);
|
|
114
|
+
quickSort(s, pi - 1, low);
|
|
115
|
+
quickSort(s, high, pi + 1);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// 返回p点dire方向上w距离的点
|
|
119
|
+
coordinate Neighbor(coordinate temp, int dire, int w) {
|
|
120
|
+
const int direction[4][2] = {{1, 0}, {0, 1}, {1, 1}, {1, -1}};
|
|
121
|
+
coordinate neighbor;
|
|
122
|
+
neighbor.x = temp.x + w * direction[dire][0];
|
|
123
|
+
neighbor.y = temp.y + w * direction[dire][1];
|
|
124
|
+
return neighbor;
|
|
125
|
+
}
|
|
126
|
+
// 判断点在d距离内是否有邻居
|
|
127
|
+
int hasNeighbor(coordinate temp, int d, const Game &game) {
|
|
128
|
+
// 找棋盘上有落子点d个距离内
|
|
129
|
+
for (int i = 0; i < 4; i++) {
|
|
130
|
+
for (int j = -d; j <= d; j++) {
|
|
131
|
+
if (j != 0) {
|
|
132
|
+
coordinate neighbor = Neighbor(temp, i, j);
|
|
133
|
+
if (judgeInRange(neighbor) && getColor(neighbor, game))
|
|
134
|
+
return 1;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
// 获取drie方向上的情况
|
|
141
|
+
int analyzeDire(coordinate temp, int dire, int player, char *beside, const Game &game) {
|
|
142
|
+
int length = 1;
|
|
143
|
+
int i;
|
|
144
|
+
for (i = -1;; i--) {
|
|
145
|
+
coordinate neighbor = Neighbor(temp, dire, i);
|
|
146
|
+
if (judgeInRange(neighbor) && player == game.MAP.board[neighbor.x][neighbor.y])
|
|
147
|
+
length++;
|
|
148
|
+
else {
|
|
149
|
+
for (int j = 0; j < 4; j++) {
|
|
150
|
+
neighbor = Neighbor(temp, dire, i - j);
|
|
151
|
+
if (judgeInRange(neighbor))
|
|
152
|
+
beside[j] = game.MAP.board[neighbor.x][neighbor.y];
|
|
153
|
+
else
|
|
154
|
+
beside[j] = 3 - player;
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
for (i = 1;; i++) {
|
|
160
|
+
coordinate neighbor = Neighbor(temp, dire, i);
|
|
161
|
+
if (judgeInRange(neighbor) && player == game.MAP.board[neighbor.x][neighbor.y])
|
|
162
|
+
length++;
|
|
163
|
+
else {
|
|
164
|
+
for (int j = 0; j < 4; j++) {
|
|
165
|
+
neighbor = Neighbor(temp, dire, i + j);
|
|
166
|
+
if (judgeInRange(neighbor))
|
|
167
|
+
beside[4 + j] = game.MAP.board[neighbor.x][neighbor.y];
|
|
168
|
+
else
|
|
169
|
+
beside[4 + j] = 3 - player;
|
|
170
|
+
}
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return length;
|
|
175
|
+
}
|
|
176
|
+
// 根据连子数目和边缘信息判断棋型
|
|
177
|
+
chessType typeAnalysis(coordinate p, int dire, int player, const Game &game) {
|
|
178
|
+
char b[8] = {0}; // beside
|
|
179
|
+
int length = analyzeDire(p, dire, player, b, game); // 获取p点连子的长度和两边延伸4子的信息
|
|
180
|
+
chessType temp = {0};
|
|
181
|
+
if (length >= 5)
|
|
182
|
+
temp.win5++;
|
|
183
|
+
else if (length == 4) {
|
|
184
|
+
if (b[0] == 0)
|
|
185
|
+
if (b[4] == 0)
|
|
186
|
+
temp.alive4++; // 011110
|
|
187
|
+
else
|
|
188
|
+
temp.conti4++; // 011112
|
|
189
|
+
else if (b[4] == 0)
|
|
190
|
+
temp.conti4++; // 211110
|
|
191
|
+
} else if (length == 3) {
|
|
192
|
+
if (b[0] == 0) {
|
|
193
|
+
if (b[4] == 0) {
|
|
194
|
+
if (b[1] == player) {
|
|
195
|
+
if (b[5] == player)
|
|
196
|
+
temp.conti4 += 2; // 1011101
|
|
197
|
+
else if (b[5] == player)
|
|
198
|
+
temp.conti4++; // 101110x
|
|
199
|
+
} else if (b[5] == player)
|
|
200
|
+
temp.conti4++; // x011101
|
|
201
|
+
else if (b[5] == 0 || b[1] == 0)
|
|
202
|
+
temp.alive3++; // x01110x
|
|
203
|
+
else
|
|
204
|
+
temp.conti3++; // 2011102
|
|
205
|
+
} else if (b[1] == player)
|
|
206
|
+
temp.conti4++; // 101112x
|
|
207
|
+
else if (b[1] == 0)
|
|
208
|
+
temp.conti3++; // 001112x
|
|
209
|
+
} else if (b[4] == 0) {
|
|
210
|
+
if (b[5] == player)
|
|
211
|
+
temp.conti4++; // x211101
|
|
212
|
+
else
|
|
213
|
+
temp.conti3++; // x211100
|
|
214
|
+
}
|
|
215
|
+
} else if (length == 2) {
|
|
216
|
+
if (b[0] == 0) {
|
|
217
|
+
if (b[4] == 0) {
|
|
218
|
+
if (b[1] == player) {
|
|
219
|
+
if (b[5] == player) {
|
|
220
|
+
if (b[2] == player) {
|
|
221
|
+
if (b[6] == player)
|
|
222
|
+
temp.conti4 += 2; // 11011011
|
|
223
|
+
else
|
|
224
|
+
temp.conti4++; // 1101101x
|
|
225
|
+
} else if (b[2] == 0) {
|
|
226
|
+
if (b[6] == player)
|
|
227
|
+
temp.conti4++; // 01011011
|
|
228
|
+
else
|
|
229
|
+
temp.jump3++; // 0101101x
|
|
230
|
+
} else {
|
|
231
|
+
if (b[6] == player)
|
|
232
|
+
temp.conti4++; // 21011011
|
|
233
|
+
else if (b[6] == 0)
|
|
234
|
+
temp.jump3++; // 21011010
|
|
235
|
+
else
|
|
236
|
+
temp.conti3++; // 21011012
|
|
237
|
+
}
|
|
238
|
+
} else {
|
|
239
|
+
if (b[2] == player)
|
|
240
|
+
temp.conti4++; // 110110xx
|
|
241
|
+
else if (b[2] == 0)
|
|
242
|
+
temp.jump3++; // 010110xx
|
|
243
|
+
else
|
|
244
|
+
temp.conti3++; // 210110xx
|
|
245
|
+
}
|
|
246
|
+
} else if (b[1] == 0) {
|
|
247
|
+
if (b[5] == player) {
|
|
248
|
+
if (b[6] == player)
|
|
249
|
+
temp.conti4++; // x0011011
|
|
250
|
+
else if (b[6] == 0)
|
|
251
|
+
temp.jump3++; // x0011010
|
|
252
|
+
else
|
|
253
|
+
temp.conti3++; // x0011012
|
|
254
|
+
} else
|
|
255
|
+
temp.alive2++; // x00110xx
|
|
256
|
+
} else {
|
|
257
|
+
if (b[5] == player) {
|
|
258
|
+
if (b[6] == player)
|
|
259
|
+
temp.conti4++; // x2011011
|
|
260
|
+
else if (b[6] == 0)
|
|
261
|
+
temp.jump3++; // x2011010
|
|
262
|
+
else
|
|
263
|
+
temp.conti3++; // x2011012
|
|
264
|
+
} else if (b[5] == 0)
|
|
265
|
+
temp.alive2++; // x201100x
|
|
266
|
+
}
|
|
267
|
+
} else {
|
|
268
|
+
if (b[1] == player) {
|
|
269
|
+
if (b[2] == player)
|
|
270
|
+
temp.conti4++; // 110112xx
|
|
271
|
+
else if (b[2] == 0)
|
|
272
|
+
temp.conti3++; // 010112xx
|
|
273
|
+
} else if (b[1] == 0) {
|
|
274
|
+
if (b[2] == player)
|
|
275
|
+
temp.conti3++; // 100112xx
|
|
276
|
+
else if (b[2] == 0)
|
|
277
|
+
temp.conti2++; // 000112xx
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
} else if (b[4] == 0) {
|
|
281
|
+
if (b[5] == player) {
|
|
282
|
+
if (b[6] == player)
|
|
283
|
+
temp.conti4++; // xx211011
|
|
284
|
+
else if (b[6] == 0)
|
|
285
|
+
temp.conti3++; // xx211010
|
|
286
|
+
} else if (b[5] == 0) {
|
|
287
|
+
if (b[6] == player)
|
|
288
|
+
temp.conti3++; // xx211001
|
|
289
|
+
else if (b[6] == 0)
|
|
290
|
+
temp.conti2++; // xx211000
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
} else {
|
|
294
|
+
if (b[0] == 0) {
|
|
295
|
+
if (b[4] == 0) {
|
|
296
|
+
if (b[1] == player) {
|
|
297
|
+
if (b[5] == player) {
|
|
298
|
+
if (b[2] == player) {
|
|
299
|
+
if (b[6] == player) {
|
|
300
|
+
if (b[3] == player) {
|
|
301
|
+
if (b[7] == player)
|
|
302
|
+
temp.conti4 += 2; // 111010111
|
|
303
|
+
else
|
|
304
|
+
temp.conti4++; // 11101011x
|
|
305
|
+
} else if (b[3] == 0) {
|
|
306
|
+
if (b[7] == player || b[7] == 0)
|
|
307
|
+
temp.conti4++; // 011010111/0
|
|
308
|
+
else
|
|
309
|
+
temp.jump3++; // 011010112
|
|
310
|
+
} else {
|
|
311
|
+
if (b[7] == player)
|
|
312
|
+
temp.conti4++; // 211010111
|
|
313
|
+
else if (b[7] == 0)
|
|
314
|
+
temp.jump3++; // 211010110
|
|
315
|
+
else
|
|
316
|
+
temp.conti3++; // 211010112
|
|
317
|
+
}
|
|
318
|
+
} else {
|
|
319
|
+
if (b[3] == player)
|
|
320
|
+
temp.conti4++; // 1110101xx
|
|
321
|
+
else if (b[3] == 0)
|
|
322
|
+
temp.jump3++; // 0110101xx
|
|
323
|
+
else
|
|
324
|
+
temp.conti3++; // 2110101xx
|
|
325
|
+
}
|
|
326
|
+
} else if (b[2] == 0) {
|
|
327
|
+
if (b[6] == player) {
|
|
328
|
+
if (b[7] == player)
|
|
329
|
+
temp.conti4++; // x01010111
|
|
330
|
+
else if (b[7] == 0)
|
|
331
|
+
temp.jump3++; // x01010110
|
|
332
|
+
else
|
|
333
|
+
temp.conti3++; // x01010112
|
|
334
|
+
} else
|
|
335
|
+
temp.jump2++; // x010101xx
|
|
336
|
+
} else {
|
|
337
|
+
if (b[6] == player) {
|
|
338
|
+
if (b[7] == player)
|
|
339
|
+
temp.conti4++; // x21010111
|
|
340
|
+
else if (b[7] == 0)
|
|
341
|
+
temp.jump3++; // x21010110
|
|
342
|
+
else
|
|
343
|
+
temp.conti3++; // x21010112
|
|
344
|
+
} else if (b[6] == 0)
|
|
345
|
+
temp.jump2++; // x2101010x
|
|
346
|
+
else
|
|
347
|
+
temp.conti3++; // x2101012x
|
|
348
|
+
}
|
|
349
|
+
} else {
|
|
350
|
+
if (b[2] == player) {
|
|
351
|
+
if (b[3] == player)
|
|
352
|
+
temp.conti4++; // 1110102xx
|
|
353
|
+
else if (b[3] == 0)
|
|
354
|
+
temp.jump3++; // 0110102xx
|
|
355
|
+
else
|
|
356
|
+
temp.conti3++; // 2110102xx
|
|
357
|
+
} else if (b[2] == 0)
|
|
358
|
+
temp.jump2++; // x010102xx
|
|
359
|
+
else
|
|
360
|
+
temp.conti2++; // x210102xx
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
if (b[5] == player)
|
|
364
|
+
if (b[6] == player) {
|
|
365
|
+
if (b[7] == player)
|
|
366
|
+
temp.conti4++; // xxx010111
|
|
367
|
+
else if (b[7] == 0)
|
|
368
|
+
temp.jump3++; // xxx010110
|
|
369
|
+
else
|
|
370
|
+
temp.conti3++; // xxx010112
|
|
371
|
+
} else if (b[6] == 0)
|
|
372
|
+
temp.jump2++; // xxx01010x
|
|
373
|
+
else
|
|
374
|
+
temp.conti2++; // xxx01012x
|
|
375
|
+
else
|
|
376
|
+
temp.alive1++; // xxx0102xx
|
|
377
|
+
}
|
|
378
|
+
} else {
|
|
379
|
+
if (b[1] == player) {
|
|
380
|
+
if (b[2] == player) {
|
|
381
|
+
if (b[3] == player)
|
|
382
|
+
temp.conti4++; // 111012xxx
|
|
383
|
+
else if (b[3] == 0)
|
|
384
|
+
temp.conti3++; // 011012xxx
|
|
385
|
+
} else if (b[2] == 0) {
|
|
386
|
+
if (b[3] == player)
|
|
387
|
+
temp.conti3++; // 101012xxx
|
|
388
|
+
else if (b[3] == 0)
|
|
389
|
+
temp.conti2++; // 001012xxx
|
|
390
|
+
}
|
|
391
|
+
} else
|
|
392
|
+
temp.conti1++; // xxx012xxx
|
|
393
|
+
}
|
|
394
|
+
} else {
|
|
395
|
+
if (b[4] == 0) {
|
|
396
|
+
if (b[5] == player) {
|
|
397
|
+
if (b[6] == player) {
|
|
398
|
+
if (b[7] == player)
|
|
399
|
+
temp.conti4++; // xxx210111
|
|
400
|
+
else if (b[7] == 0)
|
|
401
|
+
temp.conti3++; // xxx210110
|
|
402
|
+
} else if (b[6] == 0) {
|
|
403
|
+
if (b[7] == player)
|
|
404
|
+
temp.conti3++; // xxx210101
|
|
405
|
+
else if (b[7] == 0)
|
|
406
|
+
temp.conti2++; // xxx210100
|
|
407
|
+
}
|
|
408
|
+
} else if (b[5] == 0) {
|
|
409
|
+
if (b[6] == player) {
|
|
410
|
+
if (b[7] == player)
|
|
411
|
+
temp.conti3++; // xxx210011
|
|
412
|
+
else if (b[7] == 0)
|
|
413
|
+
temp.conti2++; // xxx210010
|
|
414
|
+
} else if (b[6] == 0) {
|
|
415
|
+
if (b[7] == player)
|
|
416
|
+
temp.conti2++; // xxx210001
|
|
417
|
+
else if (b[7] == 0)
|
|
418
|
+
temp.conti1++; // xxx210000
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return temp;
|
|
425
|
+
}
|
|
426
|
+
// 单点得分
|
|
427
|
+
int singleScore(coordinate p, int player, const Game &game) {
|
|
428
|
+
chessType chesstype = typeAnalysis(p, 0, player, game);
|
|
429
|
+
for (int i = 1; i < 4; i++) {
|
|
430
|
+
chessType temp;
|
|
431
|
+
temp = typeAnalysis(p, i, player, game);
|
|
432
|
+
chesstype.win5 += temp.win5;
|
|
433
|
+
chesstype.alive4 += temp.alive4;
|
|
434
|
+
chesstype.conti4 += temp.conti4;
|
|
435
|
+
chesstype.alive3 += temp.alive3;
|
|
436
|
+
chesstype.conti3 += temp.conti3;
|
|
437
|
+
chesstype.jump3 += temp.jump3;
|
|
438
|
+
chesstype.alive2 += temp.alive2;
|
|
439
|
+
chesstype.conti2 += temp.conti2;
|
|
440
|
+
chesstype.jump2 += temp.jump2;
|
|
441
|
+
chesstype.alive1 += temp.alive1;
|
|
442
|
+
chesstype.conti1 += temp.conti1;
|
|
443
|
+
}
|
|
444
|
+
if (chesstype.win5) // 胜
|
|
445
|
+
return 1048576;
|
|
446
|
+
int score = ((chesstype.conti4 << 12) +
|
|
447
|
+
(chesstype.alive3 << 12) + (chesstype.conti3 << 8) + (chesstype.jump3 << 10) +
|
|
448
|
+
(chesstype.alive2 << 8) + (chesstype.conti2 << 3) + (chesstype.jump2 << 6) +
|
|
449
|
+
(chesstype.alive1 << 3) + chesstype.conti1);
|
|
450
|
+
if (chesstype.alive3 >= 2 || (chesstype.conti4 && chesstype.alive3) || chesstype.alive4 || chesstype.conti4 >= 2) // 必胜?
|
|
451
|
+
score += 65536;
|
|
452
|
+
return score;
|
|
453
|
+
}
|
|
454
|
+
// 棋盘整体局面分
|
|
455
|
+
int wholeScore(int player, const Game &game) {
|
|
456
|
+
int Score = 0;
|
|
457
|
+
for (int i = 0; i < BOARD_SIZE; i++) {
|
|
458
|
+
for (int j = 0; j < BOARD_SIZE; j++) {
|
|
459
|
+
coordinate temp = {i, j, game.MAP.board[i][j]};
|
|
460
|
+
if (temp.score == 0)
|
|
461
|
+
continue;
|
|
462
|
+
if (temp.score == player) {
|
|
463
|
+
if (player == BLACK)
|
|
464
|
+
Score += singleScore(temp, player, game); // 己方落子的单点分相加
|
|
465
|
+
} else
|
|
466
|
+
Score -= singleScore(temp, 3 - player, game); // 对方落子的单点分相加
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return Score; // 己方总分减对方总分 得到当前对己方来说的局势分
|
|
470
|
+
}
|
|
471
|
+
// 启发性搜索
|
|
472
|
+
int inspireSearch(coordinate *scoreBoard, int player, Game &game) {
|
|
473
|
+
int length = 0;
|
|
474
|
+
for (int i = 0; i < BOARD_SIZE; i++) {
|
|
475
|
+
for (int j = 0; j < BOARD_SIZE; j++) {
|
|
476
|
+
if (game.MAP.board[i][j] == 0) {
|
|
477
|
+
coordinate temp = {i, j, 0};
|
|
478
|
+
if (hasNeighbor(temp, 2, game)) {
|
|
479
|
+
scoreBoard[length] = temp;
|
|
480
|
+
scoreBoard[length].score = singleScore(temp, 3 - player, game);
|
|
481
|
+
if (player == BLACK)
|
|
482
|
+
scoreBoard[length].score += singleScore(temp, player, game);
|
|
483
|
+
length++;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
// 对 scoreBoard 进行排序
|
|
489
|
+
quickSort(scoreBoard, length - 1);
|
|
490
|
+
// 找到最高分数
|
|
491
|
+
int maxScore = scoreBoard[0].score;
|
|
492
|
+
if (maxScore < 5)
|
|
493
|
+
game.draw = 1;
|
|
494
|
+
const int threshold = maxScore / 3;
|
|
495
|
+
// 找到分界线
|
|
496
|
+
int boundary = 1;
|
|
497
|
+
for (int i = 1; i < length; i++) {
|
|
498
|
+
if (scoreBoard[i].score <= threshold) {
|
|
499
|
+
boundary = i; // 更新分界线位置
|
|
500
|
+
break; // 分数低于阈值,跳出循环
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
// 更新 length 为分界线的位置
|
|
504
|
+
length = boundary;
|
|
505
|
+
// 返回 length,最多不超过
|
|
506
|
+
return length > 10 ? 10 : length;
|
|
507
|
+
}
|
|
508
|
+
// 负极大极小值搜索
|
|
509
|
+
coordinate alphaBeta(int depth, int alpha, int beta, int player, coordinate command, coordinate current, Game &game) {
|
|
510
|
+
coordinate temp = command;
|
|
511
|
+
if (depth == 0) {
|
|
512
|
+
temp.score = wholeScore(player, game);
|
|
513
|
+
return temp;
|
|
514
|
+
}
|
|
515
|
+
coordinate steps[BOARD_SIZE * BOARD_SIZE];
|
|
516
|
+
int length = inspireSearch(steps, player, game); // 搜索可落子点
|
|
517
|
+
if (length > 7 && depth > 1)
|
|
518
|
+
depth--;
|
|
519
|
+
else if (length > 2)
|
|
520
|
+
depth--;
|
|
521
|
+
for (int i = 0; i < length; i++) {
|
|
522
|
+
place(steps[i], player, game); // 模拟落子
|
|
523
|
+
temp = alphaBeta(depth, -beta, -alpha, 3 - player, steps[i], command, game); // 取负值并交换alpha和beta
|
|
524
|
+
temp.score *= -1;
|
|
525
|
+
place(steps[i], 0, game); // 还原落子
|
|
526
|
+
if (temp.score >= beta) {
|
|
527
|
+
temp.score = beta;
|
|
528
|
+
return temp; // 剪枝
|
|
529
|
+
}
|
|
530
|
+
if (temp.score > alpha)
|
|
531
|
+
alpha = temp.score;
|
|
532
|
+
}
|
|
533
|
+
temp.score = alpha;
|
|
534
|
+
return temp;
|
|
535
|
+
}
|
|
536
|
+
// 搜索入口
|
|
537
|
+
coordinate entrance(int depth, int alpha, int beta, int player, coordinate command, coordinate current, Game &game) {
|
|
538
|
+
coordinate steps[BOARD_SIZE * BOARD_SIZE]{};
|
|
539
|
+
coordinate temp;
|
|
540
|
+
coordinate best;
|
|
541
|
+
int length;
|
|
542
|
+
length = inspireSearch(steps, player, game); // 搜索可落子点
|
|
543
|
+
if (length == 1 || game.draw)
|
|
544
|
+
return steps[0];
|
|
545
|
+
for (int i = 0; i < length; i++) {
|
|
546
|
+
place(steps[i], player, game); // 模拟落子
|
|
547
|
+
temp = alphaBeta(depth, -beta, -alpha, 3 - player, steps[i], command, game); // 递归
|
|
548
|
+
temp.score *= -1;
|
|
549
|
+
place(steps[i], 0, game); // 还原落子
|
|
550
|
+
if (temp.score > alpha) {
|
|
551
|
+
alpha = temp.score;
|
|
552
|
+
best = steps[i]; // 记录最佳落子
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
best.score = alpha;
|
|
556
|
+
return best;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/*
|
|
560
|
+
* @param board: 棋盘状态
|
|
561
|
+
* @param myFlag: 我方棋子颜色
|
|
562
|
+
* @param depth: 搜索深度
|
|
563
|
+
* @return: 返回落子位置
|
|
564
|
+
*/
|
|
565
|
+
extern "C" int decideMove(const int board[BOARD_SIZE * BOARD_SIZE], int myFlag, int depth) {
|
|
566
|
+
Game localGame;
|
|
567
|
+
for (int i = 0; i < BOARD_SIZE; i++) {
|
|
568
|
+
for (int j = 0; j < BOARD_SIZE; j++) {
|
|
569
|
+
localGame.MAP.board[i][j] = board[i * BOARD_SIZE + j];
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
localGame.myFlag = myFlag;
|
|
573
|
+
localGame.enemyFlag = 3 - myFlag;
|
|
574
|
+
localGame.draw = 0;
|
|
575
|
+
|
|
576
|
+
coordinate cmd, current;
|
|
577
|
+
coordinate result = entrance(depth, _INF, INF, localGame.myFlag, cmd, current, localGame);
|
|
578
|
+
if (localGame.draw) {
|
|
579
|
+
return -1;
|
|
580
|
+
}
|
|
581
|
+
return result.score * 10000 + result.x * 100 + result.y;
|
|
582
|
+
}
|
package/wasm/gobang.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
var Module = (() => {
|
|
2
|
+
var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined;
|
|
3
|
+
if (typeof __filename != 'undefined') _scriptName = _scriptName || __filename;
|
|
4
|
+
return (
|
|
5
|
+
async function(moduleArg = {}) {
|
|
6
|
+
var moduleRtn;
|
|
7
|
+
|
|
8
|
+
var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_NODE=true;if(ENVIRONMENT_IS_NODE){}var moduleOverrides={...Module};var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("fs");var nodePath=require("path");scriptDirectory=__dirname+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(!Module["thisProgram"]&&process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];var wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAP64,HEAPU64,HEAPF64;var runtimeInitialized=false;var isFileURI=filename=>filename.startsWith("file://");function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b);Module["HEAP64"]=HEAP64=new BigInt64Array(b);Module["HEAPU64"]=HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["c"]()}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}var runDependencies=0;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var wasmBinaryFile;function findWasmBinary(){return locateFile("gobang.wasm")}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){return{a:wasmImports}}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["b"];updateMemoryViews();removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(mod,inst)=>{receiveInstance(mod,inst);resolve(mod.exports)})})}wasmBinaryFile??=findWasmBinary();try{var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}catch(e){readyPromiseReject(e);return Promise.reject(e)}}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.unshift(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.unshift(cb);var noExitRuntime=Module["noExitRuntime"]||true;var abortOnCannotGrowMemory=requestedSize=>{abort("OOM")};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;abortOnCannotGrowMemory(requestedSize)};var wasmImports={a:_emscripten_resize_heap};var wasmExports=await createWasm();var ___wasm_call_ctors=wasmExports["c"];var _decideMove=Module["_decideMove"]=wasmExports["d"];var _malloc=Module["_malloc"]=wasmExports["f"];var _free=Module["_free"]=wasmExports["g"];function run(){if(runDependencies>0){dependenciesFulfilled=run;return}preRun();if(runDependencies>0){dependenciesFulfilled=run;return}function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();moduleRtn=readyPromise;
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
return moduleRtn;
|
|
12
|
+
}
|
|
13
|
+
);
|
|
14
|
+
})();
|
|
15
|
+
if (typeof exports === 'object' && typeof module === 'object') {
|
|
16
|
+
module.exports = Module;
|
|
17
|
+
// This default export looks redundant, but it allows TS to import this
|
|
18
|
+
// commonjs style module.
|
|
19
|
+
module.exports.default = Module;
|
|
20
|
+
} else if (typeof define === 'function' && define['amd'])
|
|
21
|
+
define([], () => Module);
|
package/wasm/gobang.wasm
ADDED
|
Binary file
|