はじめに
前回KIF形式で保存された将棋の棋譜ファイルから指し手を抽出しました。touch-sp.hatenablog.com
今回は抽出した指し手をPC上で再現し、それぞれの局面をSFEN形式で保存してみます。
棋譜データからすべての局面を一括で取得する方法になります。
これを可能にするフリーソフトが見つけられなかったので自分で作ることにしました。
SFEN形式について
以下の規則を知っておく必要があります。p 歩 ポーン (pawn) l 香 ランス (lance) n 桂 ナイト (knight) s 銀 シルバー (silver) g 金 ゴールド (gold) k 王、玉 キング (king) r 飛 ルック (rook) b 角 ビショップ (bishop)
- 先手側は大文字、後手側は小文字
- 成っている場合は先頭に「+」をつける
- 持ち駒を表記する順番は飛→角→金→銀→桂→香→歩
- 同じ種類の持ち駒は先頭に個数をつける
- 両者持ち駒がない場合には「-」と表記する
- 横方向に連続する空きますは数字に変換
Pythonスクリプト
import re import collections import sys ###########最初に設定しておくもの################################## trans1 = ['1', '2', '3', '4', '5', '6', '7', '8', '9'] trans2 = ['一', '二', '三', '四', '五', '六', '七', '八', '九'] koma_moji = ['飛', '角', '金', '銀', '桂', '香', '歩'] koma_kigo = ['r', 'b', 'g', 's', 'n', 'l', 'p'] koma_kigo2 = [x.swapcase() for x in koma_kigo] + koma_kigo def make_sfen(retu): for i in range(9, 0, -1): retu = retu.replace('0'*i, str(i)) return retu ################################################################# args = sys.argv input_file = args[1] output_file = args[2] with open(input_file, 'r') as f: kifu = f.read() for x in range(9): kifu = kifu.replace(trans1[x], str(x+1)) kifu = kifu.replace(trans2[x], str(x+1)) kifu = kifu.replace('\u3000', '') sashite = [x.groups()[0] for x in re.finditer('^\s*[0-9]+\s+(\S+).*$', kifu, flags=re.MULTILINE)] for x in range(1, len(sashite)): if ('同' in sashite[x]): sashite[x] = sashite[x].replace('同', sashite[x-1][:2]) if ('投了' in sashite): sashite.remove('投了') #SFENリスト sfen = [] #持ち駒 mochigoma = [] #局面データをlistに変換 kyokumen = 'lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL' sfen.append(kyokumen + ' ' + 'b' + ' ' + '-' + ' ' + '1') for i in range(1, 10): kyokumen = kyokumen.replace(str(i), '0'*i) kyokumen = [list(x) for x in kyokumen.split('/')] #駒を動かす for i, each_sashite in enumerate(sashite): if each_sashite[-1] != '打': #駒を動かすときの処理 move = re.match('^(\d+)(\D+)\((\d+).*$', each_sashite).groups() after_x = 9 - int(move[0][0]) after_y = int(move[0][1]) - 1 before_x = 9 - int(move[2][0]) before_y = int(move[2][1]) - 1 active_koma = kyokumen[before_y][before_x] #移動元は空きになる kyokumen[before_y][before_x] = '0' #「成」ならば「+」をつける if move[1][-1] == '成': active_koma = '+' + active_koma #移動先に駒があれば持ち駒とする #大文字、小文字は入れ替える必要がある if kyokumen[after_y][after_x] != '0': mochigoma.append(kyokumen[after_y][after_x][-1].swapcase()) #移動先に駒をセットする kyokumen[after_y][after_x] = active_koma else: #駒を打つときの処理 after_x = 9 - int(each_sashite[0]) after_y = int(each_sashite[1]) -1 active_koma = koma_kigo[koma_moji.index(each_sashite[2])] if i % 2 == 0: #先手が駒を打つ active_koma = active_koma.upper() kyokumen[after_y][after_x] = active_koma mochigoma.remove(active_koma) #SFENリストに保存 mochigoma_dict = collections.Counter(''.join(mochigoma)) sfen_mochigoma = '' for x in koma_kigo2: if mochigoma_dict[x] == 1: sfen_mochigoma += x elif mochigoma_dict[x] > 1: sfen_mochigoma += (str(mochigoma_dict[x]) + x) if sfen_mochigoma =='': sfen_mochigoma = '-' sfen.append('/'.join([make_sfen(''.join(x)) for x in kyokumen]) + ' ' + ('w' if i % 2 == 0 else 'b') + ' ' + sfen_mochigoma + ' ' + str(i + 2)) with open(output_file, "w") as f: f.write('\n'.join(sfen))
使い方
読み込む棋譜データと出力先を記述して実行します。「sample.kif」ファイルを読み込んで「output.sfen」ファイルに出力する場合を示します。
上記スクリプトは今回「kifu.py」という名前になっています。
python kifu.py sample.kif output.sfen
「output.sfen」は拡張子をsfenとしていますがただのテキストファイルです。
2021年7月14日追記
続きを書きました。良かったら読んでください。touch-sp.hatenablog.com