概要
mahjong は Python 向けの麻雀ライブラリです。
点数計算・シャンテン数計算・牌姿文字列のパースなどを提供します。
v1.2.1 のリリース後、リポジトリはしばらくアーカイブ状態でした。
その後、v1.3.0・v1.4.0 でバグ修正や型アノテーション追加などのメンテナンスが行われ、今回 v2.0.0 がリリースされました。
本記事では、v1.4.0 から v2.0.0 への主な変更点と、移行時に注意すべき破壊的変更を紹介します。
筆者は v1.3.0 以降の開発に参加しており、v2.0.0 からメンテナーを務めています。
PyPI:
GitHub:
v1.4.0 → v2.0.0 変更点ハイライト
点数計算が大幅に高速化
面子分解アルゴリズムや役判定処理の見直しにより、点数計算が高速化されました。
500,000手の点数計算を用いたベンチマークでは、v1.4.0 比で 約6倍 のスループットを記録しています(5回計測した中央値)。
| バージョン | スループット [hand/s] | 1 手あたり平均時間 [ms] |
|---|---|---|
| 1.4.0 | 5,925 | 0.169 |
| 2.0.0 | 35,364 | 0.028 |
裏ドラに対応
HandCalculator.estimate_hand_value() に ura_dora_indicators (裏ドラ表示牌) 引数が追加され、立直・ダブル立直時の裏ドラ計算に対応しました。
from mahjong.hand_calculating.hand import HandCalculator
from mahjong.hand_calculating.hand_config import HandConfig
from mahjong.tile import TilesConverter
tiles = TilesConverter.one_line_string_to_136_array("123456m33p123456s")
win_tile = TilesConverter.one_line_string_to_136_array("6m")[0]
ura_dora_indicators = TilesConverter.one_line_string_to_136_array("2p")
config = HandConfig(is_riichi=True)
result = HandCalculator.estimate_hand_value(
tiles,
win_tile,
ura_dora_indicators=ura_dora_indicators,
config=config,
)
print(f"{result.fu}符{result.han}飜")
print(result.yaku)
出力:
30符4飜
[Riichi, Pinfu, Ura Dora 2]
裏ドラは立直・ダブル立直時のみ有効です。
場風牌・自風牌を風牌ごとに分離
v1.4.0 では、点数計算結果に含まれる場風牌・自風牌は、YakuhaiOfPlace / YakuhaiOfRound として表現されていました。
v2.0.0 では、自風・場風それぞれについて東南西北の個別クラスが導入されました。
自風牌は以下のクラスで表現されます。
SeatWindEastSeatWindSouthSeatWindWestSeatWindNorth
場風牌は以下のクラスで表現されます。
RoundWindEastRoundWindSouthRoundWindWestRoundWindNorth
これにより、「自風 東」「場風 南」などを個別の役として扱えるようになり、天鳳の役表示に近い粒度で扱いやすくなりました。
from mahjong.constants import EAST, SOUTH
from mahjong.hand_calculating.hand import HandCalculator
from mahjong.hand_calculating.hand_config import HandConfig
from mahjong.tile import TilesConverter
tiles = TilesConverter.one_line_string_to_136_array("123456m33p111z222z")
win_tile = TilesConverter.one_line_string_to_136_array("6m")[0]
config = HandConfig(player_wind=EAST, round_wind=SOUTH)
calculator = HandCalculator()
result = calculator.estimate_hand_value(tiles, win_tile, config=config)
print(result.yaku)
v1.4.0 の出力:
[Yakuhai (wind of place), Yakuhai (wind of round)]
v2.0.0 の出力:
[Yakuhai (seat wind east), Yakuhai (round wind south)]
この変更により、風牌役のクラスは v1.x とは互換性がありません。
YakuhaiOfPlace や YakuhaiOfRound などの旧クラスを直接参照しているコードは修正が必要です。
主要APIのstaticmethod化
点数計算やシャンテン数計算などの主要APIが staticmethod 化され、インスタンス生成なしで呼び出せるようになりました。
v1.4.0:
from mahjong.shanten import Shanten
from mahjong.tile import TilesConverter
tiles = TilesConverter.one_line_string_to_34_array('13569m123459p443s')
shanten = Shanten()
print(shanten.calculate_shanten(tiles))
v2.0.0:
from mahjong.shanten import Shanten
from mahjong.tile import TilesConverter
tiles = TilesConverter.one_line_string_to_34_array("13569m123459p443s")
print(Shanten.calculate_shanten(tiles))
主な staticmethod 化対象:
HandCalculator.estimate_hand_value()Agari.is_agari()Shanten.calculate_shanten()HandDivider.divide_hand()FuCalculator.calculate_fu()ScoresCalculator.calculate_scores()
TypedDict による戻り値の型情報の詳細化
一部の戻り値の型アノテーションが通常の dict から TypedDict ベースに変更されました。
以下の TypedDict が追加されました。
ScoresResultFuDetailSuitCount
HandResponse.cost は ScoresResult | None、HandResponse.fu_details は list[FuDetail] | None として型付けされるようになりました。
これにより、型チェッカーやIDE補完で戻り値の扱いが分かりやすくなりました。
シャンテン数計算の入力検証を強化
Shanten.calculate_shanten() と Shanten.calculate_shanten_for_regular_hand() の入力検証が強化されました。
v2.0.0 では、15枚以上や0・3・6・9・12枚のような、実戦上発生しない枚数の手牌に対しては ValueError が送出されます。
また、手牌枚数が13枚未満、つまり副露面子が別途ある前提の入力では、七対子・国士無双のシャンテン数計算を無視するようになりました。
たとえば以下の手牌では従来と計算結果が異なります。
from mahjong.shanten import Shanten
from mahjong.tile import TilesConverter
tiles = TilesConverter.one_line_string_to_34_array("19m19p19s12345z")
shanten = Shanten()
print(shanten.calculate_shanten(tiles))
v1.4.0 では 2、v2.0.0 では 6 を返します。
役なしの場合は計算結果を返さない
従来は、役なしの手でも error とあわせて fu、han、ドラ役などの情報が設定されていました。
v2.0.0 では、役なしの場合は error のみが設定され、それ以外のフィールドは None になります。
from mahjong.constants import EAST
from mahjong.hand_calculating.hand import HandCalculator
from mahjong.hand_calculating.hand_config import HandConfig
from mahjong.tile import TilesConverter
tiles = TilesConverter.one_line_string_to_136_array("123456m123456s11z")
win_tile = TilesConverter.one_line_string_to_136_array("6m")[0]
dora_indicators = TilesConverter.one_line_string_to_136_array("4z")
config = HandConfig(player_wind=EAST)
calculator = HandCalculator()
result = calculator.estimate_hand_value(
tiles,
win_tile,
dora_indicators=dora_indicators,
config=config,
)
print(f"{result.error=}")
print(f"{result.fu}符{result.han}飜")
print(result.yaku)
v1.4.0 の出力:
result.error='no_yaku'
40符2飜
[Dora 2]
v2.0.0 の出力:
result.error='no_yaku'
None符None飜
None
APIドキュメント整備
すべての public API に docstring が追加され、Sphinx ベースの API ドキュメントも公開されました。
APIドキュメント:
移行時の注意点
v2.0.0 はメジャーバージョンアップであり、破壊的変更を含みます。
通常の点数計算用途では影響は限定的ですが、
役クラスや内部寄りAPIを利用している場合は修正が必要になる可能性があります。
Python 3.10以上が必要
v2.0.0 では Python 3.9 のサポートが終了しました。
Python 3.10 以上が必要です。
役クラスの変更
Yaku クラスが再設計されました。
主な変更点は以下です。
-
Yakuが抽象基底クラス (ABC) になりました - 各役クラスの
yaku_idやnameなどがクラス属性になりました -
Yaku.english/Yaku.japaneseが削除されました - 各役クラスの
tenhou_idが削除されました- 天鳳の役IDが必要な場合は、
mahjong.hand_calculating.yaku_configのYAKU_ID_TO_TENHOU_IDを使用してください
- 天鳳の役IDが必要な場合は、
- 各役の
yaku_idの値が再割り当てされました
役クラスを直接参照しているコードや、yaku_id を保存・比較・シリアライズしているコードでは対応が必要です。
風牌役のクラス変更
風牌役の再設計により、旧クラスは新しいクラスに置き換えられました。
| v1.4.0 | v2.0.0 |
|---|---|
YakuhaiOfPlace |
SeatWindEast / SeatWindSouth / SeatWindWest / SeatWindNorth
|
YakuhaiOfRound |
RoundWindEast / RoundWindSouth / RoundWindWest / RoundWindNorth
|
YakuhaiOfPlace は自風に応じた SeatWind* 系クラスへ、YakuhaiOfRound は場風に応じた RoundWind* 系クラスへ置き換わります。
自風牌と場風牌が東南西北ごとに分離されたため、役一覧を直接処理しているコードでは対応が必要です。
一部定数がlistからfrozensetに変更
以下の定数は、list から frozenset に変更されました。
TERMINAL_INDICESWINDSHONOR_INDICESAKA_DORA_LIST
また、AKA_DORA_LIST は AKA_DORAS にリネームされています。
x in WINDS のような包含判定はそのまま動作します。
一方で、インデックスアクセスやリスト結合など、list 固有の操作に依存している場合は修正が必要です。
Meld.tiles が tuple に変更
Meld.tiles は list[int] から tuple[int, ...] に変更されました。
そのため、以下のようなインプレースな変更はできなくなります。
meld.tiles.append(tile)
meld.tiles.sort()
meld.tiles[0] = tile
Meld.tiles を変更する場合は、新しい tuple を代入する必要があります。
その他の破壊的変更
v2.0.0 では、上記以外にも一部の定数・引数・内部APIが削除または変更されています。
主な変更点は以下です。
-
Meld.CHANKANは削除されました。加槓にはMeld.SHOUMINKANを使用してください -
HandCalculator.estimate_hand_value()のuse_hand_divider_cache引数は削除されました -
HandCalculator.ERR_HAND_NOT_CORRECTは削除され、該当するケースではHandCalculator.ERR_HAND_NOT_WINNINGが返るようになりました
これらを利用しているコードでは修正が必要です。
また、HandDivider など内部寄りのクラスにも変更があります。これらを直接利用している場合はCHANGELOGも確認してください。
CHANGELOG: https://github.com/MahjongRepository/mahjong/blob/v2.0.0/CHANGELOG.md
まとめ
v2.0.0 では、点数計算の高速化、裏ドラ対応、風牌役の分離、API整理、型情報改善など、多くの変更が入りました。
一方で、Python 3.9 のサポート終了、役クラスの再設計、定数や Meld.tiles の型変更など、破壊的変更も含まれます。
既存コードを移行する場合は、特に役クラスや内部API利用部分への影響を確認してください。