2021/12/16(木)ボー

2021/12/16 18:55

光成のスコアボードの表記はどうなるんですかね。

西武に24歳ブラジル人右腕、ボー・タカハシ入団 渡辺GM「リリーフで」 - プロ野球 : 日刊スポーツ

韓国リーグと聞いて、バンヘッケンの嫌な思い出が蘇りましたが、バンヘッケンとは違ってその韓国リーグの実績で獲ったわけでもなさそうなので、温かい目で見守ろうと思います。

まだ24歳と若くて、無限の可能性がある投手です。

そういうことは、フォームをぶっ壊して数多の無限の可能性を潰してきたコーチを更迭してから言ってください。

2021/12/15(水)蚊帳の外

2021/12/15 21:37

MVPや新人王がライオンズに関係ないのは明白なので、今年の結果発表は完全に他人事でしたw

ファーム打撃2冠西武渡部健人「ホームラン王取りたい」来季1軍で大暴れだ - プロ野球 : 日刊スポーツ

二軍のシーズンが一足先に終わったあと、当然最後は一軍に上がって経験を積むものと思っていましたが、メンバーを固定したがる首脳陣ではそれも叶わず、少し消化不良気味なところはあったかもしれません。二軍であれだけ結果を残してすぐに使わないって何だよ。

まぁ、今年は一軍にほとんどいなかったからこそ阿部や赤田に壊されなかったという考え方もできるので、来年はより一層の飛躍の年にしてもらいたいと思います。骨牙を控えに押しやるくらいの活躍を期待しています。

2021/12/14(火)ベストナイン

2021/12/14 18:11

森選手、源田選手、受賞おめでとうございます。細かいところでは不満もありましたが、トータルで見れば今年もよく頑張ってくれました。

投票結果(ベストナイン) | 2021年 表彰選手 | NPB.jp 日本野球機構

セ・リーグについては明るくないので論評を差し控えますが、パ・リーグについては納得の行く結果かと思います。外野の3人目は荻野でも良かったと思いますし、私に投票権があれば杉本、吉田、荻野に入れますが、柳田でも決しておかしくないでしょう。実際、柳田と荻野の差は19票ということで、記者の意見も割れたことが分かります。

しかし、この結果を不満に思う人たちが、NPBやスポーツ紙のTwitterアカウントにクレームをつけているのは実に恥ずかしい光景ですね。ちなみに、君たちは知らないかもしれないけど、ベストナインはポジションごとに優れた結果を残した選手を記者投票で選ぶタイトルなんだ。打点を一番多く挙げた選手を表彰するために、これとは別に「打点王」というタイトルがあるんだよ。覚えておこうね。

2021/12/13(月)継続だけでは力にならない

2021/12/13 20:17

今年最後のリリースが終わったので、どうやら無事に仕事を納められそうです。

西武今井達也「もっとストライクゾーンで勝負」完投数増へ与四球減誓う - プロ野球 : 日刊スポーツ

言っていること自体はまったくそのとおりなのですが、それをどうやって実現するかは頭を使ってよく考えてほしいと思います。フォーム探しの旅をやめられるくらい安定したフォームを身につければコントロールは改善するでしょうし、球威を磨いて多少甘くなっても構わないからグイグイ押すという考え方もあるでしょう。自分に合ったスタイルを真剣に考えた上で練習に取り組んでほしいと思います。

最悪なのは、

???「制球力をつけるために腕を下げてみようか」

となることですね……。

2021/12/12(日)雀魂の牌譜をNAGAに解析させる-完全版-

2021/12/12 22:29 ゲーム::雀魂技術::JavaScript
アクセスありがとうございます。本エントリの内容は現在(2024年3月30日現在)でも有効なものですが、このコードがほかの方に転用されていることがわかったため、いったん破棄します。今後はライセンスを明記し、内容的にもパワーアップした 雀魂の牌譜をNAGAに解析させる -段位pt期待値対応版- をご参照いただけると幸いです。


以前、 雀魂の牌譜をNAGAに解析させる というエントリを書きました。ありがたいことに、このエントリは今でも多くのアクセスを集めています。このたび、それよりもパワーアップした、完全版と呼んでも差し支えない仕組みができました。ぜひご覧ください。

0. はじめに


NAGA のカスタム牌譜解析サービスがスタートし、より多くの人が利用するようになって以降、NAGAが深層学習AIだということを無視した言動が散見されるようになりました。

NAGAは 天鳳 の特上卓で十段に到達しました。しかし、特上卓で十段に達した人間のプレイヤーと同等の実力があるとは言えません。特上卓で十段になれるような選択を繰り返すことができるだけなのです。これは似ているようで大きく違います。

雀魂 の牌譜を解析させた場合、ラスを避けたいルールという共通点があるので、それほどおかしな結果を出すことはないと思います。しかし、ポイント配分や素点の影響有無の差がある以上、必ずしも雀魂でのベストな解を教えてくれるとは限りません。「天鳳の特上卓で打っていると仮定した場合、この選択を繰り返せば十段になれる」ということを前提に、自分の打牌を振り返るきっかけとしましょう。

本エントリの内容が、あなたの良き雀魂ライフのお手伝いとなれば幸いです。

1. Tampermonkeyをインストールする


ブラウザの拡張機能である Tampermonkey をインストールします。私はGoogle Chromeでしか試していませんが、メジャーどころのブラウザにはほとんど対応しているようです。

2. Tampermonkeyにdownloadlogsを登録する


雀魂の牌譜データをダウンロードするスクリプトをTampermonkeyに登録します。

まず、Tampermonkeyのエディターを起動しましょう。ブラウザによって多少操作は異なると思いますが、Chromeの場合は拡張機能のアイコンをクリックし、表示されるメニューから[Tampermonkey]→[新規スクリプトを追加...]を選択してください。

起動したエディターに downloadlogs.js の内容を貼り付けます。このとき、15行目の const NAMEPREF = 1const NAMEPREF = 0 に書き換えてください。

メニューの[ファイル]→[保存]を選択すれば登録は完了です。

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

3. Tampermonkeyにsoul2nagaを登録する(2022-11-24更新)


2と同様の手順で、Tampermonkeyに以下のスクリプトを登録してください。
// ==UserScript==
// @name         soul2naga
// @namespace    lions.blue
// @icon         http://1.gravatar.com/avatar/6fa3836d10d691125749472297cf516a
// @version      1.0.0
// @description  downloadlogsで取得する牌譜をNAGAで解析可能な形式に変換する
// @include      https://mahjongsoul.game.yo-star.com/*
// @include      https://game.mahjongsoul.com/*
// @include      https://majsoul.union-game.com/0/*
// ==/UserScript==

(function() {
    // ダウンロードリンクのhref属性において、牌譜データに先行する部分の文字列。
    const DOWNLOAD_HREF_PREFIX = "data:text/plain;charset=utf-8,";

    // 天鳳牌譜エディタのURLにおいて、牌譜データに先行する部分の文字列。
    const EDITOR_URL_PREFIX = "https://tenhou.net/6/#json=";

    // ダウンロードイベントの前に割り込んで牌譜データを書き換える。
    document.addEventListener("click", function(e) {
        const links = document.body.getElementsByTagName("a");
        for (let i = 0; i < links.length; i++) {
            if (isDownloadLink(links[i])) {
                const soulJson = fetchSoulJson(links[i]);
                const urls = createViewerUrls(soulJson);
                links[i].href = buildDownloadHref(urls);
                links[i].download = buildFileName(links[i].download);
                return;
            }
        }
    }, {capture: true});

    /**
     * オブジェクトをディープコピーする。
     * 
     * @param {Object} src コピー対象のオブジェクトを指定する。
     * @returns {Object} 複製したオブジェクトを返す。
     */
    function deepCopy(src) {
        // JSON文字列化してからオブジェクトに戻すことでディープコピーを実現する。
        return JSON.parse(JSON.stringify(src));
    }

    /**
     * ダウンロードリンクであるか判定する。
     * 
     * @param {HTMLElement} element 判定対象の要素を指定する。
     * @returns {Boolean} ダウンロードリンクの場合はtrue、それ以外の場合はfalseを返す。
     */
    function isDownloadLink(element) {
        // href属性の先頭部分で判定する。
        return element.href.startsWith(DOWNLOAD_HREF_PREFIX);
    }

    /**
     * ダウンロードリンクから雀魂の牌譜データを抽出する。
     * 
     * @param {HTMLElement} element ダウンロードリンクを指定する。
     * @returns {String} 雀魂の牌譜データを返す。
     */
    function fetchSoulJson(element) {
        // href属性から牌譜データを抽出する。
        return decodeURIComponent(element.href.replace(DOWNLOAD_HREF_PREFIX, ""));
    }

    /**
     * ダウンロードリンクのhref属性を組み立てる。
     * 
     * @param {Array<String>} urls 牌譜エディタのURL群を指定する。
     * @returns {String} ダウンロードリンクのhref属性を返す。
     */
    function buildDownloadHref(urls) {
        // ダウンロードリンクのhref属性を組み立てる。
        return DOWNLOAD_HREF_PREFIX + encodeURIComponent(urls.join("\n"));
    }

    /**
     * ダウンロードファイル名を組み立てる。
     * 
     * @param {String} baseFileName もともとのダウンロードファイル名を指定する。
     * @returns {String} 組み立てたダウンロードファイル名を返す。
     */
    function buildFileName(baseFileName) {
        // 卓名を雀魂っぽく変換し、拡張子を.txtに変更する。
        return toSoulTable(baseFileName).replace(".json", ".txt");
    }

    /**
     * 雀魂の牌譜JSONを牌譜エディタのURL群に変換する。
     * 
     * @param {String} soulJson 雀魂の牌譜JSONを指定する。
     * @returns {Array<String>} 牌譜エディタのURL群を返す。
     */
    function createViewerUrls(soulJson) {
        // 雀魂の牌譜JSONをオブジェクトに変換する。
        const soulPaifu = JSON.parse(soulJson);

        // title内の卓名を雀魂っぽく変換する。
        // 
        // 変換前のtitle:
        //     "title": [ "玉の間南喰赤", "2021/10/20 20:48:01" ]
        // 変換後のtitle:
        //     "title": [ "玉の間四人南", "2021/10/20 20:48:01" ]
        const title = deepCopy(soulPaifu.title);
        title[0] = toSoulTable(title[0]);

        const name = deepCopy(soulPaifu.name);
        const encodedName = name.map(function(v) {
            return encodeURIComponent(v);
        });

        // rule内の卓名を雀魂っぽく変換する。
        // 
        // 変換前のrule:
        //     "rule": { "disp": "玉の間南喰赤", "aka53": 1, "aka52": 1, "aka51": 1 }
        // 変換後のrule:
        //     "rule": { "disp": "玉の間四人南", "aka53": 1, "aka52": 1, "aka51": 1 }
        const rule = deepCopy(soulPaifu.rule);
        rule.disp = toSoulTable(rule.disp);

        // logを局ごとのデータに分割し、牌譜エディタのURL群として返す。
        return soulPaifu.log.map(function(v) {
            return EDITOR_URL_PREFIX + JSON.stringify({
                "title": title,
                "name": encodedName,
                "rule": rule,
                "log": [toNagaLog(v)],
            });
        });
    }

    /**
     * 卓名を雀魂っぽく変換する。
     * 
     * @param {String} tenhouTable 天鳳っぽい卓名を指定する。
     * @returns {String} 雀魂っぽい卓名を返す。
     */
    function toSoulTable(tenhouTable) {
        // 表記の好みの問題なので、必ずしも必要となる処理ではない。
        return tenhouTable.replace("南喰赤", "四人南");
    }

    /**
     * logをNAGAが解析可能な形式に変換する。
     * 
     * @param {Array<Array>} soulLog 雀魂形式のlogを指定する。
     * @returns {Array<Array>} NAGAで解析可能な形式のlogを返す。
     */
    function toNagaLog(soulLog) {
        // 流局のデータは変換の必要がない。
        if (soulLog[16].length < 3) {
            return soulLog;
        }
        const nagaLog = deepCopy(soulLog);

        // 当該局の場風を算出する。
        // 
        // 局を表す数字と意味:
        //     0 => 東1局, 1 => 東2局, ...
        const prevalent = ["東", "南", "西", "北"][Math.floor(nagaLog[0][0] / 4)];

        // 役名をNAGAが解析可能な表記に変換する。
        // ダブロン・トリロンに対応するため複数回繰り返す。
        for (let i = 1; i < nagaLog[16].length; i += 2) {
            // 当該局における和了者の自風を設定する。
            // 
            // 算出方法:
            //     (和了者のプレイヤー番号 - 親の位置 + 4) % 4
            const seat = ["東", "南", "西", "北"][
                (nagaLog[16][i].indexOf(Math.max(...nagaLog[16][i])) - (nagaLog[0][0] % 4) + 4) % 4
            ];

            // 役名をNAGAが解析可能な表記に変換する。
            nagaLog[16][i + 1] = nagaLog[16][i + 1].slice(0, 4).concat(
                nagaLog[16][i + 1].slice(4).map(function(v) {
                    return toNagaHand(v, prevalent, seat);
                }
            ));
        }

        // 変換後のlogを返す。
        return nagaLog;
    }

    /**
     * 役名をNAGAが解析可能な表記に変換する。
     * 
     * @param {String} hand 和了役を指定する。
     * @param {String} prevalent 場風を指定する。
     * @param {String} seat 和了者の自風を指定する。
     * @returns {String} NAGAで解析可能な表記の役名を返す。
     */
    function toNagaHand(hand, prevalent, seat) {
        // 対応が必要な役が判明次第、随時追加する。
        switch(hand) {
            case "役牌:場風牌(1飜)":
                return `場風 ${prevalent}(1飜)`;
            case "役牌:自風牌(1飜)":
                return `自風 ${seat}(1飜)`;
            case "ダブル立直(2飜)":
                return "両立直(2飜)";
            default:
                return hand;
        }
    }
})();

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


雀魂にログインし、対象の牌譜画面でキーボードの S キーを押してください。設定が正しく行われていれば、牌譜エディタのURLが並んだテキストファイルをダウンロードできるはずです。

5. NAGAに解析させる


4でダウンロードしたファイルは1行が1局の構成になっています。解析したい局を「カスタム牌譜解析」のページから読み込ませてください。すべての行をまとめて読み込ませれば、半荘を単一のレポートとしてまとめることもできます。