2024/04/02(火)ベルーナドームに行ってきました(vs オリックス 第1回戦)
2024/04/02 24:38

【埼玉西武 vs オリックス 第1回戦】 (2024年04月02日/ベルーナドーム) オリックス 0 0 0 0 0 0 1 0 0 1 埼玉西武 0 2 0 0 0 0 0 0 x 2 [勝] 平良 1勝0敗0S [S] アブレイユ 0勝0敗2S [敗] カスティーヨ 0勝1敗0S本拠地開幕戦に行ってきました。
ライオンズの先発は平良。ネット裏とはいえ最上段の席からの観戦だったので、あまり細かいコースは分からなかったのですが、かなり制球に苦しんでいたように見えました。フォアボール5つも多いですが、それ以外でも3ボールになるシーンが目立ちました。5回2/3で120球という球数にもそれが表れているでしょう。むしろ6回のマウンドに上がったときに少し驚きました。
とはいえ、スピードは出ていましたし、連打を浴びることもなく、悪いなりにゼロで抑えてゲームを作ってくれました。このあたりはさすがです。松本さん、見ていましたか?
平良は5回2アウト一二塁のピンチを残して降板しましたが、2番手の水上が牽制で二塁ランナーを刺してピンチを脱します。キャッチャーからのサインだったと思うのですが、外崎のカバーのタイミング含め、気持ちいいくらい完璧な牽制でしびれました。こういうのが見たくて球場に行っているんですよ。
打つ方は2回、中村の内野安打、佐藤龍のレフト前ヒットでチャンスを作ると、西川のバントが内野安打になって満塁。続く古賀の内野ゴロの間に1点を先制します。さらに源田の右中間の当たりがセンター福田の超ファインプレイに阻まれますが、三塁ランナータッチアップでもう1点を追加しました。
このシーンは三塁ランナーの佐藤が一回戻りかけてから再スタートを切ったため、ちゃんとタッチアップができていないようにも見えた(だからこそバファローズもリクエストしたのだと思います)のですが、正規のタッチアップをしたあと戻りかけただけのようです。おそらく、捕球前に離塁した二塁ランナーの西川に対する阿部コーチの「戻れ」という指示を、佐藤が自分に向けられたと勘違いして一瞬戻ったのかと思います。現場では阿部が悪いと思っていましたが、あとから振り返ってみると西川の判断ミスが原因ですね。反省してください。
その後はチャンスを作りながらもあと一本が出ない展開でしたが、水上のあと本田、甲斐野、アブレイユとつないで1点差を逃げ切り。本拠地開幕戦を白星で飾りました。本田は3連打でいきなり1点を失ったときはどうなることかと思いましたが、頓宮、森、セデーニョという厳しいところを打ち取って切り抜けました。
今日はブランドンに代わって起用された佐藤、若林に代わって起用された西川がともに2安打と結果を残したのが大きいです。しばらくはこのオーダーが続くんですかね。
2024/04/01(月)4月1日
2024/04/01 21:38
ラブライブ!シリーズ× パ・リーグ6球団 第2弾スペシャルコラボが決定!ライオンズは8/24(土)楽天戦でコラボ試合を開催! | 埼玉西武ライオンズ
ラブライブ!シリーズ × パ・リーグ6球団 第2弾スペシャルコラボが決定! - 千葉ロッテマリーンズ
いろいろな意味で仙台はまだ迷っているのですが(長距離移動が必要な上に雨天中止の可能性がある、いくらAqoursだとはいえ着ているのはあのチームのユニフォームである、など)、関東圏の2試合は万難を排して駆けつける所存です。ちなみにマリーンズ戦の翌日はいつものメンバーとベルーナドームに交流戦を見に行く予定ですw
今年は事前にグッズが買えるようなので(ライオンズは去年の幻日のヨハネコラボのときもそうでしたが)、無理して早起きする必要はなさそうです。マリーンズのグッズをどこまで買うかは悩みどころですが。
……え? ライオンズのグッズ? 端から端までですよ?
2024/03/31(日)サヨナラ負け(vs 東北楽天 第3回戦)
2024/03/31 20:13
【東北楽天 vs 埼玉西武 第3回戦】 (2024年03月31日/楽天モバイルパーク宮城) 埼玉西武 0 0 3 0 0 0 0 0 0 0 0 3 東北楽天 0 0 3 0 0 0 0 0 0 0 1x 4 [勝] 渡辺翔 1勝0敗0S [敗] 糸川 0勝1敗0S今年もラブライブ!コラボが発表されました 。
8月24日のベルーナドームには当然行くとして、6月15日のZOZOマリンも今のところ行くつもりです。場合によっては7月14日の仙台にも行くかもしれません。 ZOZOマリンのチケット一般販売開始は4月24日 、 仙台のチケット一般販売開始は5月26日 ……よし、覚えた。
さて、試合のほうですが、3回にコルデロ、外崎の連続タイムリーと相手エラーで3点を先制しますが、その裏、松本が突然崩れて3失点。まぁ、崩れたのは突然なのですが、ボールが甘いのは初回からだったのですが……。
4回以降は両チームランナーを出しながらも得点には結びつかず、試合は延長戦へ。
10回表、11回表とライオンズは得点圏にランナーを進めますが、あと一本が出ずに無得点。11回裏は7番手の糸川が先頭の石原にフォアボールを与えると、村林の送りバント、茂木のヒットで1アウト一三塁とピンチを広げ、小深田の犠牲フライでサヨナラ負け。痛い黒星ではありますが、プロ初登板には厳しい場面だったと思うので、糸川にはこれを成長の糧にしてもらいたいと思います。
今日は松本がもっとしっかりしてくれていれば勝てたゲームではありますが、ビジターでの開幕戦でカード勝ち越しできたということで前向きに考えましょう。
2024/03/30(土)開幕連勝(vs 東北楽天 第2回戦)
2024/03/30 22:13
【東北楽天 vs 埼玉西武 第2回戦】 (2024年03月30日/楽天モバイルパーク宮城) 埼玉西武 1 0 3 1 0 0 0 1 2 8 東北楽天 0 2 0 0 0 0 0 0 0 2 [勝] 隅田 1勝0敗0S [敗] 荘司 0勝1敗0S [本塁打] 4回表 古賀 1号 ソロ (荘司)ライオンズ先発の隅田は立ち上がりからピリッとしませんでした。初回は2アウト満塁のピンチをしのぎましたが、2回には太田と阿部のタイムリーで2点を失い逆転を許します。しかし、その後はどうにか踏ん張って5回2/3を2失点。ストレートの制球に苦しんでいたせいもあるのでしょうが、変化球頼みの苦しいピッチングでしたが、それでもゲームは作ってくれました。
打線は初回に金子侑、コルデロの連打で1点を先制。逆転を許した直後の3回には外崎のタイムリーとアギラーの2点タイムリーツーベースで逆転に成功します。その後も古賀のホームランなどもあって、終わってみれば15安打8得点で快勝。得点シーン以外にもけっこうチャンスはあって、12残塁ともう少し点が取れたような気がしないでもありませんが、ひとまずはこれだけヒットを打てたことを喜ぼうと思います。
これでライオンズは開幕2連勝。ほかの球場はいずれも1勝1敗なので、現時点で単独首位に立ちました。いつ以来だろう……。
2024/03/30(土)雀魂の牌譜をNAGAに解析させる -段位pt期待値対応版-
2024/03/30 20:41
0. はじめに
本エントリで紹介しているプログラムは、 Apache License, Version 2.0 で公開します。ライセンスと言われると、プログラマーでない方は身構えてしまうかもしれませんが、単純に利用するぶんにはこれまでのバージョンと何か変わることはありません。本エントリの内容が、あなたの良き雀魂ライフのお手伝いとなれば幸いです。
1. Tampermonkeyをインストールする
ブラウザの拡張機能である Tampermonkey をインストールします。私はGoogle Chromeでしか試していませんが、メジャーどころのブラウザにはほとんど対応しているようです。2. Tampermonkeyにdownloadlogsを登録する
雀魂の牌譜データをダウンロードするスクリプトをTampermonkeyに登録します。まず、Tampermonkeyのエディターを起動しましょう。ブラウザによって多少操作は異なると思いますが、Chromeの場合は拡張機能のアイコンをクリックし、表示されるメニューから[Tampermonkey]→[新規スクリプトを追加...]を選択してください。
起動したエディターに downloadlogs.js の内容を貼り付けます。このとき、15行目の
const NAMEPREF = 1 を const NAMEPREF = 0 に書き換えてください。メニューの[ファイル]→[保存]を選択すれば登録は完了です。
3. Tampermonkeyにsoul2nagaを登録する
2と同様の手順で、Tampermonkeyに以下のスクリプトを登録してください。パラメーターの都合上、そのままでは魂天は雀聖3として扱われます。もしあなたが魂天である場合、36行目をコメントアウト(行頭に
// を追加する)し、37~40行目のうち対象の行(たとえば、頂上決戦でない半荘戦は37行目)をアンコメント(行頭の // を削除する)すると、魂珠の増減数を解析できます。雀聖以下の場合は特に変更の必要はありません。
// Copyright 2021-2024 HASEBA Junya
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ==UserScript==
// @name soul2naga
// @namespace lions.blue
// @icon http://1.gravatar.com/avatar/6fa3836d10d691125749472297cf516a
// @version 2.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() {
/**
* @type {string} 順位点配分・素点倍率を固定値で指定する際のパラメーター。
*
* nullを指定した場合は、段位と卓のレベルに応じた順位点配分・素点倍率を使用する。
* このとき、パラメーターの都合上、魂天は雀聖3として扱う。
*
* パラメーターの記述ルールは公式の説明( https://twitter.com/NAGA025/status/1768508358179615190 )を参照のこと。
* 魂天視点で解析する場合は、36行目をコメントアウトし、37~40行目の該当行を有効化すると魂珠の増減数がパラメーターになる。
*/
const CUSTOM_PARAMETER = null;
// const CUSTOM_PARAMETER = "[0.5, 0.2, -0.2, -0.5], [0.5, 0.2, -0.2, -0.5], [0.5, 0.2, -0.2, -0.5], [0.5, 0.2, -0.2, -0.5], 0"; // 魂天・半荘の場合
// const CUSTOM_PARAMETER = "[1.0, 0.4, -0.4, -1.0], [1.0, 0.4, -0.4, -1.0], [1.0, 0.4, -0.4, -1.0], [1.0, 0.4, -0.4, -1.0], 0"; // 魂天・半荘・頂上決戦の場合
// const CUSTOM_PARAMETER = "[0.3, 0.1, -0.1, -0.3], [0.3, 0.1, -0.1, -0.3], [0.3, 0.1, -0.1, -0.3], [0.3, 0.1, -0.1, -0.3], 0"; // 魂天・東風の場合
// const CUSTOM_PARAMETER = "[0.6, 0.2, -0.2, -0.6], [0.6, 0.2, -0.2, -0.6], [0.6, 0.2, -0.2, -0.6], [0.6, 0.2, -0.2, -0.6], 0"; // 魂天・東風・頂上決戦の場合
/** @type {string} ダウンロードリンクのhref属性において、牌譜データに先行する部分の文字列。 */
const DOWNLOAD_HREF_PREFIX = "data:text/plain;charset=utf-8,";
/** @type {string} 天鳳牌譜エディタの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 = convertToViewerUrls(soulJson);
links[i].href = buildDownloadHref(urls);
links[i].download = buildFileName(links[i].download);
break;
}
}
}, {capture: true});
/**
* ダウンロードリンクか否かを判定する。
*
* @param {HTMLElement} element 判定対象の要素を指定する。
* @returns {boolean} ダウンロードリンクの場合はtrue、それ以外の場合はfalseを返す。
*/
function isDownloadLink(element) {
// href属性の先頭部分で判定する。
return element.href.startsWith(DOWNLOAD_HREF_PREFIX);
}
/**
* ダウンロードリンクから雀魂の牌譜JSONを抽出する。
*
* @param {HTMLElement} element ダウンロードリンクを指定する。
* @returns {string} 雀魂の牌譜JSONを返す。
*/
function fetchSoulJson(element) {
// href属性から牌譜JSONを抽出する。
return decodeURIComponent(element.href.replace(DOWNLOAD_HREF_PREFIX, ""));
}
/**
* 雀魂の牌譜JSONを牌譜エディタのURL群に変換する。
*
* @param {string} soulJson 雀魂の牌譜JSONを指定する。
* @returns {Array<string>} 牌譜エディタのURL群を返す。
*/
function convertToViewerUrls(soulJson) {
// 雀魂の牌譜JSONをオブジェクトに変換する。
const soulPaifu = JSON.parse(soulJson);
// title内の卓名を雀魂っぽく変換し、順位点配分・素点倍率を付与する。
const title = structuredClone(soulPaifu.title);
title[0] = toSoulRoom(title[0]);
title[1] = buildPtEVParameter(title[0], soulPaifu.dan);
// プレイヤー名をエンコードする。
const name = structuredClone(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 = structuredClone(soulPaifu.rule);
rule.disp = toSoulRoom(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} tenhouRoom 天鳳っぽい卓名を指定する。
* @returns {string} 雀魂っぽい卓名を返す。
*/
function toSoulRoom(tenhouRoom) {
// 卓名を雀魂っぽく変換する。
//
// 半荘: ○の間四人南
// 東風: ○の間四人東
return tenhouRoom.replace(/(.)喰赤/, "四人$1");
}
/**
* 順位点配分・素点倍率を組み立てる。
*
* @param {string} room 卓名を指定する。
* @param {Array<string>} dan プレイヤーの段位を指定する。
* @returns {string} 順位点配分・素点倍率を返す。
*/
function buildPtEVParameter(room, dan) {
// カスタムパラメーターが指定されている場合はそのまま利用する。
if (CUSTOM_PARAMETER !== null) {
return CUSTOM_PARAMETER;
}
// 順位点配分・素点倍率を組み立てる。
const pointTable = fetchPointTable(room);
if (pointTable !== null) {
return JSON.stringify(dan.map(function(v) {
const rank = v.startsWith("魂天") ? "雀聖★3" : v;
return pointTable[0].concat(pointTable[1][rank]);
}).concat(1)).match(/^\[(.+)\]$/)[1];
} else {
return "";
}
}
/**
* 卓に応じた順位点配分テーブルを取得する。
*
* @param {string} room 卓名を指定する。
* @returns {Array<Array<number>, object>} 順位配分点テーブルを返す。
*/
function fetchPointTable(room) {
// 卓に応じた順位点配分テーブルを返す。
//
// 参考URL:
// https://mahjongsoul.info/how_to_enjoy1/
switch (room) {
case "銅の間四人南":
return [
[35, 15, -5], {
"初心★1": -15,
"初心★2": -15,
"初心★3": -15,
"雀士★1": -35,
"雀士★2": -55,
"雀士★3": -75,
},
];
case "銀の間四人南":
return [
[55, 25, -5], {
"雀士★1": -35,
"雀士★2": -55,
"雀士★3": -75,
"雀傑★1": -95,
"雀傑★2": -115,
"雀傑★3": -135,
},
];
case "金の間四人南":
return [
[95, 45, -5], {
"雀傑★1": -95,
"雀傑★2": -115,
"雀傑★3": -135,
"雀豪★1": -180,
"雀豪★2": -195,
"雀豪★3": -210,
},
]
case "玉の間四人南":
return [
[125, 60, -5], {
"雀豪★1": -180,
"雀豪★2": -195,
"雀豪★3": -210,
"雀聖★1": -225,
"雀聖★2": -240,
"雀聖★3": -255,
},
];
case "王座の間四人南":
return [
[135, 65, -5], {
"雀聖★1": -225,
"雀聖★2": -240,
"雀聖★3": -255,
},
];
case "銅の間四人東":
return [
[25, 10, -5], {
"初心★1": -15,
"初心★2": -15,
"初心★3": -15,
"雀士★1": -25,
"雀士★2": -35,
"雀士★3": -45,
},
];
case "銀の間四人東":
return [
[35, 15, -5], {
"雀士★1": -25,
"雀士★2": -35,
"雀士★3": -45,
"雀傑★1": -55,
"雀傑★2": -65,
"雀傑★3": -75,
},
];
case "金の間四人東":
return [
[55, 25, -5], {
"雀傑★1": -55,
"雀傑★2": -65,
"雀傑★3": -75,
"雀豪★1": -95,
"雀豪★2": -105,
"雀豪★3": -115,
},
];
case "玉の間四人東":
return [
[70, 35, -5], {
"雀豪★1": -95,
"雀豪★2": -105,
"雀豪★3": -115,
"雀聖★1": -125,
"雀聖★2": -135,
"雀聖★3": -145,
},
];
case "王座の間四人東":
return [
[75, 35, -5], {
"雀聖★1": -125,
"雀聖★2": -135,
"雀聖★3": -145,
},
];
default:
return null;
}
}
/**
* logをNAGAが解析可能な形式に変換する。
*
* @param {Array<Array>} soulLog 雀魂形式のlogを指定する。
* @returns {Array<Array>} NAGAで解析可能な形式のlogを返す。
*/
function toNagaLog(soulLog) {
// 流局のデータは変換する必要はない。
if (soulLog[16].length < 3) {
return soulLog;
}
const nagaLog = structuredClone(soulLog);
// 当該局の場風牌を算出する。
//
// 数字と局の対応:
// 0 => 東1局, 1 => 東2局, ...
const prevalentWind = ["東", "南", "西", "北"][Math.floor(nagaLog[0][0] / 4)];
// 役名をNAGAが解析可能な表記に変換する。
// ダブロン・トリロンに対応するため複数回繰り返す。
for (let i = 1; i < nagaLog[16].length; i += 2) {
// 和了者の自風牌を算出する。
//
// 算出方法:
// (和了者のプレイヤー番号 - 親の位置 + 4) % 4
const seatWind = ["東", "南", "西", "北"][
(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, prevalentWind, seatWind);
}
));
}
// 変換後のlogを返す。
return nagaLog;
}
/**
* 役名をNAGAが解析可能な表記に変換する。
*
* @param {string} hand 和了役を指定する。
* @param {string} prevalentWind 場風牌を指定する。
* @param {string} seatWind 和了者の自風牌を指定する。
* @returns {string} NAGAで解析可能な表記の役名を返す。
*/
function toNagaHand(hand, prevalentWind, seatWind) {
// 対応が必要な役が判明次第、随時追加する。
switch(hand) {
case "役牌:場風牌(1飜)":
return `場風 ${prevalentWind}(1飜)`;
case "役牌:自風牌(1飜)":
return `自風 ${seatWind}(1飜)`;
case "ダブル立直(2飜)":
return "両立直(2飜)";
default:
return hand;
}
}
/**
* ダウンロードリンクの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 toSoulRoom(baseFileName).replace(".json", ".txt");
}
})();
4. 牌譜データをダウンロードする
雀魂にログインし、対象の牌譜画面でキーボードのS キーを押してください。設定が正しく行われていれば、牌譜エディタのURLが並んだテキストファイルをダウンロードできるはずです。5. NAGAに解析させる
4でダウンロードしたファイルは1行が1局の構成になっています。解析したい局を「カスタム牌譜解析」のページから読み込ませてください。すべての行をまとめて読み込ませれば、半荘を単一のレポートとしてまとめることもできます。6. 謝辞
「2. Tampermonkeyにdownloadlogsを登録する」で利用しているdownloadlogs.jsを作成された Equimさん に感謝いたします。ありがとうございます。本エントリはNAGAに関するものですが、Equimさんは Mortal の開発者でもあります。いつもお世話になっております。パラメーターを作る際の順位点の配分は 雀魂.info の記事を参考にさせていただきました。ありがとうございます。こちらもいつもお世話になっております。