みなさんこんにちは。LAPRASでデータベースエンジニアをしている@denzowillです。
突然ですが、社会人のスケジュールで最も多くの時間を占める予定はなんでしょうか?
そうです。"MTG"です。
社会人のスケージュルには多くのMTGが入っています。私も、LAPRASで勤務する中では業務時間中はもちろん、時にはランチMTG、業務時間終了後の残業MTGをすることもあります。
パフォーマンスチューニングでは、最も大きなボトルネックから着手するのが定石です。そのため、予定の多くを占めるMTGをハックすることは、労働効率を上げる上で効果的と言えます。
私はエンジニアなのでハックするなら精神論ではなくシステムを使うなり作るなりして対処したいです。そこで今回はMTGをハックするための技術を見ていきます。
MTGの定義
まずはハック対象であるMTGの定義を確認します。今回対象としているMTGは
- 集会
- 会合
- 報告会
などではなく、Magic: The Gatheringです。ということで、ミィーティングの効率化を知りたい方はこれ以降有用な情報は一切書かれていないことをお伝えしておきます。
MTG関連のAPI等
MTGは歴史が長いだけあって、色々データが整っています。利用できるメジャー所のサービスも色々特徴がありますのでそれぞれ紹介していきます。
Magic: The Gathering API(api.magicthegathering.io
)
MTGのカードやフォーマット等の情報を取得することが出来るAPIが提供されています。Qiitaでもいくつか記事で紹介されています。
こちらは https://api.magicthegathering.io/<version>/<resource>
と言った形式のREST APIを提供している上、各種主要言語に対応したSDKを提供しています。
ちなみにドキュメント外ではありますが、直接GitHubを見に行くとElixir
やRust
等のSDKも提供されています。
ちなみにこの手のSDKは単にAPIのラッパーなだけなことが多いのですが、こいつはQueryBuilder
を持っていておしゃれに使えます。例えば、以下はカード名のオーコ
を日本語表記内で含み、カードタイプがPlainswalker
を含むカードを探し出します。
cards = Card.where(name='オーコ').where(language='japanese').where(types='Planeswalker').all()
for card in cards:
original_name = card.name
japanese_name = [f for f in card.foreign_names if f['language'] == 'Japanese'][0]['name']
print(f'English: {original_name}, Japanese: {japanese_name}')
English: Oko, Thief of Crowns, Japanese: 王冠泥棒、オーコ
English: Oko, the Trickster, Japanese: トリックスター、オーコ
このAPIのRate limitは 5000 request/1時間 で特に引き上げる方法もなさそうなのでおとなしめに使いましょう。また、検索系のパフォーマンスはかなり悪いです。おそらく全文検索エンジン等を使用しない素直なRDBMSをバックエンドに持っているのでしょう。先のサンプルコードでも5秒くらいかかります。
なお、このAPIは決して公式(ウィザーズオブザコースト)が提供しているものではありません。有志の方が管理しているAPIのようです。
MTGJSON
先のAPIのデータとして利用されているのがmtgjsonです。
mtgjsonは、有志によってMTGに存在するカード等の全てを取り回しの効くフォーマットとしてメンテナンス・提供をするプロジェクトです。これらのデータはJSONやSQLファイル、sqliteのデータベースファイルとして誰でもダウンロード可能になっています。
これらを利用して、先のAPIのように独自のMTG関連のサービスを提供することが出来ます。逆に、カード情報に関するサービスを自作するのであれば先のAPIを使うのではなくこちらのデータベースを直接利用する方が楽です。
なお、mtgjsonはGitHubリポジトリもあります。
これは、mtgjsonが提供しているデータをビルドするためのコードが提供されています。そのため、このリポジトリをローカルで動かすことで同様のデータを手に入れることが出来ます。じゃあその元データは何処?と思って見てましたが https://scryfall.com/ から取得しているようです。
SCRYFALL_API_SETS: str = "https://api.scryfall.com/sets/"
SCRYFALL_API_CARD: str = "https://api.scryfall.com/cards/"
SCRYFALL_API_CATALOG: str = "https://api.scryfall.com/catalog/{0}-types"
SCRYFALL_VARIATIONS: str = "https://api.scryfall.com/cards/search?q=is%3Avariation%20set%3A{0}&unique=prints"
SCRYFALL_SET_SIZE: str = "https://api.scryfall.com/cards/search?order=set&q=set:{0}%20is:booster%20unique:prints"
SCRYFALL_API_SEARCH: str = "https://api.scryfall.com/cards/search?q=(o:deck%20o:any%20o:number%20o:cards%20o:named)"
scryfall
MTGのカードの検索を提供している https://scryfall.com/ にもREST APIが提供されています。
こちらもapi.magicthegathering.io
同様にカードの検索等が提供されており、更にapi.magicthegathering.io
よりも高速に動作します。残念ながら公式に提供されているSDKはありませんが、REST APIなのでラップしたSDKを作ることはそれほど難しくないでしょう。
気になるRateLimitは明確な値は書かれていませんが、10 request/秒程度とのことですのでapi.magicthegathering.io
より少し厳し目ですが、APIとしてのスペック的にはこちらのほうが良さそうです。
まとめ
結局何使えばいいのかという点でいうと以下の様な形になります。
- とにかくSDKがほしい
api.magicthegathering.io
- 高性能な検索APIがほしい
- scryfall
- データベースごとほしい
- mtgjson
自身でデータベースを持たない場合はscryfall, データベースを持った上で何か作るのであればmtgjsonを利用するのが良さそうです。MTGが好きなエンジニアさんが自分でなにか面白いことをする時のデータの取得先として参考になれば幸いです。