自制帶得分和推薦走法的象棋視頻
① 到東萍象棋網可以下載許多棋譜。我用程序下載了1萬多個推薦的,grep -v (--invert-match)去掉了和棋的,還有6813個。bash+wget干的,python的連不通(要送user-agent?) 從.html提取字符串用的Python,不必用正則。有禮貌的都sleep; NUMBER need not be an integer.
pu.txt 6813行,形如:
河北金環建設象棋隊-申鵬-勝-山東省棋牌運動管理中心-張瑞峰=694712321927102226250010091972427967706289798070777362817343
用Python按走法長度升序,用了dict,長度做key和sorted(keys),setdefault(k, []).append(...)
② ffmpeg把一系列bmp壓縮成mp4,幾十步棋才幾十到數百KB,KB, KB. /dev/shm是內存,不擔心SSD磨損。編碼速度賊快,一回車完事。總之這事可干,就是CPU風扇時不時會響。引擎是從象棋巫師ElephantEye的改來的,在Intel N100上每秒636萬節點(只用了單核),中國象棋云庫查詢0.868 GNPS。Fritz 9 Chess Benchmarks, Ryzen 9 7950X, 16 x 5947, 54,204 Kn/s.
ee.py
from subprocess import Popen, PIPE class EleEye(Popen): BM = 'bestmove' def __init__(m): Popen.__init__(m, 'eemod', stdin=PIPE, stdout=PIPE, text=True) def send(m, s): m.stdin.write(s + '\n'); m.stdin.flush() def recv(m): out = '' while True: s = m.stdout.readline() out += s.replace('info ', '') if s.find(m.BM) != -1: return out def get_score(m, f): m.send('position fen ' + f + '\ngo') n = -900; fx = fy = tx = ty = -1 while True: s = m.stdout.readline() i = s.find('score ') if i != -1: i += 6 j = s.find(' ', i) n = int(s[i:j]) # j=-1: ok i = s.find(m.BM) if i != -1: if i != 0: break # nobestmove # b0c2 fx = ord(s[9]) - ord('a') tx = ord(s[11]) - ord('a') fy = ord('9') - ord(s[10]) ty = ord('9') - ord(s[12]) break if n > 900: n = 900 elif n < -900: n = -900 return n,fx,fy,tx,ty ee = EleEye()
img.py
#! /usr/bin/python3 from PIL import Image as I, ImageDraw, ImageFont #, ImageFilter def get_circle_img(i): img = I.new('RGBA', (144,144), (255,255,255,0)) # no LA d = ImageDraw.Draw(img) f = (255,255,255,255) if i else (0,0,0,255) d.ellipse((0,0,132,132), fill=f, outline=(68,68,68,255), width=8) return img.resize((36,36), I.LANCZOS) #return img.filter(ImageFilter.GaussianBlur(radius=1)) red = get_circle_img(1); hei = get_circle_img(0) FNT = '/usr/local/share/fonts/漢/漢儀旗黑.ttf' fnt = ImageFont.truetype(FNT, 20) fnt2 = ImageFont.truetype(FNT, 14) dic = {"r":"車", "n":"馬", "c":"炮", "b":"象", "B":"相", "a":"士", "A":"仕", "k":"將", "K":"帥", "p":"卒", "P":"兵"} dic['R'] = dic['r']; dic['N'] = dic['n']; dic['C'] = dic['c'] jpg = I.open('brd.jpg') def fen2img (f, nm, str=None): if nm.find('bmp') == -1: S = 40; L = 6; T = 4 brd = I.new('L', (368,400), 255) bmp = False else: S = 40; L = 6; T = 32 brd = I.new('L', (368,432), 255) bmp = True brd.paste(jpg, (L,T-4)) id = ImageDraw.Draw(brd) #id.rectangle([(0, 408), (432, 800)], fill='blue') def draw(c, x, y): if c.isupper(): img = red; tc=0 else: img = hei; tc=255 brd.paste(img, (x,y), mask=img.split()[-1]) id.text((x+6,y+4), dic[c], font=fnt, fill=tc) f = f.split('/') b = [[' '] * 9 for i in range(10)] for y in range(10): x = 0 for i in range(len(f[y])): c = f[y][i] j = ord(c) - ord('0') if j >= 1 and j <= 9: x += j else: draw(c, L+x*S, T+y*S); b[y][x] = c; x += 1 if str != None: bb = fnt2.getbbox(str) id.text(((brd.size[0]-bb[2]+bb[0])/2, 5), str, font=fnt2, fill=0) if not bmp: brd = brd.resize((736,800), I.NEAREST) brd.save(nm, compress_level=9) # smaller than jpeg if __name__ == '__main__': import sys if 'cover' in sys.argv: brd = I.new('L', (1920,1080), 255) brd.save('cover.png', compress_level=9) else: fen2img(input().replace(' ', ''), '/t/fen.png')
pu.py
#! /usr/bin/python3 from ee import * from img import * def fen2brd (): global b a = f.split('/') for y in range(10): x = 0 for i in range(len(a[y])): c = a[y][i] j = ord(c) - ord('0') if j >= 1 and j <= 9: x += j else: b[y][x] = c; x += 1 def brd2fen (): global f f = '' for y in range(10): n = 0 for x in range(9): c = b[y][x] if c == ' ': n += 1 else: if n: f += str(n) f += c; n = 0 if n: f += str(n) if y != 9: f += '/' return f def mv2str (fx, fy, tx, ty): if fx < 0: return None d = abs(fy - ty) c = b[fy][fx]; red = c < 'a' m = p[c] x = "九八七六五四三二一" if red else "123456789" m += x[fx] if fy == ty: m += "平" + x[tx] else: m += "進" if red == (fy > ty) else "退" if c in "RCPK": m += "零一二三四五六七八九"[d] elif c in "rcpk": m += "0123456789"[d] else: m += x[tx] return m p = {"r":"車", "n":"馬", "c":"炮", "b":"象", "B":"相","a":"士", "A":"仕", "k":"將", "K":"帥", "p":"卒", "P":"兵"} p['R'] = p['r']; p['N'] = p['n']; p['C'] = p['c'] b = [[' '] * 9 for i in range(10)] f = 'rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR'; fen2brd() # 開局,go 1500 7分,300 4分,bm都是兵七進一。這個分是假設走了bm. # 若實際帥五進一呢?應該用黑分如17。紅走棋后,考慮黑方的最佳應對。 a = [4]; bms = ['兵七進一']; n = 1 s = input().split('=')[1] for i in range(0, len(s), 4): # 處理(紅)實際走法 fx,fy,tx,ty = int(s[i]), int(s[i+1]), int(s[i+2]), int(s[i+3]) m = mv2str(fx,fy,tx,ty) b[ty][tx] = b[fy][fx]; b[fy][fx] = ' ' # 不能放在mv2str前 # 處理(黑) r,fx,fy,tx,ty = ee.get_score(brd2fen() + (' b' if n % 2 else ' w')) # brd2fen() modifies f a.append(r); bms.append(mv2str(fx,fy,tx,ty)) ## bm = bms[-2] if bm and m != bm: m += '_' + bm if n % 2: print(f'{n // 2 + 1}. {m}', end=' ') else: print(m, flush=True) ## fen2img(f, f'/t/{n:02d}.bmp', m + (' 黑 ' if n % 2 else ' 紅 ') + str(r)) n += 1 exit() fen2img(f, '/t/t.png') import matplotlib.pyplot as plt #import numpy as np plt.figure(figsize=(8, 6), dpi=72) x = a[::2] plt.plot(x, color='#FF0000', linewidth=1) plt.plot(a[1::2], color='#000000', linewidth=1) plt.xticks(rotation=45) n = len(x) plt.xticks(ticks=list(range(n)), labels=list(range(1, n+1))) # 下標+1作為刻度 #plt.xticks(ticks=np.arange(len(x)), labels=np.arange(1, len(x)+1)) #plt.axis('off') plt.savefig('/t/score.png', bbox_inches='tight', pil_kwargs={'compress_level':9}) #plt.show()
pu
# pu.htm里無對手信息; move用3字符編碼 if [[ $# -lt 1 ]]; then exit; fi n=$1 # n=$(expr $1 + 10) p=`sed -n ${n}p pu.txt` echo $p | xclip -f -selection clipboard if [[ $# -eq 1 ]]; then exit; fi echo 'Pressing Enter will rm -f /t/*' read rm -f /t/* t=/t/t.txt echo $p >$t echo $p | pu.py | tee -a $t echo cd /t v=t.mp4 ffmpeg -framerate 0.5 -i %02d.bmp -r 8 -vf format=gray,format=yuv420p -c:v h264 -b:v 100k -preset fast -loglevel 0 $v rm -f *.bmp #ls -lh $v | awk '{print $5}' >>$t #cp ~/xq/cover.png . cd - >/dev/null #kwrite $t 2>/dev/null &
分辨率為368x432,都是16的倍數。除了VLC播放時會截去下面一塊,ffplay,手機等都沒事。在海信A5這樣的墨水屏手機上,視頻播放器默認背景也是大白紙,一點都不閃,真是“紙上的東西動起來了”。

手抖拍糊了。黑邊為手機實際邊框。
man ffplay, While playing, q/Esc quit; p/Space pause
VLC 3.0.21 Vetinari, debian 6.1.0-18-amd64, Intel N100,播576x432的也截(4:3, 都是16的倍數)。未排除是硬件解碼器的問題。

浙公網安備 33010602011771號