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