【Python】【将棋ネタ】SFEN形式で書かれた局面をKIF形式に変換する


はじめに

正規表現の勉強としてタイトルにある通りのことをします。

上の文字列を下のように変換するのが目的です。

1+S5n1/6gk1/p2p1+B1pl/2p3s1p/1p4pN1/2PPp3P/PP1G2+bP1/1KGS1+r3/LN6L w GL2Prsn2p 102
後手の持駒:飛 銀 桂 歩二
| ・ 全 ・ ・ ・ ・ ・v桂 ・|
| ・ ・ ・ ・ ・ ・v金v玉 ・|
|v歩 ・ ・v歩 ・ 馬 ・v歩v香|
| ・ ・v歩 ・ ・ ・v銀 ・v歩|
| ・v歩 ・ ・ ・ ・v歩 桂 ・|
| ・ ・ 歩 歩v歩 ・ ・ ・ 歩|
| 歩 歩 ・ 金 ・ ・v馬 歩 ・|
| ・ 玉 金 銀 ・v龍 ・ ・ ・|
| 香 桂 ・ ・ ・ ・ ・ ・ 香|
先手の持駒:金 香 歩二
後手番

最終的にはSFEN文字列が複数書かれたテキストファイルから行数分のKIFファイルを作成できるようにしました。
「ShogiGUI」で動作確認済みです。

使い方

Pythonスクリプトが書かれたファイルを「sfen_converter.py」とします。
SFEN文字列が複数書かれたテキストファイルを「sfen.txt」とします。
以下の1行を実行するだけです。

python sfen_converter.py sfen.txt

これでたくさんのKIFファイルが作成されます。

Pythonスクリプト

import re
import sys

koma_convert = {
    '+p':'vと',
    '+l':'v杏',
    '+n':'v圭',
    '+s':'v全',
    '+r':'v龍',
    '+b':'v馬',
    '+P':' と',
    '+L':' 杏',
    '+N':' 圭',
    '+S':' 全',
    '+R':' 龍',
    '+B':' 馬',
    'p':'v歩',
    'l':'v香',
    'n':'v桂',
    's':'v銀',
    'g':'v金',
    'k':'v玉',
    'r':'v飛',
    'b':'v角',
    'P':' 歩',
    'L':' 香',
    'N':' 桂',
    'S':' 銀',
    'G':' 金',
    'K':' 玉',
    'R':' 飛',
    'B':' 角',
    '0':' ・'
}

mochigoma_convert = {
    '18':'十八',
    '17':'十七',
    '16':'十六',
    '15':'十五',
    '14':'十四',
    '13':'十三',
    '12':'十二',
    '11':'十一',
    '10':'十',
    '9':'九',
    '8':'八',
    '7':'七',
    '6':'六',
    '5':'五',
    '4':'四',
    '3':'三',
    '2':'二',
    'p':'歩',
    'l':'香',
    'n':'桂',
    's':'銀',
    'g':'金',
    'r':'飛',
    'b':'角',
    'P':'歩',
    'L':'香',
    'N':'桂',
    'S':'銀',
    'G':'金',
    'R':'飛',
    'B':'角',
}

inputFile = sys.argv[1]

with open(inputFile, 'r') as f:
    all_sfen = [x.strip() for x in f.readlines()]

for line_num, sfen in enumerate(all_sfen):
    
    sfen_split = sfen.split(' ')
    kyokumen = sfen_split[0]
    teban = sfen_split[1]
    mochigoma = sfen_split[2]

    for i in range(1, 10):
        kyokumen = kyokumen.replace(str(i), '0'*i)

    for key, value in koma_convert.items():
        kyokumen = kyokumen.replace(key, value)

    each_lines = [('|' + each_line + '|') for each_line in kyokumen.split('/')]

    mochigoma_sente = []
    mochigoma_gote = []

    while(mochigoma != ''):
        m = re.match(r'([0-9]+)([a-z|A-Z])', mochigoma)
        if(m != None):
            mochigoma = mochigoma.replace(m.group(), '')
            if(m.group().isupper()):
                mochigoma_sente.append(''.join(m.groups()[::-1]))
            else:
                mochigoma_gote.append(''.join(m.groups()[::-1]))
        
        m = re.match(r'[a-z|A-Z]', mochigoma)
        if(m != None):
            mochigoma = mochigoma.replace(m.group(), '')
            if(m.group().isupper()):
                mochigoma_sente.append(m.group())
            else:
                mochigoma_gote.append(m.group())
        
    sente_string = ' '.join(mochigoma_sente)
    gote_string = ' '.join(mochigoma_gote)

    for key, value in mochigoma_convert.items():
        sente_string = sente_string.replace(key, value)
        gote_string = gote_string.replace(key, value)

    outputFile = 'KIF_%d.kif'%line_num

    with open(outputFile, 'w', encoding='cp932') as f:
        f.write('後手の持駒:')
        if(gote_string==''):
            f.write('なし')
        else:
            f.write(gote_string)
        f.write('\n')
        f.write('\n'.join(each_lines))
        f.write('\n')
        f.write('先手の持駒:')
        if(sente_string==''):
            f.write('なし')
        else:
            f.write(sente_string)
        if(teban=='w'):
            f.write('\n')
            f.write('後手番')

注意点

Pythonは3.7以降を使用して下さい。それ以前のPythonでは辞書の順序がばらばらになるようです。
例えば「18」を「十八」に変換するような場合に辞書の順序が重要になります。

さいごに

間違いや改善点があればコメント頂けましたら幸いです。