【Python】【将棋ネタ】盤面を回転させる

はじめに

同一局面検索をするにあたっては盤面を回転させた局面も一緒に検索できたら良いと思います。前回の記事のアップデートです。
回転させた局面のSFENデータを取得できるようなPythonスクリプトを書きました。
SFENデータに関してはこちらを見てください。

本題

盤面を回転させる

回転前

ln5nl/6g1k/3ps2p1/2p1p1+Bgp/p2P1PP2/2r5P/P3PSKP1/LP4S2/1+r3G2L

回転後

l2g3+R1/2s4pl/1pksp3p/p5R2/2pp1p2P/PG+b1P1P2/1P2SP3/K1G6/LN5NL

ただ大文字と小文字をいれかえて逆から読んだだけのように見えますが実はそうではありません。
成り駒をあらわす「+」に注意が必要です。
そのために一つの関数を定義しました。

def make_list(str):
    skip = False    
    result = []
    for i in range(len(str)):
        if skip == True:
            skip = False
            continue
        if str[i] == '+':
            skip = True
            result.append(str[i:i+2])
        else:
            result.append(str[i])
    return result

これで「+」問題は解決します。
最終スクリプトがこのようになります。

def make_list(str):
    skip = False    
    result = []
    for i in range(len(str)):
        if skip == True:
            skip = False
            continue
        if str[i] == '+':
            skip = True
            result.append(str[i:i+2])
        else:
            result.append(str[i])
    return result

banmen = 'ln5nl/6g1k/3ps2p1/2p1p1+Bgp/p2P1PP2/2r5P/P3PSKP1/LP4S2/1+r3G2L'

banmen_row_list = [make_list(x) for x in banmen.swapcase().split('/')]
r_banmen_row_list = [x[::-1] for x in banmen_row_list[::-1]]

r_banmen = '/'.join([''.join(x) for x in r_banmen_row_list])

print(r_banmen)

持ち駒を書きかえる

回転前

GSNPbn3p

回転後

BN3Pgsnp

ただ大文字と小文字をいれかえただけのように見えますが実はそうではありません。
PCソフトに読み込ませるだけであればそれで良いのですが今回はテキスト検索を念頭にいれています。
先手→後手の順で記述する必要があります。
そのために二つの関数を定義しました。

import collections

koma_kigo = ['r', 'b', 'g', 's', 'n', 'l', 'p']
koma_kigo2 = [x.swapcase() for x in koma_kigo] + koma_kigo

def mochigoma_list_to_str(mochigoma_list):
    if len(mochigoma_list) == 0:
        return '-'
    else:
        mochigoma_dict = collections.Counter(mochigoma_list)
        mochigoma_str = ''
        for x in koma_kigo2:
            if mochigoma_dict[x] == 1:
                mochigoma_str += x
            elif mochigoma_dict[x] > 1:
                mochigoma_str += (str(mochigoma_dict[x]) + x)
        return mochigoma_str

def mochigoma_str_to_list(mochigoma_str):
    if mochigoma_str == '-':
        return []
    else:
        mochigoma_list = []
        for i in range(len(mochigoma_str)):
            if mochigoma_str[i].isdigit():
                for n in range(int(mochigoma_str[i])-1):
                    mochigoma_list.append(mochigoma_str[i+1])
            else:
                mochigoma_list.append(mochigoma_str[i])
        return mochigoma_list

最終スクリプトがこのようになります。

import collections

koma_kigo = ['r', 'b', 'g', 's', 'n', 'l', 'p']
koma_kigo2 = [x.swapcase() for x in koma_kigo] + koma_kigo

def mochigoma_list_to_str(mochigoma_list):
    if len(mochigoma_list) == 0:
        return '-'
    else:
        mochigoma_dict = collections.Counter(mochigoma_list)
        mochigoma_str = ''
        for x in koma_kigo2:
            if mochigoma_dict[x] == 1:
                mochigoma_str += x
            elif mochigoma_dict[x] > 1:
                mochigoma_str += (str(mochigoma_dict[x]) + x)
        return mochigoma_str

def mochigoma_str_to_list(mochigoma_str):
    if mochigoma_str == '-':
        return []
    else:
        mochigoma_list = []
        for i in range(len(mochigoma_str)):
            if mochigoma_str[i].isdigit():
                for n in range(int(mochigoma_str[i])-1):
                    mochigoma_list.append(mochigoma_str[i+1])
            else:
                mochigoma_list.append(mochigoma_str[i])
        return mochigoma_list

mochigoma = 'GSNPbn3p'
r_mochigoma = mochigoma_list_to_str(mochigoma_str_to_list(mochigoma.swapcase()))

print(r_mochigoma)

指し手を書きかえる

回転前

52馬(34)

回転後

58馬(76)

こちらはPythonを使えば1行で書けます。

sashite = '52馬(34)'
sashite = ''.join([str(10-int(x)) if x.isdigit() else x for x in sashite])

print(sashite)

最終的な同一局面検索のPythonスクリプト

import sys
import re
import glob
import os
import collections

def make_list(str):
    skip = False    
    result = []
    for i in range(len(str)):
        if skip == True:
            skip = False
            continue
        if str[i] == '+':
            skip = True
            result.append(str[i:i+2])
        else:
            result.append(str[i])
    return result

def mochigoma_list_to_str(mochigoma_list):
    if len(mochigoma_list) == 0:
        return '-'
    else:
        mochigoma_dict = collections.Counter(mochigoma_list)
        mochigoma_str = ''
        for x in koma_kigo2:
            if mochigoma_dict[x] == 1:
                mochigoma_str += x
            elif mochigoma_dict[x] > 1:
                mochigoma_str += (str(mochigoma_dict[x]) + x)
        return mochigoma_str

def mochigoma_str_to_list(mochigoma_str):
    if mochigoma_str == '-':
        return []
    else:
        mochigoma_list = []
        for i in range(len(mochigoma_str)):
            if mochigoma_str[i].isdigit():
                for n in range(int(mochigoma_str[i])-1):
                    mochigoma_list.append(mochigoma_str[i+1])
            else:
                mochigoma_list.append(mochigoma_str[i])
        return mochigoma_list

koma_kigo = ['r', 'b', 'g', 's', 'n', 'l', 'p']
koma_kigo2 = [x.swapcase() for x in koma_kigo] + koma_kigo

sfen_files = glob.glob('sfen/*.sfen')

while True:
    input_text = input('sfenを入力して下さい、終了時はq\n')

    if input_text == 'q':
        break
    else:
        input_text = input_text.replace('sfen ', '')
        print('\n')
    
    text_list = input_text.split(' ')

    if len(text_list) !=  4:
        print('データが不適です')
        continue

    banmen, teban, mochigoma, tesu = text_list

    banmen_row_list = [make_list(x) for x in banmen.swapcase().split('/')]
    r_banmen_row_list = [x[::-1] for x in banmen_row_list[::-1]]

    r_banmen = '/'.join([''.join(x) for x in r_banmen_row_list])

    r_mochigoma = mochigoma_list_to_str(mochigoma_str_to_list(mochigoma.swapcase()))

    normal_pattern = r'^' + re.escape(banmen) + r'\s([w|b])\s' + re.escape(mochigoma) + r'\s' + r'(\d+)$'
    rotate_pattern = r'^' + re.escape(r_banmen) + r'\s([w|b])\s' + re.escape(r_mochigoma) + r'\s' + r'(\d+)$'
    
    for each_file in sfen_files:
        with open(each_file, 'r') as f:
            my_data = f.read()

        normal_result = re.finditer(normal_pattern, my_data, flags = re.MULTILINE)
        rotate_result = re.finditer(rotate_pattern, my_data, flags = re.MULTILINE)
        
        normal_result_list = list(normal_result)
        rotate_result_list = list(rotate_result)


        if (len(normal_result_list) + len(rotate_result_list)) != 0:
            print(os.path.basename(each_file))
            sente = re.search(r'^先手:.*$', my_data, flags = re.MULTILINE).group()
            gote = re.search(r'^後手:.*$', my_data, flags = re.MULTILINE).group()
            
            if len(normal_result_list) != 0:
                print('通常検索')
                for mm in normal_result_list:
                    teban = mm.groups()[0]
                    teban_text = sente if teban == 'b' else gote
                    sashite_pattern = r'^' + re.escape(mm.groups()[1]) + r'\s(\S+)$'
                    sashite_search = re.search(sashite_pattern, my_data, flags = re.MULTILINE) 
                    sashite = sashite_search.groups()[0]
                    print('%s手目 %s が %s を指しました\n'%(mm.groups()[1], teban_text, sashite))
            
            if len(rotate_result_list) != 0:  
                print('反転検索')  
                for mm in rotate_result_list:
                    teban = mm.groups()[0]
                    teban_text = sente if teban == 'b' else gote
                    sashite_pattern = r'^' + re.escape(mm.groups()[1]) + r'\s(\S+)$'
                    sashite_search = re.search(sashite_pattern, my_data, flags = re.MULTILINE) 
                    sashite = sashite_search.groups()[0]
                    sashite = ''.join([str(10-int(x)) if x.isdigit() else x for x in sashite])
                    print('%s手目 %s が %s を指しました\n'%(mm.groups()[1], teban_text, sashite))
sys.exit()