「十六式いろは」のあれこれ

コンピュータ将棋ソフトのエンジン「十六式いろは」の開発者のブログです。

「将スタ -将棋ソフトスタジオ-」の公開

★2022年4月18日までに将スタシリーズをダウンロードして
ファイルを展開している方へ!
お手数ですが、以下のページを読んで対応をお願い致します。

https://16-168.hatenablog.jp/entry/2022/04/18/223512

※公開日 2022-01-17
※更新日 2022-04-18
 あなたスタイル機能等を追加しました。7-zip問題対応済み。 →更新内容

ディープラーニング将棋AI制作ソフト

「将スタ -将棋ソフトスタジオ-」

将スタのイメージ画像

目的別おすすめ将スタ

  • 自分っぽい将棋ソフト(クローンAI?)を作りたい→【onlyあなたスタイル】
  • 試したい、ファイルサイズが小さめがいい→【lite版】:このページ
  • 将スタのフル機能を使いたい→【将スタ(無印)】:このページ
  • 将スタの前のバージョンを使っている→【差分版】:このページ

↓ 使い方動画 ↓
youtu.be

lite版、あなたスタイル以外の新機能の使い方(30:18~)
あなたスタイル機能の使い方は、onlyあなたスタイルのページへ
youtu.be

差分版の使い方(36:44~) youtu.be

お待たせしました!
ついに!堂々ここに登場!(←え?コソコソじゃないの?w)

「信じられる?指し手に、あなたが重なって見えたんだよ……」

待望の新機能!「あなたスタイル」(っぽいAI)を搭載しました!
なんと!たった10局の棋譜あれば、あなた風な指し手を再現!?
※期待しすぎちゃダメですよ?(ノ∀`)あちゃーw
→機能限定な「onlyあなたスタイル」版もご用意!
「将スタ -将棋ソフトスタジオ- onlyあなたスタイル」ページへ

プログラミングの知識がなくても、
将棋ソフトが作れちゃいますっ!( ´∀` )やったぜ

一言で例えるなら、RPG●クールみたいな!
(見た目は●クールと違って、しょぼいけどナw)

プログラミング知識がある人はこの書籍で
もっと改造ができちゃいますよ( ´∀` )

「通常版(無印)」「lite版」「差分」をご用意しました。
※「onlyあなたスタイル」版はこのページへ

↓↓↓ 「将スタ -将棋ソフトスタジオ-」 ↓↓↓
ダウンロード「shogi-studio.exe」(1.93GB)

drive.google.com

↓↓↓ 「将スタ -将棋ソフトスタジオ- lite」 ↓↓↓
ダウンロード「shogi-studio_lite.exe」(480MB)

※同梱しているfloodgateの棋譜2018年度の分のみです。
※「あなたスタイル」機能は搭載しています。
drive.google.com

↓↓↓ 「将スタ -将棋ソフトスタジオ-」差分 ↓↓↓
ダウンロード「shogi-studio_diff.exe」(371MB)

※ダウンロードしたファイルを前バージョン「shogi-studio」フォルダと
 同じ場所で実行してください。選択肢は「Yes to All」で。
drive.google.com

AobaZeroの棋譜から、やねうら王形式の定跡ファイルつくってみた

どんなことをしたん?

自己対戦のみで棋力を高める将棋エンジン「AobaZero」が
残した棋譜から定跡ファイル(db形式)を作ってみたテストです。
←つまり人間が指していない棋譜。強いかどうかは今回は気にせず。
 ちょっとやってみたかったんですよ、以前から。

手順

1. AobaZeroの棋譜を手に入れたり

AobaZero
http://www.yss-aya.com/aobazero/
↑のリンク先のページ下方にある棋譜のリンクから
最初の方の棋譜を持ってこようかな、という感じで
「arch000047060000.csa.xz」をダウンロードします。

圧縮・解凍ソフト 7-Zip
https://sevenzip.osdn.jp/
↑の7-Zipでダウンロードしたファイルを解凍します。

出てきた棋譜ファイル(csa形式)を
メモ帳等のテキストエディタで開いちゃって。
←ファイルサイズが100メガ級なので、
 開くだけで何十秒かかかるかも、ひょえー。

2. AobaZeroの棋譜を書き変えたり

1行目に「V2.2」という文字列を追加して保存します。

'no000047060000 09/30/21
'w 3702 (crc64:358ea878f5e11fbe), autousi 1.8, usi-engine 15
'-q -r 0.0 -p 800 -n -m 30 -w ./weight-save/w000000003666.txt -e 0
PI
+
+6978KI,'v=0.579,800,2726FU,554,6978KI,197,9796FU,9,4958KI,9,4948KI,8,5948OU,7,3736FU,5,3938GI,5,2878HI,3,8786FU,3
-8384FU,'v=0.425,996,8384FU,720,4132KI,226,5162OU,38,8272HI,9,4142KI,3
以下略

↑のような感じものを↓のようにする。

V2.2
'no000047060000 09/30/21
'w 3702 (crc64:358ea878f5e11fbe), autousi 1.8, usi-engine 15
'-q -r 0.0 -p 800 -n -m 30 -w ./weight-save/w000000003666.txt -e 0
PI
+
+6978KI,'v=0.579,800,2726FU,554,6978KI,197,9796FU,9,4958KI,9,4948KI,8,5948OU,7,3736FU,5,3938GI,5,2878HI,3,8786FU,3
-8384FU,'v=0.425,996,8384FU,720,4132KI,226,5162OU,38,8272HI,9,4142KI,3
以下略

3. csa形式からsfen形式に変換したり

Blunder.Converter: 棋譜変換ツール
https://github.com/ak110/Blunder.Converter
↑将スタにも同梱している棋譜変換ツールで
やねうら王形式の定跡ファイルに変換するのに必要な
sfen形式に変換します。
←ファイル名はめんどいのでそのまま「arch000047060000.sfen」
←変換するために変換するとか、そんなもんですよネ、世の中。

4. やねうら王の定跡ファイルに変換したり

Release やねうら王 実行ファイル詰め合わせ v7.6.1+20220511b
https://github.com/mizar/YaneuraOu/releases/tag/v7.6.1%2B20220511b.master
↑「YaneuraOu-v7.6.1+20220511b.master-windows.7z (162.57 MB)」を
 ダウンロードしてファイルを解凍します。
←★注意★
 「Suisho5-YaneuraOu-v7.6.1+20220511b.master-windows.7z (94.44 MB)」
 じゃない方ですわ。Suisho5版だと変換できない。

で、↓あたりを見ながら黒いメモ帳(←コマンドプロンプトのこと)で
定跡ファイルを作ります。

定跡の作成 · yaneurao/YaneuraOu Wiki
https://github.com/yaneurao/YaneuraOu/wiki/%E5%AE%9A%E8%B7%A1%E3%81%AE%E4%BD%9C%E6%88%90

これも深い意味はないですが「YaneuraOu761_NNUE_KP256」フォルダの
「YaneuraOu_NNUE_KP256-evallearn-g++-avx2.exe」
を使い「20手目まで」で作成しました。 ←魚沼産やねうら王が使えるやつ。

5. 定跡を視覚化したり

Bookconvについて|くらっきぃ
https://note.com/spiritual_sh/n/n558bef75f31e
↑くらっきぃさんを見習って視覚化してみます。
←ちなみに、昨年の世界コンピュータ将棋選手権では
 十六式いろは煌(きらめき)は、くらっきぃさんの
 定跡ファイルを使わせてもらいました。

ai5/BookConv
https://github.com/ai5/BookConv
↑ShogiGUIの作者お手製の変換ツール
←違ってたらごめんなさい。

↑ほう?初手が幅広いですネ、ふふふ。

(追記)

←やねうら王の駒割りのみ評価関数で自己対戦したところ、
AobaZeroの棋譜の定跡がある方が勝ち越しましたわ!

将棋ソフト「十六式いろは煌(きらめき)2022冬セット」

★2023-03-25更新 GPU版、ポータブル版も公開しました。

将棋ソフト「十六式いろは煌(きらめき)2022冬セット」

Release 将棋ソフト「十六式いろは煌(きらめき)2022冬セット」 · sueyoshiryosuke/16shiki-Iroha_kirameki · GitHub

十六式いろは煌(きらめき)2022冬セットの画像

将棋AIエンジン「十六式いろは煌(きらめき)2022冬」と
その専用の将棋ソフト用GUI「いろは煌盤(きらめきばん)」を
セットにしたものを公開します。

Pythonで将棋ソフト制作-評価値編の補足

youtu.be

動画内での正規表現

上記の動画で作成したソースコードです。正規表現を使います。
(この記事の後半に、処理速度を速くした版もあります)

main.py

import cshogi
import random
# 文字列の検索のために正規表現を使う。
import re


def koma_count(board):
    """
    局面の先手、後手の各駒の数を数えてdictで返す。(王を除く)

    Args:
        board (cshogi.Board): cshogiでの局面

    Returns:
        koma_cnt (dict): 各駒とその数
        例:
        {'先手の歩': 7, '先手の香': 2, '先手の桂': 1, '先手の銀': 3,
         '先手の角': 0, '先手の飛': 1, '先手の金': 1, '先手のと': 1,
         '先手の杏': 0, '先手の圭': 0, '先手の全': 0, '先手の馬': 0,
         '先手の竜': 0, '先手持ち駒の歩': 0, '先手持ち駒の香': 0,
         '先手持ち駒の桂': 0, '先手持ち駒の銀': 0, '先手持ち駒の角': 0,
         '先手持ち駒の飛': 1, '先手持ち駒の金': 1, '後手の歩': 5,
         '後手の香': 2, '後手の桂': 2, '後手の銀': 0, '後手の角': 2,
         '後手の飛': 0, '後手の金': 1, '後手のと': 0, '後手の杏': 0,
         '後手の圭': 0, '後手の全': 0, '後手の馬': 0, '後手の竜': 0,
         '後手持ち駒の歩': 5, '後手持ち駒の香': 0, '後手持ち駒の桂': 1,
         '後手持ち駒の銀': 1, '後手持ち駒の角': 0, '後手持ち駒の飛': 0,
         '後手持ち駒の金': 1} 

    """

    board_str = str(board)  # 局面の文字列化
    # cshogiのソースコードposition.cppに駒の種類が書いている。
    koma_cnt = {"先手の歩": 0}  # 初期化
    # 先手
    koma_cnt["先手の歩"] = board_str.count("+FU")
    koma_cnt["先手の香"] = board_str.count("+KY")
    koma_cnt["先手の桂"] = board_str.count("+KE")
    koma_cnt["先手の銀"] = board_str.count("+GI")
    koma_cnt["先手の角"] = board_str.count("+KA")
    koma_cnt["先手の飛"] = board_str.count("+HI")
    koma_cnt["先手の金"] = board_str.count("+KI")
    koma_cnt["先手のと"] = board_str.count("+TO")
    koma_cnt["先手の杏"] = board_str.count("+NY")
    koma_cnt["先手の圭"] = board_str.count("+NK")
    koma_cnt["先手の全"] = board_str.count("+NG")
    koma_cnt["先手の馬"] = board_str.count("+UM")
    koma_cnt["先手の竜"] = board_str.count("+RY")

    # print(koma_cnt["先手の歩"])  # 盤上の先手の歩の数を表示する。

    # 先手の持ち駒
    line_str = str(re.search(r"P\+00FU.*\n", board_str))
    koma_cnt["先手持ち駒の歩"] = line_str.count("FU")
    line_str = str(re.search(r"P\+00KY.*\n", board_str))
    koma_cnt["先手持ち駒の香"] = line_str.count("KY")
    line_str = str(re.search(r"P\+00KE.*\n", board_str))
    koma_cnt["先手持ち駒の桂"] = line_str.count("KE")
    line_str = str(re.search(r"P\+00GI.*\n", board_str))
    koma_cnt["先手持ち駒の銀"] = line_str.count("GI")
    line_str = str(re.search(r"P\+00KA.*\n", board_str))
    koma_cnt["先手持ち駒の角"] = line_str.count("KA")
    line_str = str(re.search(r"P\+00HI.*\n", board_str))
    koma_cnt["先手持ち駒の飛"] = line_str.count("HI")
    line_str = str(re.search(r"P\+00KI.*\n", board_str))
    koma_cnt["先手持ち駒の金"] = line_str.count("KI")

    # 後手
    koma_cnt["後手の歩"] = board_str.count("-FU")
    koma_cnt["後手の香"] = board_str.count("-KY")
    koma_cnt["後手の桂"] = board_str.count("-KE")
    koma_cnt["後手の銀"] = board_str.count("-GI")
    koma_cnt["後手の角"] = board_str.count("-KA")
    koma_cnt["後手の飛"] = board_str.count("-HI")
    koma_cnt["後手の金"] = board_str.count("-KI")
    koma_cnt["後手のと"] = board_str.count("-TO")
    koma_cnt["後手の杏"] = board_str.count("-NY")
    koma_cnt["後手の圭"] = board_str.count("-NK")
    koma_cnt["後手の全"] = board_str.count("-NG")
    koma_cnt["後手の馬"] = board_str.count("-UM")
    koma_cnt["後手の竜"] = board_str.count("-RY")

    # 後手の持ち駒
    line_str = str(re.search(r"P-00FU.*\n", board_str))
    koma_cnt["後手持ち駒の歩"] = line_str.count("FU")
    line_str = str(re.search(r"P-00KY.*\n", board_str))
    koma_cnt["後手持ち駒の香"] = line_str.count("KY")
    line_str = str(re.search(r"P-00KE.*\n", board_str))
    koma_cnt["後手持ち駒の桂"] = line_str.count("KE")
    line_str = str(re.search(r"P-00GI.*\n", board_str))
    koma_cnt["後手持ち駒の銀"] = line_str.count("GI")
    line_str = str(re.search(r"P-00KA.*\n", board_str))
    koma_cnt["後手持ち駒の角"] = line_str.count("KA")
    line_str = str(re.search(r"P-00HI.*\n", board_str))
    koma_cnt["後手持ち駒の飛"] = line_str.count("HI")
    line_str = str(re.search(r"P-00KI.*\n", board_str))
    koma_cnt["後手持ち駒の金"] = line_str.count("KI")

    return koma_cnt


def komadoku_evaluate(koma_cnt):
    """
    その局面の駒得による評価値を計算する。

    Args:
        koma_cnt(dict): 各駒とその数

    Returns:
        phase_value (int): その局面の駒得による評価値

    """

    koma_value = {"先手の歩":   75,
                  "先手の香":  175,
                  "先手の桂":  275,
                  "先手の銀":  475,
                  "先手の角":  775,
                  "先手の飛":  875,
                  "先手の金":  575,
                  "先手のと":  575,
                  "先手の杏":  575,
                  "先手の圭":  575,
                  "先手の全":  575,
                  "先手の馬": 1175,
                  "先手の竜": 1275,
                  "先手持ち駒の歩": 125,
                  "先手持ち駒の香": 225,
                  "先手持ち駒の桂": 325,
                  "先手持ち駒の銀": 525,
                  "先手持ち駒の角": 825,
                  "先手持ち駒の飛": 925,
                  "先手持ち駒の金": 625,
                  "後手の歩": -75,
                  "後手の香": -175,
                  "後手の桂": -275,
                  "後手の銀": -475,
                  "後手の角": -775,
                  "後手の飛": -875,
                  "後手の金": -575,
                  "後手のと": -575,
                  "後手の杏": -575,
                  "後手の圭": -575,
                  "後手の全": -575,
                  "後手の馬": -1175,
                  "後手の竜": -1275,
                  "後手持ち駒の歩": -125,
                  "後手持ち駒の香": -225,
                  "後手持ち駒の桂": -325,
                  "後手持ち駒の銀": -525,
                  "後手持ち駒の角": -825,
                  "後手持ち駒の飛": -925,
                  "後手持ち駒の金": -625}

    phase_value = 0  # 出力する評価値を初期化
    for key in koma_cnt.keys():
        #print(key, value)

        # 駒の種類 * その駒の数
        phase_value += koma_value[key] * koma_cnt[key]
        #print(phase_value, key, koma_cnt[key])

    return phase_value


def order_list(move_lst):
    """
    cshogiから返ってきた合法手のリストをオーダリングする。

    今のところ、とりあえずランダム。
    これで、アルファベータカットしても
    事前にシャッフルしているので、ランダムで指す。

    Args:
        move_lst(int list): 合法手のリスト

    Returns:
        int list: オーダリング(並べ替えた)した合法手のリスト

    """

    random.shuffle(move_lst)

    return move_lst


while True:
    input_cmd = input()

    # USIプロトコル対応の将棋GUI(将棋所)に
    # エンジン登録するときに通信する処理。
    #
    # 普通にprintを使うと、改行はしてくれるので
    # 将棋エンジンを作る場合、問題なし。
    # バッファリングさせない(フラッシュさせる)ために
    # printの引数に「flush=True」を使う。
    if input_cmd == "usi":
        # ソフト名
        print("id name 16-168ui", flush=True)
        # 開発者名
        print("id author R.Sueyoshi", flush=True)
        print("usiok", flush=True)

    # 対局準備
    if input_cmd == "isready":
        # 設定の読み込みが必要ならここで処理する。
        print("readyok", flush=True)

    # 対局開始の合図
    if input_cmd == "usinewgame":
        pass

    # 局面の受け取り
    # 平手はstartpos
    # 例)
    # position startpos moves 7g7f 3c3d 2g2f
    # 最初の8文字を使う。
    if input_cmd[0:8] == "position" or input_cmd[0:1] == "a" or input_cmd[0:1] == "w":
        cmd_lst = list(input_cmd.split(" "))  # スペース区切りのリスト。
        if len(cmd_lst) == 2 or len(cmd_lst) % 2 == 1:
            my_turn = "先手"
        else:
            my_turn = "後手"

        # 局面にセットする。
        # cmd_lst[-1:][0]は最後の文字列(相手の手等)
        if cmd_lst[0] == "a":  # 裏コマンド、平手をセットする。
            board = cshogi.Board()

        elif cmd_lst[0] == "w":  # 裏コマンドその2。指し手生成祭(後手)
            # 「指し手生成祭」の局面
            b_phase = "l6nl/5+P1gk/2np1S3/p1p4Pp/3P2Sp1/1PPb2P1P/P5GS1/R8/LN4bKL w GR5pnsg 1"
            b_cmd_lst = list(b_phase.split(" "))  # スペース区切りのリスト。
            board = cshogi.Board(b_phase)  # 指定局面のセット
            # 手番の確認
            if b_cmd_lst[1] == "b":
                my_turn = "先手"
            else:
                my_turn = "後手"

        elif cmd_lst[1] == "startpos":  # 平手
            board = cshogi.Board()
            # 送られてきた局面まで局面をセットしなおす。
            if len(cmd_lst) > 2:
                for i in range(3, len(cmd_lst)):
                    board.push_usi(cmd_lst[i])  # 指す
            # print(board)

        elif cmd_lst[1] == "sfen":  # 指定局面
            sfen_str = (cmd_lst[2] + " " + cmd_lst[3] +
                        " " + cmd_lst[4] + " " + cmd_lst[5])
            if sfen_str[1] == "b":
                my_turn = "先手"
            else:
                my_turn = "後手"
            board = cshogi.Board(sfen_str)
            # 送られてきた局面まで局面をセットしなおす。
            if len(cmd_lst) > 6:
                for i in range(7, len(cmd_lst)):
                    board.push_usi(cmd_lst[i])  # 指す
            # print(board)

        else:
            # 想定外の時はエンジンを終了する。
            break

    # 思考開始の合図
    # 最初の3文字を使う。
    if input_cmd[0:2] == "go":
        # 自分の次の手を考えさせる。
        # board.legal_movesを使うと
        # 合法手がリストで戻り値として返ってくる。
        move_lst = list(board.legal_moves)  # 合法手をリスト化する
        move_lst = order_list(move_lst)  # 合法手をオーダリングする

        # 王手をかけられたとき
        # if board.is_check() == True:
        #   print("bestmove resign", flush=True)
        # 詰みのとき(負け)
        if len(move_lst) == 0:
            print("bestmove resign", flush=True)
        else:
            # 次の一手を将棋所等の将棋GUIに伝える。
            # (廃止→)返事の最初に並んでいる手を指す。
            koma_cnt_dict = koma_count(board)  # 先手、後手の駒の数を数える。
            #print(type(koma_cnt_dict), koma_cnt_dict)
            evaluation_value = komadoku_evaluate(koma_cnt_dict)  # 駒得の評価値を計算する。

            # 後手の時は評価値を反転させる必要がある
            if my_turn == "後手":
                evaluation_value = -evaluation_value

            print(
                f"info pv {cshogi.move_to_usi(move_lst[0])} score cp {evaluation_value}", flush=True)

            print("bestmove", cshogi.move_to_usi(move_lst[0]), flush=True)
            # 将棋エンジン内の局面を変更する。
            move = board.push(move_lst[0])  # 指す
            # print(board)

    # 1ゲームが終わった場合
    if input_cmd[:8] == "gameover":
        # コマンド受付を終了する。
        break

    # エンジン停止の合図
    if input_cmd == "quit":
        # コマンド受付を終了する。
        break

# エンジンを終了させる。
quit()

cshogiにある駒の数を取り出す機能版

以下は、上記のソースコードに対して、48さんからの助言で
正規表現ではなくcshogiにある駒の数を取り出す機能を
使ったものに変えたものです。
★koma_count関数のところだけ変更しました。

たぶん、こっちの方が処理速度が速いと思うので
今後はこちらを使う予定です。

main.py

import cshogi
import random


def koma_count(board):
    """
    局面の先手、後手の各駒の数を数えてdictで返す。(王を除く)

    Args:
        board (cshogi.Board): cshogiでの局面

    Returns:
        koma_cnt (dict): 各駒とその数
        例:
        {'先手の歩': 7, '先手の香': 2, '先手の桂': 1, '先手の銀': 3,
         '先手の角': 0, '先手の飛': 1, '先手の金': 1, '先手のと': 1,
         '先手の杏': 0, '先手の圭': 0, '先手の全': 0, '先手の馬': 0,
         '先手の竜': 0, '先手持ち駒の歩': 0, '先手持ち駒の香': 0,
         '先手持ち駒の桂': 0, '先手持ち駒の銀': 0, '先手持ち駒の角': 0,
         '先手持ち駒の飛': 1, '先手持ち駒の金': 1, '後手の歩': 5,
         '後手の香': 2, '後手の桂': 2, '後手の銀': 0, '後手の角': 2,
         '後手の飛': 0, '後手の金': 1, '後手のと': 0, '後手の杏': 0,
         '後手の圭': 0, '後手の全': 0, '後手の馬': 0, '後手の竜': 0,
         '後手持ち駒の歩': 5, '後手持ち駒の香': 0, '後手持ち駒の桂': 1,
         '後手持ち駒の銀': 1, '後手持ち駒の角': 0, '後手持ち駒の飛': 0,
         '後手持ち駒の金': 1} 

    """

    # position.cppより
    koma_cnt = {"先手の歩": 0}  # 初期化
    # 先手
    koma_cnt["先手の歩"] = board.pieces.count(1)
    koma_cnt["先手の香"] = board.pieces.count(2)
    koma_cnt["先手の桂"] = board.pieces.count(3)
    koma_cnt["先手の銀"] = board.pieces.count(4)
    koma_cnt["先手の角"] = board.pieces.count(5)
    koma_cnt["先手の飛"] = board.pieces.count(6)
    koma_cnt["先手の金"] = board.pieces.count(7)
    koma_cnt["先手のと"] = board.pieces.count(9)
    koma_cnt["先手の杏"] = board.pieces.count(10)
    koma_cnt["先手の圭"] = board.pieces.count(11)
    koma_cnt["先手の全"] = board.pieces.count(12)
    koma_cnt["先手の馬"] = board.pieces.count(13)
    koma_cnt["先手の竜"] = board.pieces.count(14)

    # 先手の持ち駒
    koma_cnt["先手持ち駒の歩"] = board.pieces_in_hand[0][0]
    koma_cnt["先手持ち駒の香"] = board.pieces_in_hand[0][1]
    koma_cnt["先手持ち駒の桂"] = board.pieces_in_hand[0][2]
    koma_cnt["先手持ち駒の銀"] = board.pieces_in_hand[0][3]
    koma_cnt["先手持ち駒の角"] = board.pieces_in_hand[0][5]
    koma_cnt["先手持ち駒の飛"] = board.pieces_in_hand[0][6]
    koma_cnt["先手持ち駒の金"] = board.pieces_in_hand[0][4]

    # 後手
    koma_cnt["後手の歩"] = board.pieces.count(17)
    koma_cnt["後手の香"] = board.pieces.count(18)
    koma_cnt["後手の桂"] = board.pieces.count(19)
    koma_cnt["後手の銀"] = board.pieces.count(20)
    koma_cnt["後手の角"] = board.pieces.count(21)
    koma_cnt["後手の飛"] = board.pieces.count(22)
    koma_cnt["後手の金"] = board.pieces.count(23)
    koma_cnt["後手のと"] = board.pieces.count(25)
    koma_cnt["後手の杏"] = board.pieces.count(26)
    koma_cnt["後手の圭"] = board.pieces.count(27)
    koma_cnt["後手の全"] = board.pieces.count(28)
    koma_cnt["後手の馬"] = board.pieces.count(29)
    koma_cnt["後手の竜"] = board.pieces.count(30)

    # 後手の持ち駒
    koma_cnt["後手持ち駒の歩"] = board.pieces_in_hand[1][0]
    koma_cnt["後手持ち駒の香"] = board.pieces_in_hand[1][1]
    koma_cnt["後手持ち駒の桂"] = board.pieces_in_hand[1][2]
    koma_cnt["後手持ち駒の銀"] = board.pieces_in_hand[1][3]
    koma_cnt["後手持ち駒の角"] = board.pieces_in_hand[1][5]
    koma_cnt["後手持ち駒の飛"] = board.pieces_in_hand[1][6]
    koma_cnt["後手持ち駒の金"] = board.pieces_in_hand[1][4]

    return koma_cnt


def komadoku_evaluate(koma_cnt):
    """
    その局面の駒得による評価値を計算する。

    Args:
        koma_cnt(dict): 各駒とその数

    Returns:
        phase_value (int): その局面の駒得による評価値

    """

    koma_value = {"先手の歩":   75,
                  "先手の香":  175,
                  "先手の桂":  275,
                  "先手の銀":  475,
                  "先手の角":  775,
                  "先手の飛":  875,
                  "先手の金":  575,
                  "先手のと":  575,
                  "先手の杏":  575,
                  "先手の圭":  575,
                  "先手の全":  575,
                  "先手の馬": 1175,
                  "先手の竜": 1275,
                  "先手持ち駒の歩": 125,
                  "先手持ち駒の香": 225,
                  "先手持ち駒の桂": 325,
                  "先手持ち駒の銀": 525,
                  "先手持ち駒の角": 825,
                  "先手持ち駒の飛": 925,
                  "先手持ち駒の金": 625,
                  "後手の歩": -75,
                  "後手の香": -175,
                  "後手の桂": -275,
                  "後手の銀": -475,
                  "後手の角": -775,
                  "後手の飛": -875,
                  "後手の金": -575,
                  "後手のと": -575,
                  "後手の杏": -575,
                  "後手の圭": -575,
                  "後手の全": -575,
                  "後手の馬": -1175,
                  "後手の竜": -1275,
                  "後手持ち駒の歩": -125,
                  "後手持ち駒の香": -225,
                  "後手持ち駒の桂": -325,
                  "後手持ち駒の銀": -525,
                  "後手持ち駒の角": -825,
                  "後手持ち駒の飛": -925,
                  "後手持ち駒の金": -625}

    phase_value = 0  # 出力する評価値を初期化
    for key in koma_cnt.keys():
        #print(key, value)

        # 駒の種類 * その駒の数
        phase_value += koma_value[key] * koma_cnt[key]
        #print(phase_value, key, koma_cnt[key])

    return phase_value


def order_list(move_lst):
    """
    cshogiから返ってきた合法手のリストをオーダリングする。

    今のところ、とりあえずランダム。
    これで、アルファベータカットしても
    事前にシャッフルしているので、ランダムで指す。

    Args:
        move_lst(int list): 合法手のリスト

    Returns:
        int list: オーダリング(並べ替えた)した合法手のリスト

    """

    random.shuffle(move_lst)

    return move_lst


while True:
    input_cmd = input()

    # USIプロトコル対応の将棋GUI(将棋所)に
    # エンジン登録するときに通信する処理。
    #
    # 普通にprintを使うと、改行はしてくれるので
    # 将棋エンジンを作る場合、問題なし。
    # バッファリングさせない(フラッシュさせる)ために
    # printの引数に「flush=True」を使う。
    if input_cmd == "usi":
        # ソフト名
        print("id name 16-168ui", flush=True)
        # 開発者名
        print("id author R.Sueyoshi", flush=True)
        print("usiok", flush=True)

    # 対局準備
    if input_cmd == "isready":
        # 設定の読み込みが必要ならここで処理する。
        print("readyok", flush=True)

    # 対局開始の合図
    if input_cmd == "usinewgame":
        pass

    # 局面の受け取り
    # 平手はstartpos
    # 例)
    # position startpos moves 7g7f 3c3d 2g2f
    # 最初の8文字を使う。
    if input_cmd[0:8] == "position" or input_cmd[0:1] == "a" or input_cmd[0:1] == "w":
        cmd_lst = list(input_cmd.split(" "))  # スペース区切りのリスト。
        if len(cmd_lst) == 2 or len(cmd_lst) % 2 == 1:
            my_turn = "先手"
        else:
            my_turn = "後手"

        # 局面にセットする。
        # cmd_lst[-1:][0]は最後の文字列(相手の手等)
        if cmd_lst[0] == "a":  # 裏コマンド、平手をセットする。
            board = cshogi.Board()

        elif cmd_lst[0] == "w":  # 裏コマンドその2。指し手生成祭(後手)
            # 「指し手生成祭」の局面
            b_phase = "l6nl/5+P1gk/2np1S3/p1p4Pp/3P2Sp1/1PPb2P1P/P5GS1/R8/LN4bKL w GR5pnsg 1"
            b_cmd_lst = list(b_phase.split(" "))  # スペース区切りのリスト。
            board = cshogi.Board(b_phase)  # 指定局面のセット
            # 手番の確認
            if b_cmd_lst[1] == "b":
                my_turn = "先手"
            else:
                my_turn = "後手"

        elif cmd_lst[1] == "startpos":  # 平手
            board = cshogi.Board()
            # 送られてきた局面まで局面をセットしなおす。
            if len(cmd_lst) > 2:
                for i in range(3, len(cmd_lst)):
                    board.push_usi(cmd_lst[i])  # 指す
            # print(board)

        elif cmd_lst[1] == "sfen":  # 指定局面
            sfen_str = (cmd_lst[2] + " " + cmd_lst[3] +
                        " " + cmd_lst[4] + " " + cmd_lst[5])
            if sfen_str[1] == "b":
                my_turn = "先手"
            else:
                my_turn = "後手"
            board = cshogi.Board(sfen_str)
            # 送られてきた局面まで局面をセットしなおす。
            if len(cmd_lst) > 6:
                for i in range(7, len(cmd_lst)):
                    board.push_usi(cmd_lst[i])  # 指す
            # print(board)

        else:
            # 想定外の時はエンジンを終了する。
            break

    # 思考開始の合図
    # 最初の3文字を使う。
    if input_cmd[0:2] == "go":
        # 自分の次の手を考えさせる。
        # board.legal_movesを使うと
        # 合法手がリストで戻り値として返ってくる。
        move_lst = list(board.legal_moves)  # 合法手をリスト化する
        move_lst = order_list(move_lst)  # 合法手をオーダリングする

        # 王手をかけられたとき
        # if board.is_check() == True:
        #   print("bestmove resign", flush=True)
        # 詰みのとき(負け)
        if len(move_lst) == 0:
            print("bestmove resign", flush=True)
        else:
            # 次の一手を将棋所等の将棋GUIに伝える。
            # (廃止→)返事の最初に並んでいる手を指す。
            koma_cnt_dict = koma_count(board)  # 先手、後手の駒の数を数える。
            #print(type(koma_cnt_dict), koma_cnt_dict)
            evaluation_value = komadoku_evaluate(koma_cnt_dict)  # 駒得の評価値を計算する。

            # 後手の時は評価値を反転させる必要がある
            if my_turn == "後手":
                evaluation_value = -evaluation_value

            print(
                f"info pv {cshogi.move_to_usi(move_lst[0])} score cp {evaluation_value}", flush=True)

            print("bestmove", cshogi.move_to_usi(move_lst[0]), flush=True)
            # 将棋エンジン内の局面を変更する。
            move = board.push(move_lst[0])  # 指す
            # print(board)

    # 1ゲームが終わった場合
    if input_cmd[:8] == "gameover":
        # コマンド受付を終了する。
        break

    # エンジン停止の合図
    if input_cmd == "quit":
        # コマンド受付を終了する。
        break

# エンジンを終了させる。
quit()

(連続企画)Pythonで将棋ソフト制作その5の補足

youtu.be

↓↓↓ 「十六式いろは初(うい)」 ↓↓↓
ダウンロード「16-168ui.exe」(34MB)

drive.google.com

↑ 十六式いろは初(うい)のインストール方法
 「16-168ui.exe」を実行して、ファイル群を展開してください。
 展開されたフォルダの中にある「readme.txt」をお読みください。  ちなみに将棋所などで登録するエンジンのファイルは
 「16-168ui_entry.bat」です。

youtube.com
将棋ソフト作成の連続企画の一つです。
上記の動画で作成したソースコードです。

main.py

import cshogi
import random

while True:
    input_cmd = input()

    # USIプロトコル対応の将棋GUI(将棋所)に
    # エンジン登録するときに通信する処理。
    #
    # 普通にprintを使うと、改行はしてくれるので
    # 将棋エンジンを作る場合、問題なし。
    # バッファリングさせない(フラッシュさせる)ために
    # printの引数に「flush=True」を使う。
    if input_cmd == "usi":
        # ソフト名
        print("id name 16-168ui", flush=True)
        # 開発者名
        print("id author R.Sueyoshi", flush=True)
        print("usiok", flush=True)

    # 対局準備
    if input_cmd == "isready":
        # 設定の読み込みが必要ならここで処理する。
        print("readyok", flush=True)

    # 対局開始の合図
    if input_cmd == "usinewgame":
        pass

    # 局面の受け取り
    # 平手はstartpos
    # 例)
    # position startpos moves 7g7f 3c3d 2g2f
    # 最初の8文字を使う。
    if input_cmd[0:8] == "position":
        cmd_lst = list(input_cmd.split(" "))  # スペース区切りのリスト。

        # 局面にセットする。
        # cmd_lst[-1:][0]は最後の文字列(相手の手等)
        if cmd_lst[1] == "startpos":  # 平手
            board = cshogi.Board()
            # 送られてきた局面まで局面をセットしなおす。
            if len(cmd_lst) > 2:
                for i in range(3, len(cmd_lst)):
                    board.push_usi(cmd_lst[i])  # 指す
            # print(board)

        elif cmd_lst[1] == "sfen":  # 指定局面
            sfen_str = (cmd_lst[2] + " " + cmd_lst[3] +
                        " " + cmd_lst[4] + " " + cmd_lst[5])
            board = cshogi.Board(sfen_str)
            # 送られてきた局面まで局面をセットしなおす。
            if len(cmd_lst) > 6:
                for i in range(7, len(cmd_lst)):
                    board.push_usi(cmd_lst[i])  # 指す
            # print(board)

        else:
            # 想定外の時はエンジンを終了する。
            break

    # 思考開始の合図
    # 最初の3文字を使う。
    if input_cmd[0:2] == "go":
        # 自分の次の手を考えさせる。
        # board.legal_movesを使うと
        # 合法手がリストで戻り値として返ってくる。
        move_lst = list(board.legal_moves)  # 合法手をリスト化する

        # 王手をかけられたとき
        # if board.is_check() == True:
        #   print("bestmove resign", flush=True)
        # 詰みのとき(負け)
        if len(move_lst) == 0:
            print("bestmove resign", flush=True)
        else:
            # 次の一手を将棋所等の将棋GUIに伝える。
            # (廃止→)返事の最初に並んでいる手を指す。
            # ランダムな手を指す。
            move_num = random.randint(0, len(move_lst) - 1)
            print("bestmove", cshogi.move_to_usi(
                move_lst[move_num]), flush=True)
            # 将棋エンジン内の局面を変更する。
            move = board.push(move_lst[move_num])  # 指す
            # print(board)

    # 1ゲームが終わった場合
    if input_cmd[:8] == "gameover":
        # コマンド受付を終了する。
        break

    # エンジン停止の合図
    if input_cmd == "quit":
        # コマンド受付を終了する。
        break

# エンジンを終了させる。
quit()