2021/10/24(日)長い移動日

2021/10/24 21:46

ライオンズの最終戦はいつなんだっけ? と思って確認したら、火曜日なんですね。今年はいろいろと文句ばかり書いてきましたが、あさっては有終の美を飾ってもらいたいと思います。まぁ、いずれにしろ5位か6位なんですがw

一方、優勝争いのほうは佳境に入ってきており、うらやましいなーという思いでのんびりと試合を眺めています。来年はこの争いの場にライオンズもいてほしいところではありますが、1年で立て直せるかというとまた難しいところで……。

野球殿堂博物館「松坂大輔投手 引退記念展示」のお知らせ|埼玉西武ライオンズ

さすがにそろそろ元気に外出しても良さそうな感じにはなってきたので、近々行ってみたいと思います。

2021/10/23(土)雀魂の牌譜をNAGAに解析させる

2021/10/23 19:12 ゲーム::雀魂技術::Python
(2021-12-12追記) 本エントリの内容は現在でも有効なものですが、より簡単に実践できる新しい仕組みを作りましたので、今後はその手順で解析することをお勧めします。詳しくは 雀魂の牌譜をNAGAに解析させる-完全版- をご覧ください。

0. はじめに

10月20日のアップデートにより、麻雀AIの NAGA にオリジナルの牌譜を解析させる機能が追加されました。これまでは 天鳳 の牌譜を対象としたサービスでしたが、これからはどんな牌譜でも解析させられるのです。

私もさっそく 雀魂 の牌譜を解析させようと思ったものの、かといって 天鳳牌譜エディタ にポチポチ入力するのは現実的でありません。というわけで、もっと簡単に解析対象のデータを作成する仕組みを考えてみました。

1. 牌譜データをダウンロードする

前提として、 NAGAで牌譜の解析をさせるには、天鳳牌譜エディタ形式のURLが必要です 。牌譜エディタのURLは https://tenhou.net/6/#json=牌譜データ という形式なので、この 牌譜データ の部分に雀魂の牌譜を突っ込むことが最終目標になります。

とはいえ、この牌譜データを手で作るのはあまりにも大変です。というより、それならば素直に牌譜エディタを使うべきです。では、ほかに何かいい方法はないでしょうか。

少し話は変わりますが、麻雀AIの Akochan に天鳳や雀魂の牌譜を解析させる Akochan Reviewer というシステムがあります。そして、Akochan Reviewerで雀魂の牌譜を解析させるには、事前に牌譜データをダウンロードしなければいけません。

そうです。 牌譜データをダウンロードしなければいけない のです。そして、ダウンロードするための仕組みはすでに用意されています。せっかくですから、ありがたく乗っからせてもらいましょう。以下の手順でダウンロードの準備をしてください。
このとき、 downloadlogs.jsNAMEPREF の値を 0 に設定しておきましょう。ダウンロードする牌譜データ内の役名が日本語になります。

さて、以上でダウンロードの準備が完了しました。雀魂にログインし、対象の牌譜画面でキーボードの S キーを押してください。これまでの手順に誤りがなければ、牌譜データのダウンロードが始まるはずです。

(謝辞) 本文中にもあるとおり、本節の手順はAkochan Reviewerの仕組みを流用させていただきました。Akochan Reviewerを開発されている Equimさん に感謝いたします。ありがとうございました。

2. 牌譜データをNAGAが解析できる形式に変換する

前節の手順で牌譜データをダウンロードできるようになりましたが、残念ながらこのままではNAGAに読み込ませることができません。2点ほど手を加える必要があります。

2-1. 局ごとのデータに分割する

先ほどダウンロードした牌譜データは、JSONの中に半荘すべてのデータが含まれています。
// ダウンロードした牌譜データ
{
    "title": [...],
    "name": [...],
    "rule": [...],
    "log": [
        [東1局の牌譜],
        [東2局の牌譜],
        ...
    ],
    ...
}
NAGAでは局単位の牌譜データを読み込むため、以下のように局数分のJSONに分割してあげる必要があります。
// 東1局
{
    "title": [...],
    "name": [...],
    "rule": [...],
    "log": [
        [東1局の牌譜]
    ]
}

// 東2局
{
    "title": [...],
    "name": [...],
    "rule": [...],
    "log": [
        [東2局の牌譜]
    ]
}

...
titlenamerulelog 以外の項目は削除しても問題ありません。

2-2. 役名を変換する

和了役に風牌が含まれる場合、そのままではNAGAに読み込ませることができません。雀魂では「場風牌」「自風牌」という表記であるのに対し、NAGA(というよりも天鳳)では「場風 東」「自風 南」のように牌の種類まで含まれているからです。役の名前は牌譜データの中に直接文字列で埋め込まれているので、天鳳の形式に書き換えてしまいましょう。

(2021-10-29追記) ダブルリーチについても、雀魂では「ダブル立直」、NAGAでは「両立直」と表記が異なります。こちらもNAGAの形式に書き換えましょう。

2-3. 退屈なことはPythonにやらせよう(2021-10-29更新)

以上の対応で https://tenhou.net/6/#json="title":[...],"name":[...],"rule":[...],"log":[[...]] のようなURLを組み立てられるようになりました。これでNAGAに牌譜を解析させることができますね。お疲れ様でした!

……で、終わりにしてしまうのも芸がないので、ダウンロードした牌譜データを編集するPythonスクリプトを作成しました。このスクリプトは牌譜データを標準入力から受け取り、牌譜エディタのURLを標準出力に出力します。
import json
import sys

def create_viewer_urls(soul_json):
    """
    雀魂の牌譜JSONを牌譜エディタURL群に変換する。
    
    Args:
        soul_json (str): 雀魂の牌譜JSONを指定する。
    Returns:
        list[str]: 牌譜エディタURL群を返す。
    """
    # 雀魂の牌譜JSONを辞書に変換する。
    soul_paifu = json.loads(soul_json)
    
    # title内の卓名を雀魂っぽく変換する。
    # 
    # 変換前のtitle:
    #     "title": [ "玉の間南喰赤", "2021/10/20 20:48:01" ]
    # 変換後のtitle:
    #     "title": [ "玉の間四人南", "2021/10/20 20:48:01" ]
    title = soul_paifu['title'].copy()
    title[0] = to_soul_table(title[0])
    
    # rule内の卓名を雀魂っぽく変換する。
    # 
    # 変換前のrule:
    #     "rule": { "disp": "玉の間南喰赤", "aka53": 1, "aka52": 1, "aka51": 1 }
    # 変換後のrule:
    #     "rule": { "disp": "玉の間四人南", "aka53": 1, "aka52": 1, "aka51": 1 }
    rule = soul_paifu['rule'].copy()
    rule['disp'] = to_soul_table(rule['disp'])
    
    # logを局ごとのデータに分割し、牌譜エディタURL群として返す。
    return ['https://tenhou.net/6/#json=' + json.dumps({
        'title': title,
        'name': soul_paifu['name'],
        'rule': rule,
        'log': [to_naga_log(log)],
    }, ensure_ascii=False, separators=(',', ':')) for log in soul_paifu['log']]

def to_soul_table(tenhou_table):
    """
    卓名を雀魂っぽく変換する。
    
    Args:
        tenhou_table (str): 天鳳っぽい卓名を指定する。
    Returns:
        str: 雀魂っぽい卓名を返す。
    """
    # 表記の好みの問題なので、必ずしも必要となる処理ではない。
    return tenhou_table.replace('南喰赤', '四人南')

def to_naga_log(soul_log):
    """
    局データをNAGAが解析可能な形式に変換する。
    
    Args:
        soul_log (list[list]): 雀魂形式のlogを指定する。
    Returns:
        list[list]: NAGAで解析可能な形式のlogを返す。
    """
    # 流局時のデータは変換の必要がない。
    if len(soul_log[16]) < 3:
        return soul_log
    naga_log = soul_log.copy()
    
    # 当該局の場風を設定する。
    # 
    # 局を表す数字と意味:
    #     0 => 東1局, 1 => 東2局, ...
    prevalent = ['東', '南', '西', '北'][naga_log[0][0] // 4]
    
    # 当該局の和了者の自風を設定する。
    # 
    # 算出方法:
    #     (和了者のプレイヤー番号 - 親の位置) % 4
    seat = ['東', '南', '西', '北'][
        (max(enumerate(naga_log[16][1]), key=lambda x: x[1])[0] - (naga_log[0][0] % 4)) % 4
    ]
    
    # 役名をNAGAが解析可能な表記に変換する。
    naga_log[16][2][4:] = [to_naga_hand(hand, prevalent, seat) for hand in naga_log[16][2][4:]]
    
    # 変換後のlogを返す。
    return naga_log

def to_naga_hand(hand, prevalent, seat):
    """
    役名をNAGAが解析可能な表記に変換する。
    
    Args:
        hand (str): 和了役を指定する。
        prevalent (str): 場風を指定する。
        seat (str): 和了者の自風を指定する。
    Returns:
        str: NAGAで解析可能な表記の役名を返す。
    """
    # 対応が必要な役が判明次第、随時追加する。
    if hand == '役牌:場風牌(1飜)':
        return f"場風 {prevalent}(1飜)"
    elif hand == '役牌:自風牌(1飜)':
        return f"自風 {seat}(1飜)"
    elif hand == 'ダブル立直(2飜)':
        return '両立直(2飜)'
    else:
        return hand

if __name__ == '__main__':
    for url in create_viewer_urls(''.join(sys.stdin.readlines())):
        print(url)

2-4. プログラミングはわからにゃいけどNAGAを使いたいにゃ!(2021-10-24追記)

Pythonを動かすための環境を用意できない場合は、Web上でプログラムを実行するサービスを利用しても良いでしょう。

まず、先ほどのスクリプトを少し変更します。最後の3行を以下のように書き換えた上で、 ※ここに牌譜データを貼り付けるにゃ! の部分に牌譜データを埋め込んでください。
if __name__ == '__main__':
    soul_json = """
    ※ここに牌譜データを貼り付けるにゃ!
    """
    for url in create_viewer_urls(soul_json):
        print(url)
続いて、 Paiza.io にアクセスします。入力欄(背景色が黒い領域)に変更後のスクリプトを貼り付け、実行ボタンを押してください。スクリプトに誤りがなければ、画面下部のコンソールに牌譜エディタのURLが表示されるはずです。

また、 雀魂の牌譜をNAGA解析する方法|アトリエ@凛凛、凛世|note ではGoogleスプレッドシートを使う仕組みが紹介されています。本エントリの内容はすっぱり忘れて、そちらの手順で進めるのもひとつの方法でしょう。

3. 遊びのはずなのに仕事のような障害報告をしている件(2022-01-21更新)

何度か解析を行う中で、NAGA側の不具合と思われる事象にも遭遇しました。以下の問題はすべてサポートに連絡しています。

自分で言うのもアレですが、連絡に際してはかなり質の高いレポートを送っているつもりです。 一応プロなので
  • アガリ時の点数申告画面で赤ドラと裏ドラの表記が逆になっている
    • 2021年10月20日に報告
    • 2021年10月22日に解消の報告を受領
  • カンが含まれる牌譜を読み込めない
    • 2021年10月22日に報告
    • 2021年10月25日に解消の報告を受領
  • 同一プレイヤーが2巡続けて同一の牌を切り、その両方を別々のプレイヤーが鳴いた局の牌譜を読み込めない
    • 2022年1月5日に報告
    • 2022年1月6日に原因の報告を受領
    • 2022年1月21日に解消の報告を受領
いずれも迅速にご対応いただけました。ありがとうございました。

3つ目の事象は 牌譜ビューア で見たほうがわかりやすいかもしれません。Dさんのリーチ宣言牌と次巡の捨て牌がいずれも七萬で、宣言牌はBさんがポン、次の牌はAさんがチーしています。このようなケースで読み込みに失敗するという事象でした。

4. おわりに

すべての牌譜をNAGAに読み込ませてしまうと、九種九牌や四風連打の局でも20ポイント消費することになるので、対象の局はしっかりと取捨選択しましょう。

2021/10/22(金)秋の訪れ

2021/10/22 18:22

こういうニュースが増えてくると秋を感じますね……と書こうと思ったのですが、秋とは思えないくらい寒い一日でした。

西武榎田大樹と小川龍也が戦力外「優勝した年の戦力として感謝」渡辺GM - プロ野球 : 日刊スポーツ

榎田は二軍暮らしが長くなるにつれて、ライオンズの二軍投手にありがちなボールを置きに行くフォームに変わっていました。二軍の投手コーチどもは何人壊せば気が済むのでしょうか。

小川は右を苦手にしないタイプだった(特に2019年は圧倒的に左に打たれていた)ので、1イニングを任せるような使い方をすればもう少し違った結果が残っていたと思うのですが、残念ながらライオンズの首脳陣は重度の左右病でした。

二人とも連覇に大きく貢献してくれた選手なだけに寂しいですが、年齢や年俸を加味すると構想外になるのは仕方がないところかと思います。今までありがとうございました。

西武が来季40歳の内海哲也と契約へ コーチ兼任も検討|【西日本スポーツ】

今年はメットライフドームでの初勝利を挙げるなど2試合に登板しましたが、戦力として計算するにはかなり厳しい状態ではあると思います。どちらかというとお手本であったり、検討しているというコーチとしての期待なのでしょう。契約を切った時点でジャイアンツに帰ってしまうことは分かりきっていますしw

渡辺を育てた育成手腕は確かですし、コーチという肩書きを与えることによって、個人的な相談のレベルではなく、大手を振って指導ができるようになりますから、もっと多くのピッチャーを育てることもできるはずです。本人の意思がわからないので現時点では大はしゃぎはしないでおきますが、ファンとしては兼任コーチの要請を是非とも受けてほしいと思います。

2021/10/21(木)いつもの(vs オリックス 第25回戦)

2021/10/21 22:49
【オリックス vs 埼玉西武 第25回戦】
(2021年10月21日/京セラドーム大阪)

埼玉西武   0 0 0  0 0 1  0 0 1  2
オリックス  0 0 0  2 1 0  0 0 X  3

[勝] 宮城  13勝4敗0S
[S] 平野佳 1勝3敗29S
[敗] 今井  8勝8敗0S

[本塁打]
  5回裏 宗      9号 ソロ (今井)
  6回表 ブランドン  3号 ソロ (宮城)
  9回表 山川    24号 ソロ (平野佳)

ライオンズ先発の今井は抜群にいいという感じではなかったにしろ、フォアボールを出しながらも踏ん張るいつものピッチングでした。しかし4回裏、2アウト二三塁から、紅林の大きなレフトフライを山野辺が落球。2点を先制されます。もちろん山野辺が外野が本職ではないのは重々承知していますが、捕球時に腰が砕けるような情けないエラーを見せられると今井が不憫でなりません。

打線は相変わらず宮城の前に沈黙し、ブランドンのホームランで1点を返すのが精一杯。9回に山川のホームランで1点差まで詰め寄りましたがあと一歩及びませんでした。

……何度同じ相手に同じようなやられ方をすれば気が済むんですかね。

2021/10/20(水)最下位脱出(vs 北海道日本ハム 第24回戦)

2021/10/20 22:25
【埼玉西武 vs 北海道日本ハム 第24回戦】
(2021年10月20日/メットライフドーム)

北海道日本ハム  0 1 0  1 3 0  0 0 0  5
埼玉西武     0 5 1  0 1 0  0 0 x  7

[勝] 松本   10勝8敗0S
[S] 田村   1勝0敗1S
[敗] アーリン 2勝3敗0S

[本塁打]
  2回表 近藤 10号 ソロ  (松本)
  3回裏 山川 23号 ソロ  (アーリン)
  5回表 野村  7号 3ラン (松本)

先発の松本が二桁の10勝に到達しました。とはいえ、内容的にはあまり褒められたものではなく、序盤に6点の援護をもらいながら5回5失点と、やっとの思いで勝利投手の権利を手にした感じでした。2回の近藤のホームランは仕方がないとして、先頭のフォアボールが失点に繋がった4回と、2アウトランナーなしから崩れた5回は反省していただきたいと思います。

打線は苦手のアーリンを序盤で攻略しました。1点ビハインドの2回裏、2アウト満塁から、山田の走者一掃のタイムリーツーベースで逆転。さらに栗山の2点タイムリーで追加点を挙げました。3回には山川のホームランでもう1点を追加。1点差に詰め寄られた5回には岸のタイムリーで突き放しました。打順の巡り合わせで打点こそありませんでしたが、ブランドンが4打数3安打というのも嬉しいニュースです。

6回以降は大曲、公文、森脇、田村とつないで逃げ切り。一日でファイターズと入れ替わって5位に浮上しました。また、9回を三者凡退に抑えた田村はプロ初セーブとなります。おめでとうございます。