① 到东萍象棋网可以下载许多棋谱。我用程序下载了1万多个推荐的,grep -v (--invert-match)去掉了和棋的,还有6813个。bash+wget干的,python的连不通(要送user-agent?)
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上每秒600万节点,中国象棋云库查询是0.98几,单位是G。
ee.py

from subprocess import Popen, PIPEclass 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 outdef get_score(m, f):m.send('position fen ' + f + '\ngo')n = -900; fx = fy = tx = ty = -1while True:s = m.stdout.readline()i = s.find('score ')if i != -1:i += 6j = s.find(' ', i)n = int(s[i:j]) # j=-1: oki = s.find(m.BM)if i != -1:if i != 0: break # nobestmove# b0c2fx = ord(s[9]) - ord('a')tx = ord(s[11]) - ord('a')fy = ord('9') - ord(s[10])ty = ord('9') - ord(s[12])breakif n > 900: n = 900elif n < -900: n = -900return n,fx,fy,tx,tyee = EleEye()
img.py

#! /usr/bin/python3from PIL import Image as I, ImageDraw, ImageFont #, ImageFilterdef get_circle_img(i):img = I.new('RGBA', (144,144), (255,255,255,0)) # no LAd = 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 = 4brd = I.new('L', (368,400), 255)bmp = Falseelse:S = 40; L = 6; T = 32brd = I.new('L', (368,432), 255)bmp = Truebrd.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=0else: img = hei; tc=255brd.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 = 0for i in range(len(f[y])):c = f[y][i]j = ord(c) - ord('0')if j >= 1 and j <= 9: x += jelse:draw(c, L+x*S, T+y*S); b[y][x] = c; x += 1if 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 jpegif __name__ == '__main__':import sysif '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 ba = f.split('/')for y in range(10):x = 0for i in range(len(a[y])):c = a[y][i]j = ord(c) - ord('0')if j >= 1 and j <= 9: x += jelse: b[y][x] = c; x += 1def brd2fen ():global ff = ''for y in range(10):n = 0for x in range(9):c = b[y][x]if c == ' ': n += 1else:if n: f += str(n)f += c; n = 0if n: f += str(n)if y != 9: f += '/'return fdef mv2str (fx, fy, tx, ty):if fx < 0: return Noned = 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 mp = {"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 += '_' + bmif 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 += 1exit()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; fin=$1 # n=$(expr $1 + 10) p=`sed -n ${n}p pu.txt`echo $p | xclip -f -selection clipboardif [[ $# -eq 1 ]]; then exit; fiecho 'Pressing Enter will rm -f /t/*' read rm -f /t/*t=/t/t.txt echo $p >$t echo $p | pu.py | tee -a $t echocd /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的倍数)。未排除是硬件解码器的问题。