見出し画像

beatmania IIDXのアリーナで投げるべき曲を教えてくれるwebアプリを開発した話(Python3+Discord bot)

こんにちは、かたさんです。

一番の趣味であるbeatmania IIDXのアリーナモードで勝つために、選ぶべき曲を教えてくれるインチキシステムを作ったので紹介します。

スクレイピングの細かい話には触れず、webアプリ開発・データ分析といった視点で簡潔に書いていきます。

先にこのシステムが動いている様子を乗せておきます。

背景: beatmania IIDXのアリーナモードについて

先日、beatmania IIDXでアリーナモードが解禁されました。アリーナモードとは、ネットワークを介して世界中のプレイヤーとリアルタイム対戦ができるモードです。

アリーナモードの様子(最終結果画面)

一度の試合では最大3人までのプレーヤーとマッチングして一人一曲ずつを投げ合い、順位に応じて得られるポイント(1位2pt、2位1pt)の合計が最も高かった人が勝ちというルールとなります。

また、各プレイヤーは対戦成績に応じてランク付けされます(最高ランクはA1、その下はA2, A3, A4, A5, B1, B2, …, D5と続く。筆者の過去最高ランクはA4)。このアリーナランクが近い人同士でしかマッチングさせないことで、ある程度実力が近いプレーヤ同士で対戦できるようになっています。

課題: オンライン対戦の難しさ

しかし、このbeatmania IIDXという音楽ゲームはあまりにも個人差が出やすく、トップランカーですら得意な曲は千差万別だったりします。

レベル12(このゲームの最高レベル)の高密度譜面が得意な人もいれば、BPM変化が激しい曲が異様に上手い人もいるし、レベル9くらいの曲でとんでもない精度を出すのが得意な人もいるし、…と言った具合に、かなり多彩な戦い方ができる音楽ゲームなのです。

beatmania IIDXのゲーム性の高さについては、BPL2021をご覧になった方には伝わりやすいかもしれません。地力のあるプレイヤーが必ず勝てる!というわけではなく、戦略がしっかりハマれば格上のプレイヤーを刺すこともできる点が非常に面白いです。

しかし、この幅広いゲーム性のために生まれる課題もあります。楽曲解禁のためにたまにアリーナに入る程度の一般プレイヤーには、初めて見る相手プレイヤーに一体どんな曲が刺さるのか全く分からないのです。

そこで、公式サイトのプレーデータをスクレイピングし、マッチング時間の間に相手プレイヤーの情報を調べ上げて投げるべき曲を教えてくれるシステムをPython3で作りました。

今回開発したシステム

Discord botを動かすためのクラスとKONAMIのサーバからプレイヤーデータやスコアデータを取得するためのクラスをPython3で実装し、これらをVPS上で実行しています。

今回開発したシステムの概要

プログラムの入口部分となる、Discord bot部の大まかなコードは以下のようになっています。メッセージが書き込まれた時のハンドラを定義して、あとはライブラリによろしくやってもらっています。(よくあるイベント駆動型プログラミングだと思いますが、あまり細かくは理解していません)

#!/usr/bin/python3
import discord
client = discord.Client()
import pandas as pd

TOKEN="discord botのアクセストークン"

acc = Accessor() # スクレイピング用クラス
rival = []       # ライバルデータ格納用配列
level = [11,12]  # 取得対象のレベルを格納した配列

diff_max = 500   # ハード落ちスコアを除外するために、これ以上点差がある曲は表示しない
diff_min = -50   # これ以上負けている曲を表示しない

#Bot起動時に実行される
@client.event
async def on_ready():
    print('ログインしました')

#メッセージを取得した時に実行される
@client.event
async def on_message(message): 
    #Botのメッセージは除外
    if message.author.bot:
        return
    #条件に当てはまるメッセージかチェックし正しい場合は返す
    def check(msg):
        return msg.author == message.author
    #/getとチャンネル上に打ち込むとBotが反応を示す
    if message.content.startswith("h"):
        msg = "(操作方法を書く)"
        await message.channel.send(msg)
    elif message.content.startswith("r"): # ライバル一覧を表示
        msg = "ライバル一覧の名前"
        await message.channel.send(msg)
    elif message.content.startswith("s"): # ライバル検索
        """
        message.contentに書き込まれた検索クエリから検索実行
        プレイヤーを特定したら、スコアデータを取得し、配列rivalにappendする。
        """
    elif message.content.startswith("c"): # 比較する
        """
        スコアの比較を行い、リコメンド情報を作成する。
        最終的に得られたテーブルをdiscordへ書き込む
        """
    elif message.content.startswith("x"): # リセット
        rival.clear()
        await message.channel.send('スコアデータを初期化しました。')

#Botの実行
client.run(TOKEN)

ユーザインターフェース

ユーザが使えるコマンドは以下のようになっています。全てDiscordへの書き込みとなります。

h: ヘルプを表示
s DJNAME [query(area spclass dpclass)]: DJNAMEを検索しスコアを取得
m: 自分のスコアを取得
r: ライバル一覧を表示
c idx0 idx1: idx0idx1と戦う場合のリコメンドを表示
l levelX [levelY ...]: レベル設定を変更 (現在の設定:[11, 12])
x: 取得済みデータをリセット

自分(ログイン中のID)だけサーバ上での扱いが異なるため、検索とは別コマンドで取得するようにしています。

ユーザ検索機能については、所属エリアや段位での絞り込みを行えるようにしています。スマホでも高速に入力できるように、皆伝は「kai」、神奈川県は「kana」などの短い表記に対応させています(Pythonのdictを活用しています)。

リコメンド作成処理

投げるべき曲のリコメンド作成は以下の手順で行います。

  1. 比較したい二人のスコアデータ(Sa, Sbとする)を難易度別のページから取得する。取得対象の難易度は指定可能で、デフォルト設定は11,12としている。

  2. Sa, Sbから二人ともプレイ済みの曲一覧Cを取得し、Sa, SbからCに含まれない曲を除外する。

  3. Sa, Sbの差分diffを計算し、差分値が大きいものほど上になるようにソートする。

  4. diffに対し、差分値が500点を超えるものを除外する。

  5. diffの上位20位をdiscordへpostする。

2で双方がプレイ済みの曲を求めていますが、これはSa, Sbでpandas.DataFrameのインデックスが不一致だとそのまま引き算できないためです。

また、4で点差が大きいものを除外しているのは、相手がハード/エクハで落ちたスコアをそのまま放置していた場合への対処となります。本当に1000点差がつくほど苦手な可能性もあるので扱いが難しいですが、これくらい離れていれば良いかと割り切っています。

おわりに

ということで、アリーナでマッチングした相手に投げる曲を調べるシステムを開発しました。

今回は説明しませんでしたが、認証したところからスタートできるJavaScriptを使わずとも、HTTPアクセス周りの処理を自前で実装してしまえばPython3でもちゃんとスクレイピングできることが分かったのは大きいです。pandasのお陰でjsよりも圧倒的にデータ処理が楽ですしね。

また、Discord botを経由することで、スマホからも操作できるようになっています。Discord botは初めて触りましたが、かなり簡単にwebアプリを作成することができて良かったです。おすすめ。

本システムでは、一人分のスコアデータ取得に12秒ほど(レベル11,12の場合。レベル12だけなら半分くらいになる)かかりますが、マッチング待ちの時間が最大約30秒、その後の選曲時間が45秒あることから相手のDJネームさえ見ていれば十分使える性能に仕上がっていると考えます。

ちなみに、このシステムを使えばBPLやKACのような公式大会で投げるべき曲は何だったのか?みたいな話も見えたりします。(双方ともプレーデータを公開設定にしていれば、ですが)  

UCCHIE さんが RAG* さんに投げるべき曲
灼熱Beach Side Bunny [SPA] (+294)
Yellow Sketch(RX-Ver.S.P.L.) [SPA] (+159)
Sigmund [SPA] (+106)
Raveit!! Raveit!!  [SPA] (+102)
ディスコルディア [SPA] (+72)
BLACK.by X-Cross Fade [SPA] (+49)
Beyond The Earth [SPA] (+49)
Be quiet [SPL] (+46)
Dynamite [SPA] (+38)
TRIUMPH [SPA] (+35)
GENE [SPA] (+33)
デモーニッシュ [SPA] (+31)
Fegrix [SPA] (+30)
紫陽花 -AZISAI- [SPA] (+29)
金野火織の金色提言 [SPA] (+29)
EVANESCENT [SPA] (+29)
表裏一体!?怪盗いいんちょの悩み [SPA] (+28)
Boomy and The Boost [SPA] (+27)
BIGソムタム [SPA] (+24)
Purple Perplex [SPA] (+23)

このシステムやソースコードを公開する予定はありませんが、今週末にはアリーナで試してみたいと思います。今作はA3まで上がりたい!(こんなの作ってる暇があったら地力を上げた方が早いのは言うまでもない)

技術のコア部分にはほぼ触れていませんが、Discord botやPython3の凄さが少しでも伝わっていましたら幸いです。

それでは。


この記事が気に入ったらサポートをしてみませんか?