每步延迟: 深度/云库秒回时保证行棋节奏(计入 thinking_time); 油猴 v1.7.0, bot 加 --move-delay
This commit is contained in:
1 parent
1ed6a176cb
commit
adf3abb5fc
2 files changed
+39
-4
No files matched your search
+28
-4
@@ -2,7 +2,7 @@
|
|||||||
// @name Xiangqi.com 本地皮卡鱼引擎对接
|
// @name Xiangqi.com 本地皮卡鱼引擎对接
|
||||||
// @name:en Xiangqi.com Local Pikafish Engine
|
// @name:en Xiangqi.com Local Pikafish Engine
|
||||||
// @namespace awesome-xiangqi
|
// @namespace awesome-xiangqi
|
||||||
// @version 1.6.0
|
// @version 1.7.0
|
||||||
// @description 在 play.xiangqi.com 对局中调用本地皮卡鱼引擎(http://127.0.0.1:9377)自动/手动走子
|
// @description 在 play.xiangqi.com 对局中调用本地皮卡鱼引擎(http://127.0.0.1:9377)自动/手动走子
|
||||||
// @description:en Hook the site WebSocket and let a local Pikafish engine play for you
|
// @description:en Hook the site WebSocket and let a local Pikafish engine play for you
|
||||||
// @match https://play.xiangqi.com/*
|
// @match https://play.xiangqi.com/*
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
const ENGINE_URL = "http://127.0.0.1:9377";
|
const ENGINE_URL = "http://127.0.0.1:9377";
|
||||||
const NS = "42/xiangqi,";
|
const NS = "42/xiangqi,";
|
||||||
const FIRST_MOVE_EXTRA_SEC = 5;
|
const FIRST_MOVE_EXTRA_SEC = 5;
|
||||||
const VERSION = "1.6.0";
|
const VERSION = "1.7.0";
|
||||||
const PATCH_MARK = Symbol.for("xiangqi.pikafish.ws.patched");
|
const PATCH_MARK = Symbol.for("xiangqi.pikafish.ws.patched");
|
||||||
const uW = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
|
const uW = typeof unsafeWindow !== "undefined" ? unsafeWindow : window;
|
||||||
|
|
||||||
@@ -53,6 +53,7 @@
|
|||||||
movetime: Math.max(50, +storage.get("pf_movetime", 800) || 800),
|
movetime: Math.max(50, +storage.get("pf_movetime", 800) || 800),
|
||||||
depth: Math.max(0, +storage.get("pf_depth", 0) || 0),
|
depth: Math.max(0, +storage.get("pf_depth", 0) || 0),
|
||||||
firstDelay: Math.max(0, +storage.get("pf_firstdelay", 0) || 0),
|
firstDelay: Math.max(0, +storage.get("pf_firstdelay", 0) || 0),
|
||||||
|
moveDelay: Math.max(0, +storage.get("pf_movedelay", 1) || 1), // 每步最小间隔秒(深度/云库秒回时保证节奏)
|
||||||
sideOverride: ["auto", "red", "black"].includes(
|
sideOverride: ["auto", "red", "black"].includes(
|
||||||
storage.get("pf_side", "auto"),
|
storage.get("pf_side", "auto"),
|
||||||
)
|
)
|
||||||
@@ -67,6 +68,7 @@
|
|||||||
storage.set("pf_movetime", cfg.movetime);
|
storage.set("pf_movetime", cfg.movetime);
|
||||||
storage.set("pf_depth", cfg.depth);
|
storage.set("pf_depth", cfg.depth);
|
||||||
storage.set("pf_firstdelay", cfg.firstDelay);
|
storage.set("pf_firstdelay", cfg.firstDelay);
|
||||||
|
storage.set("pf_movedelay", cfg.moveDelay);
|
||||||
storage.set("pf_side", cfg.sideOverride);
|
storage.set("pf_side", cfg.sideOverride);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,6 +80,7 @@
|
|||||||
over: false,
|
over: false,
|
||||||
thinking: false,
|
thinking: false,
|
||||||
firstMoveDeadline: 0,
|
firstMoveDeadline: 0,
|
||||||
|
lastMoveAt: 0,
|
||||||
reqId: 0,
|
reqId: 0,
|
||||||
turnTimer: 0,
|
turnTimer: 0,
|
||||||
sideCache: { at: 0, side: null },
|
sideCache: { at: 0, side: null },
|
||||||
@@ -293,9 +296,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const reqId = ++G.reqId;
|
// 每步最小间隔(深度/云库秒回时保持行棋节奏), 计入 thinking_time
|
||||||
G.thinking = true;
|
G.thinking = true;
|
||||||
setStatus("皮卡鱼思考中…", true);
|
if (!force && cfg.moveDelay > 0) {
|
||||||
|
const remainMs = cfg.moveDelay * 1000 - (Date.now() - (G.lastMoveAt || 0));
|
||||||
|
if (remainMs > 0) {
|
||||||
|
setStatus(`行棋节奏延迟: ${Math.ceil(remainMs / 1000)}s`, true);
|
||||||
|
await new Promise((r) => setTimeout(r, remainMs));
|
||||||
|
if (G.over || !isMyTurn()) {
|
||||||
|
G.thinking = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const reqId = ++G.reqId;
|
||||||
try {
|
try {
|
||||||
const out = await engineBestmove({
|
const out = await engineBestmove({
|
||||||
moves: G.moves.slice(),
|
moves: G.moves.slice(),
|
||||||
@@ -320,6 +334,7 @@
|
|||||||
throw new Error("未找到 socket 连接");
|
throw new Error("未找到 socket 连接");
|
||||||
|
|
||||||
G.moves.push(move);
|
G.moves.push(move);
|
||||||
|
G.lastMoveAt = Date.now();
|
||||||
setScore(
|
setScore(
|
||||||
`${out.source === "cloud" ? "☁ 云库" : "♞ 引擎"} · 评分 ${out.score ?? "?"} · ${out.ms ?? "?"}ms`,
|
`${out.source === "cloud" ? "☁ 云库" : "♞ 引擎"} · 评分 ${out.score ?? "?"} · ${out.ms ?? "?"}ms`,
|
||||||
);
|
);
|
||||||
@@ -507,6 +522,7 @@
|
|||||||
<div class="row"><span>每步用时(ms)</span><input type="number" id="pf-mt" min="50" step="50"></div>
|
<div class="row"><span>每步用时(ms)</span><input type="number" id="pf-mt" min="50" step="50"></div>
|
||||||
<div class="row"><span>深度(0=用时间)</span><input type="number" id="pf-depth" min="0" max="40" step="1"></div>
|
<div class="row"><span>深度(0=用时间)</span><input type="number" id="pf-depth" min="0" max="40" step="1"></div>
|
||||||
<div class="row"><span>首步延迟(秒+5)</span><input type="number" id="pf-delay" min="0" max="120" step="1"></div>
|
<div class="row"><span>首步延迟(秒+5)</span><input type="number" id="pf-delay" min="0" max="120" step="1"></div>
|
||||||
|
<div class="row"><span>每步延迟(秒)</span><input type="number" id="pf-mvdelay" min="0" max="60" step="1"></div>
|
||||||
<div class="row"><span>我方执子</span>
|
<div class="row"><span>我方执子</span>
|
||||||
<select id="pf-side"><option value="auto">自动识别</option>
|
<select id="pf-side"><option value="auto">自动识别</option>
|
||||||
<option value="red">红方</option><option value="black">黑方</option></select></div>
|
<option value="red">红方</option><option value="black">黑方</option></select></div>
|
||||||
@@ -569,6 +585,14 @@
|
|||||||
saveCfg();
|
saveCfg();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mv = panel.querySelector("#pf-mvdelay");
|
||||||
|
mv.value = cfg.moveDelay;
|
||||||
|
mv.addEventListener("change", () => {
|
||||||
|
cfg.moveDelay = clampInt(mv.value, 0, 60, 1);
|
||||||
|
mv.value = cfg.moveDelay;
|
||||||
|
saveCfg();
|
||||||
|
});
|
||||||
|
|
||||||
const sel = panel.querySelector("#pf-side");
|
const sel = panel.querySelector("#pf-side");
|
||||||
sel.value = cfg.sideOverride;
|
sel.value = cfg.sideOverride;
|
||||||
sel.addEventListener("change", () => {
|
sel.addEventListener("change", () => {
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ class OnlineBot:
|
|||||||
self.thinking = False
|
self.thinking = False
|
||||||
self.game_over = False
|
self.game_over = False
|
||||||
self.first_deadline = None
|
self.first_deadline = None
|
||||||
|
self.last_move_at = 0.0
|
||||||
self._ack = 0
|
self._ack = 0
|
||||||
self._send_lock = asyncio.Lock()
|
self._send_lock = asyncio.Lock()
|
||||||
|
|
||||||
@@ -199,6 +200,14 @@ class OnlineBot:
|
|||||||
|
|
||||||
fen = fen_from_moves(self.moves)
|
fen = fen_from_moves(self.moves)
|
||||||
t0 = asyncio.get_event_loop().time()
|
t0 = asyncio.get_event_loop().time()
|
||||||
|
# 每步最小间隔(深度/云库秒回时保持节奏)
|
||||||
|
if self.args.move_delay:
|
||||||
|
gap = self.args.move_delay - (t0 - self.last_move_at)
|
||||||
|
if gap > 0:
|
||||||
|
log.info('[game] 行棋节奏延迟 %.1fs', gap)
|
||||||
|
await asyncio.sleep(gap)
|
||||||
|
if self.game_over or not self.is_my_turn():
|
||||||
|
return
|
||||||
mv = score = None
|
mv = score = None
|
||||||
source = 'engine'
|
source = 'engine'
|
||||||
|
|
||||||
@@ -215,6 +224,7 @@ class OnlineBot:
|
|||||||
|
|
||||||
site_mv = ucci_to_site(mv)
|
site_mv = ucci_to_site(mv)
|
||||||
self.moves.append(mv)
|
self.moves.append(mv)
|
||||||
|
self.last_move_at = asyncio.get_event_loop().time()
|
||||||
think_ms = int((asyncio.get_event_loop().time() - t0) * 1000)
|
think_ms = int((asyncio.get_event_loop().time() - t0) * 1000)
|
||||||
log.info('[game] %s: %s → %s %s %dms', source, mv, site_mv, score, think_ms)
|
log.info('[game] %s: %s → %s %s %dms', source, mv, site_mv, score, think_ms)
|
||||||
self.emit('g_v2.move', {'game_id': self.game_id, 'move': site_mv,
|
self.emit('g_v2.move', {'game_id': self.game_id, 'move': site_mv,
|
||||||
@@ -244,6 +254,7 @@ def main():
|
|||||||
p.add_argument('--hash', type=int, default=256, help='哈希表 MB')
|
p.add_argument('--hash', type=int, default=256, help='哈希表 MB')
|
||||||
p.add_argument('--config', default='10/2', help='匹配时制 总分钟/加秒')
|
p.add_argument('--config', default='10/2', help='匹配时制 总分钟/加秒')
|
||||||
p.add_argument('--first-delay', type=int, default=0, help='红方首步延迟秒(实际=此值+5)')
|
p.add_argument('--first-delay', type=int, default=0, help='红方首步延迟秒(实际=此值+5)')
|
||||||
|
p.add_argument('--move-delay', type=float, default=1.0, help='每步最小间隔秒(深度/云库秒回时保持节奏)')
|
||||||
p.add_argument('--cloud-min-score', type=int, default=15, help='云库采纳阈值(厘兵)')
|
p.add_argument('--cloud-min-score', type=int, default=15, help='云库采纳阈值(厘兵)')
|
||||||
p.add_argument('--no-cloud', action='store_true', help='禁用云库')
|
p.add_argument('--no-cloud', action='store_true', help='禁用云库')
|
||||||
args = p.parse_args()
|
args = p.parse_args()
|
||||||
|
|||||||
Reference in new issue
Block a user