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

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

(連続企画)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()

pythonの将棋ソフト開発ライブラリ:cshogiとpython-shogi

pythonの将棋ソフト開発ライブラリの速度比較

プログラミング言語Pythonを使って、将棋ソフトを開発するときに
便利な上記の2つの将棋ライブラリ(cshogiとpython-shogi)について
処理速度の比較をしてみました( ´∀` )
(というか自分の覚書用w)

元々python-shogiが作られ、
その後、 C++により処理速度を向上させたcshogiが作られました。

環境

方法

平手(ハンデなしの初期配置)で、
合法手(反則にならない次の手)を将棋ライブラリから
呼び出し、その中から一手を選び
(何も考えない手。最善の一手にあらず)
局面を進めます。
途中で次の一手が見つからなくなれば、
初期配置に戻します。

ということを、指定回数繰り返した時の
処理時間を比較してみようかと思いまして。

※選ばれる手が異なるでしょうから、公平に
一手指して戻して一手指すということを
繰り返してみたりもしましたが、
結果はほぼ変わりませんでした(;´Д`)w

ソースコード

import shogi
import time

board = shogi.Board()

# 何回繰り返すか。10**2は、10の2乗という意味。
cnt = 10**2

# 計測開始
start_time = time.time()

for i in range(cnt):
    board.legal_moves  # 合法手を生成する
    move_lst = list(board.legal_moves)  # 合法手をリスト化する
    if len(move_lst) == 0:  # もし合法手がない場合は、平手に戻す。
        board = shogi.Board()
        continue
    best_move = str(move_lst[0])  # 次の一手を選ぶ。
    board.push_usi(best_move) # 指す

print(cnt, time.time() - start_time)
  • cshogi用
import cshogi
import time

board = cshogi.Board()

# 何回繰り返すか。10**6は、10の6乗という意味。
cnt = 10**6

# 計測開始
start_time = time.time()

for i in range(cnt):
    board.legal_moves # 合法手を生成する
    move_lst = list(board.legal_moves) # 合法手をリスト化する
    if len(move_lst) == 0:  # もし合法手がない場合は、平手に戻す。
        board = cshogi.Board()
        continue
    best_move = cshogi.move_to_usi(move_lst[0])  # 次の一手を選ぶ。
    board.push_usi(best_move) # 指す

print(cnt, time.time() - start_time)

Google Colaboratory で実行できます。
処理時間は以下の結果と異なりますが(;´Д`)w
colab.research.google.com

結果

というか、繰り返す回数の違いから結果は
わかるようなものですが、とりあえずw

環境 繰り返し回数 処理時間(秒)
python-shogi 100 2.72
cshogi 10の6乗(100万) 3.16
python-shogi(pypy) 100 2.41

8600倍!?えっ!?Σ(・□・;)

ちなみに、pypyというのは、ある工夫をしてpython
処理速度を爆速にしようぜ、的なものです( ´∀` )
ただ、cshogiとは一緒に使えません( ノД`)しくしく

←cshogiが使うnumpyという行列計算等を扱うライブラリと
非常に相性が悪いので。

結論とまとめ

将棋ソフトの棋力を求めるならcshogiを、
処理速度は気にしないが、勉強のために
ライブラリのソースコードも見たいならpython-shogiを、
という感じですネ( ´∀` )

おまけ

「board.legal_moves # 合法手を生成する」だけの繰り返しなら
pypy&python-shogiが爆速だった、なぜだ!?w
←cshogi呼び出すのに時間かかる等かもですネぇ。

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

youtu.be

上記の動画で作成したソースコードです。

main.py

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":
        position = input_cmd
        print("position: ", position, flush=True)

    # 思考開始の合図
    # 最初の3文字を使う。
    if input_cmd[0:2] == "go":
        # とりあえず先手の角道を開ける一手を指す。
        print("bestmove 7g7f", flush=True)

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

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

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

youtu.be

上記の動画で作成したソースコードです。

main.py

input_cmd = input()

if input_cmd == "usi":
    # ソフト名
    print("id name 16-168ui")
    # 開発者名
    print("id author R.Sueyoshi")
    print("usiok")

if input_cmd == "quit":
    quit()

.
16-168ui_entry.bat

@echo off
cd %~dp0
cd ..\scripts
python %~dp0\main.py