精简: 移除油猴脚本与HTTP服务端, 仅保留在线机器人(云库/深度/延迟功能全部收敛到 bot)
This commit is contained in:
1 parent
ddcf8c4bec
commit
59acf972b8
6 files changed
+6
-773
No files matched your search
@@ -1 +1 @@
|
||||
from xiangqi_bot import coords, cloud, pikafish, server, bot
|
||||
# -*- coding: utf-8 -*-
|
||||
@@ -1,140 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""本地 HTTP 引擎服务: 云库优先, 皮卡鱼兜底。供油猴脚本调用。"""
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
from aiohttp import web
|
||||
|
||||
from .cloud import CloudBook
|
||||
from .coords import fen_from_moves, site_to_ucci, ucci_to_site
|
||||
from .pikafish import Pikafish
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(name)s %(message)s')
|
||||
log = logging.getLogger('xiangqi.server')
|
||||
|
||||
|
||||
class Service:
|
||||
def __init__(self, args):
|
||||
self.args = args
|
||||
self.cloud = CloudBook(min_score=args.cloud_min_score)
|
||||
self.engine = None # 惰性启动(首个请求前/启动时)
|
||||
|
||||
async def startup(self, app):
|
||||
self.engine = await Pikafish(
|
||||
threads=self.args.threads, hash_mb=self.args.hash,
|
||||
movetime=self.args.movetime,
|
||||
log=(lambda m: log.debug(m)) if self.args.quiet else (lambda m: log.info(m)),
|
||||
).start()
|
||||
|
||||
async def shutdown(self, app):
|
||||
if self.engine:
|
||||
await self.engine.quit()
|
||||
await self.cloud.close()
|
||||
|
||||
async def bestmove(self, moves_site, fen=None, movetime=None, depth=None,
|
||||
req_cloud=None, cloud_always=None):
|
||||
moves_ucci = [site_to_ucci(m) for m in moves_site]
|
||||
fen = fen or fen_from_moves(moves_ucci)
|
||||
|
||||
# 1. 云库优先(请求可显式关闭; 全程云库模式无视分数门槛和深度强制)
|
||||
use_cloud = req_cloud if req_cloud is not None else True
|
||||
if use_cloud:
|
||||
try:
|
||||
cloud_hit = await self.cloud.best(
|
||||
fen, moves_count=len(moves_ucci),
|
||||
any_score=bool(cloud_always))
|
||||
except Exception as e:
|
||||
log.warning('cloud error: %s', e)
|
||||
cloud_hit = None
|
||||
if cloud_hit:
|
||||
mv, score, note = cloud_hit
|
||||
return {'ok': True, 'source': 'cloud', 'move': ucci_to_site(mv),
|
||||
'score': f'cp {score}', 'note': note, 'ms': 0}
|
||||
|
||||
# 2. 皮卡鱼兜底
|
||||
try:
|
||||
mv, score = await self.engine.bestmove(moves=moves_ucci, fen=fen,
|
||||
movetime=movetime, depth=depth)
|
||||
except Exception:
|
||||
log.exception('engine crashed, restarting')
|
||||
await self.engine.restart()
|
||||
raise
|
||||
return {'ok': True, 'source': 'engine', 'move': ucci_to_site(mv),
|
||||
'score': score, 'note': None}
|
||||
|
||||
|
||||
@web.middleware
|
||||
async def cors_middleware(request, handler):
|
||||
"""油猴脚本从 https 页面跨域调用本服务: 处理 OPTIONS 预检并补 CORS 头。"""
|
||||
if request.method == 'OPTIONS':
|
||||
resp = web.Response(status=204)
|
||||
else:
|
||||
resp = await handler(request)
|
||||
resp.headers['Access-Control-Allow-Origin'] = '*'
|
||||
resp.headers['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS'
|
||||
resp.headers['Access-Control-Allow-Headers'] = 'Content-Type'
|
||||
return resp
|
||||
|
||||
|
||||
def make_app(args):
|
||||
svc = Service(args)
|
||||
|
||||
async def health(_):
|
||||
return web.json_response({
|
||||
'ok': True,
|
||||
'engine': svc.engine.path if svc.engine else None,
|
||||
'cloud': {'min_score': svc.cloud.min_score,
|
||||
'hit': svc.cloud.ok_count, 'miss': svc.cloud.miss_count},
|
||||
})
|
||||
|
||||
async def bestmove(request):
|
||||
try:
|
||||
req = await request.json()
|
||||
except Exception:
|
||||
return web.json_response({'ok': False, 'error': 'bad json'}, status=400)
|
||||
moves = req.get('moves', [])
|
||||
if not isinstance(moves, list) or not all(isinstance(m, str) for m in moves):
|
||||
return web.json_response({'ok': False, 'error': 'moves 格式错误'}, status=400)
|
||||
try:
|
||||
out = await svc.bestmove(
|
||||
moves,
|
||||
fen=req.get('fen'),
|
||||
movetime=req.get('movetime'),
|
||||
depth=req.get('depth'),
|
||||
req_cloud=req.get('cloud'),
|
||||
cloud_always=req.get('cloud_always'),
|
||||
)
|
||||
return web.json_response(out)
|
||||
except Exception as e:
|
||||
return web.json_response({'ok': False, 'error': str(e)}, status=500)
|
||||
|
||||
app = web.Application(middlewares=[cors_middleware])
|
||||
app.router.add_get('/health', health)
|
||||
app.router.add_post('/bestmove', bestmove)
|
||||
app.on_startup.append(svc.startup)
|
||||
app.on_cleanup.append(svc.shutdown)
|
||||
return app
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description='皮卡鱼 + 云库本地服务')
|
||||
p.add_argument('--port', type=int, default=9377)
|
||||
p.add_argument('--movetime', type=int, default=800, help='每步思考毫秒(深度未指定时)')
|
||||
p.add_argument('--depth', type=int, default=None, help='固定深度(优先于 movetime)')
|
||||
p.add_argument('--threads', type=int, default=None, help='线程数(默认=CPU核心数)')
|
||||
p.add_argument('--hash', type=int, default=256, help='哈希表 MB')
|
||||
p.add_argument('--cloud-min-score', type=int, default=15,
|
||||
help='云库着法采纳阈值(厘兵, 红方视角)')
|
||||
p.add_argument('--quiet', action='store_true', help='不打印引擎 info')
|
||||
args = p.parse_args()
|
||||
|
||||
app = make_app(args)
|
||||
mode = f'depth={args.depth}' if args.depth else f'movetime={args.movetime}ms'
|
||||
print(f'[server] http://127.0.0.1:{args.port} ({mode}, '
|
||||
f'threads={args.threads or "max"}, hash={args.hash}MB, '
|
||||
f'cloud_min_score={args.cloud_min_score})', flush=True)
|
||||
web.run_app(app, host='127.0.0.1', port=args.port, print=None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in new issue
Block a user