Catch on Everywhere

いろんなものの練習をするブログ

人はなぜ(無償で)イベントを企画するのか

この記事は金曜日の29時ごろ書いたわけですが、

自分で読んでも普通に意味不明の文章になっています。

テンションをそのままお伝えするため、校正せずに投稿します。

はじめに

これはSplathon Advent Calendar 2018 - Adventarの24日目の記事です。

こんばんは、みーくんです。

もともと予定してたタイトルは

うるせ〜〜!!!!!知らね〜〜〜〜!!!!𝑭𝑰𝑵𝑨𝑳 𝑭𝑨𝑵𝑻𝑨𝑺𝒀

でした。僕が後のことを何も考えてない性格だということがよく分かりますね。

Splathonについては、コミュニティオーナー kawakuboxさんによる1日目の記事

kawakubox.hatenablog.com

をご覧ください。

Splathonでは各種オンラインイベントオーナーとして活動していましたが、 イキリが発動し、来年2月に開催される Splathon #9 のイベントオーナーもやることになりました。

この記事では、なんでイベントをやってるのか、なんでオフラインまでやりだしたのかを書いてみようかなと思います。

なんでイベントをやるのか

今まで

  • Splathon Online League #2, #3
  • Splathon eXtreme #1, #2

のイベントオーナーをしてきました。

これらとLadderは、通称 Online Event Admin と呼んでいるチーム*1があって、そのメンバーで運営企画をしています。
このチームは非常に優秀な人ばかりで、一人に負担が集中することもなくうまく分散しつつタスクを進行できているなと感じます。

各イベントをやって、毎回やりたいことを少しずつ入れていって、反応をみてまたやりたいことをやっていく。このサイクルは麻薬に近いものがあります。

イベント企画運営していると(分業しているとはいえ)時間・金銭リソースはガンガン消費されていくわけですが、 参加してくれた人がTwitterとかブログとかで「楽しかった」などの感想を書いてくれていると、 まあやりたいことやれてるしいいのかなという気持ちで今までやってきてます。

しかし、ほぼ空き時間なくずっとイベント運営をしていると、当然私生活へのマイナス面が出てきます。

企画運営に時間が取られ、自分がイカをやる時間や仕事関係の勉強時間がガッツリ減ってしまい、さらにお金も消えていきました。

たまにアンケートで「え、普通そんなん書く???」となるような感想が書かれ、一気に精神が削られることもあります。

それでも、なんでイベントをやるのか

マイナスがあるならこれ以上手を広げるのをやめればいいじゃん、という声が聞こえてきそうです。

超正論なので耳が痛いです。

....なんですが、こんだけイベントを好き勝手やっているけど、やればやるほど、もっとこうしたい!という欲が増大していくわけです。

もともとRAGEを現地観戦しにいってたことや、参加者としてスプラトゥーン甲子園に出た経験などを通じて、

オフラインのゲームイベントが人に与えるパワーはものすごく大きい

と感じていました。

これって、どれだけ頑張っても、オフラインイベントじゃ満たせないんですよね。
名前も知らない隣にいる人と、リアルタイムで感動を分かち合ったりはできないわけです。

そうなったとき、今の自分が手を出せる場所といったら、Splathon本体しかなかったのです。

そうして、イキリが発動してしまった僕は、ついにオフラインのナンバリングタイトルにも手を出してしまったのでした。

おわりに

自分のやりたいことベースで進行しているので、そのぶん身体を張ったつもりでいます。

たとえば機材や会場をスポンサードしてくださる企業を探すためにアポを取りまくったり、 だめだったら社内でなんとかならないかと役員や親会社と調整して回る日々でした。

割と地獄のような感じでした。

そんな中めちゃくちゃ助けてくれたOnline Event Adminのみんなには感謝してもしきれないです。

また、今までSplathon #1~8を運営してきた先駆者達が作った礎があったからこそ、 自分が好き勝手にウワモノを乗せることができています。

いろいろすれ違いもありましたが、最終的に自分に担当させてもらえたことに感謝しています。

あと、みんなイベントの感想とかブログやTwitterに書いてください。

参加者の「楽しかった」という声があるだけでイベント企画する元気が出ます。
SOLとかLadderのチームでどういう話をしてた、とかそういう感じのレポートとかあると超喜びます。

--

さて、明日はついに大トリです。

皆さんお待ちかね、謎の外国人 D氏による

走れそりよ 風のように 雪の中を 軽く早く 笑い声を 雪にまけば 明るいひかりの 花になるよ ジングルベル ジングルベル 鈴が鳴る 鈴のリズムに ひかりの輪が舞う ジングルベル ジングルベル 鈴が鳴る 森に林に 響きながら黒塗りの高級車に追突してしまう。 後輩をかばいすべての責任を負った三浦に対し、 車の主、暴力団員谷岡が言い渡した示談の条件とは…。

です。なんだよこれ。

*1:kawakubox, awa, tsukaby, nagayama, ta, *******. 敬称略. 事情があって1人だけ名前出さないでおく

[Splatoon2]ブキの強さを統計モデルで推定してみる

[Splatoon2]ブキの強さを統計モデルで推定してみる

この分析は7月ブキリリース~7月中旬アプデ前の期間を対象としています。

きっかけ

Splatoon2ブキ研究所という方が、 stat.inkで公開されているデータを元に平均勝率などを算出し、Twitterに定期的に投稿されています*1

具体的な手法は公開されていない認識ですが、多人数PvPゲームかつ半月〜1ヶ月単位で変化していく複雑な環境を探っていくための情報を提供してくれる貴重な情報ソースであることは確かです。

そんな中、彼の一連のツイートが目に入りました。

ちょうど、新しくリリースされた「クーゲルシュライバー」というブキの評価で議論があった時期のツイートです。

私自身が前作から現在に至るまでスピナーを使い続け、X帯でプレイしているひとりですので、このツイートには非常に興味をひかれました。 「理論上強いことは間違いないものの、使いこなすまでのハードルが相当高い」ブキであり、前作でいうところの「H3リールガンチェリー」と同じ立ち位置であるという認識でしたが、彼の主張では実際は勝率が低く「強いブキ」ではないそうです。

単純に勝率を計算すると上記のような結論に至るのであれば、もう少しアプローチを変えてみたら違う結果が出るのではないか?と考え、ブキごとの勝率を決める要因を分解し、推定してみようと思います。

手法

今回は勝率を単純に予測するモデルを作るわけではなく、ブキごとのポテンシャルをデータから推定したいので、ベイジアンモデリングを用いて分析していくことにしました。

推定したブキの強さを上から順に並べたものと単純に集計した勝率で上から並べたものを比較してみることで、「使う人が使えば強い武器」「キルを取らないと勝てないブキ」などが見えてくるのでは、と期待して分析をしていきます。

使用する環境はRとStanです。バージョンは下記の通りです。

> sessionInfo()$R.version$version.string
[1] "R version 3.4.3 (2017-11-30)"

> rstan::stan_version()
[1] "2.17.0"

ベイジアンモデリングはまだ経験が浅く不慣れなため、 もし記事中に間違いがあった場合は遠慮なくご指摘いただけますと幸いです*2

今回は簡単化のために、ガチエリアに限定かつマップによるブキの有利不利を考慮しない形で議論をしていきます。

モデル

推定したいブキの強さに加えて、今回は個人スキルというものを考えてみます。

個人スキルとは、その名の通りプレイヤー自身のスキルです。当然のことながら、同じ武器を使っていたとしても、プレイヤーによって立ち回りも違えばエイム力も違うので、勝ちやすさが変わってくることが想像できます。

勝敗を説明する要因をブキの強さと個人スキルの2つに分離して推定することで、個人スキルを一定にしたときどのブキが相対的に強いといえるのかを考察することができます。

モデル式

まずは

N : サンプルサイズ
W : ブキ種数
U : ユーザー数
Y : 勝敗フラグ(1が勝利、0が敗北)
a[u] : 個人スキル
b[w]: ブキの強さ

と置いて、勝敗を確率変数で表現してみます。

Y は 0または1を取るので、ベルヌーイ分布で大丈夫そうです。ベルヌーイ分布が取るパラメーター p は事実上勝率を表現しています。今回はこの pa[u] + b[w] で表現できると仮定して進めていきます。

つまり、個人スキルとブキポテンシャルの和が勝率になる、というモデル です。

a[u] + b[w]シグモイド関数を挟んで0 ~ 1の範囲でに収まるように、a[u]b[w] はそれぞれユーザー、ブキごとに正規分布に従うと仮定すると、それぞれ以下の式で表現できます。

 Y \sim bernoulli\_logit( a[u] + b[w])
 a[u] \sim normal( 0, s_a)
 b[w] \sim normal( 0, s_b)

しかし、このままだとa[u]b[w]が特定の条件下で交換可能となってしまい、収束しない可能性があるので a[u] がその試合のキル数に影響を与える という仮定を追加します。

 Kill[n] \sim normal(a[u] , s_k)

なお、 Kill[n] はStanへデータを渡す際に0 ~ 1の範囲で収まるように最大値で割っています。

実際にKickしたStanコードは下記の通りです。

data {
  int N; //サンプルサイズ
  int W; //ブキ種類
  int U; //ユーザー数
  int<lower=1, upper=W> WeaponID[N]; //ブキID
  int<lower=1, upper=U> UserID[N]; //ユーザーID
  int<lower=0, upper=1> Y[N]; //勝敗結果
  real<lower=0, upper=1> Kill[N]; //キル数
}

parameters {
  real a[U];
  real b[W];
  real<lower=0> s_a;
  real<lower=0> s_b;
  real<lower=0> s_kill;
}

model {
  for (u in 1:U) {
    a[u] ~ normal(0, s_a);
  }
  for (w in 1:W) {
    b[w] ~ normal(0, s_b);
  }
  for (n in 1:N) {
    Kill[n] ~ normal(a[UserID[n]], s_kill);
    Y[n] ~ bernoulli_logit(a[UserID[n]] + b[WeaponID[n]]);
  }
}

データ

データソースは stat.ink を使用します。

ダウンロードページが提供されていますが、個人を識別可能なID*3を取得するために今回はAPI経由でデータを取得しました。

また、使用するデータは下記の条件で抽出しています。

  • 対象試合期間:2018/7/1 9:00:00 (JST) ~ 2018/7/14 9:00:00 (JST)
    • 7月新ブキリリースから7月中旬の調整アップデートまで
  • 野良のガチエリア、X帯の試合のみ
  • stat.inkへ投稿したAgent:splatnet2statink, SquidTracks
    • 自動投稿のものに限定することでデータの正確性を上げようとしている
  • 8人で行われた試合
  • ブキやマップ、ルール、キル数等のデータが1つでも欠落していないもの
  • 取得したデータ中で4試合以上プレイしているユーザーデータのみを使用

最後の条件である "取得したデータ中で4試合以上プレイしているユーザー" を加えると、該当するユーザーは 3,467人 、全部で 27,312試合 になります。

最後の条件を入れない場合のユーザー数は16,325人なため、1/4くらいのデータしか使用していないことになります。

また、今回は個人差をモデルに組み込んでいるため投稿者自身のデータも使用していますが、なんと109人しかいないようです。ここから、stat.inkのデータがX帯全体を代表しているかどうかの議論も行われているようです*4

記事を書き終えてから気づきましたが、1試合にikalog投稿者が2名以上いた場合の対応を入れていません。 同一の試合であったかどうかを判定する術はなくはないですが、面倒なので別試合としてみなしています。 数はそんなに多くないはず……

計算結果

パラメータを iter = 3000, chain = 4 に設定し、{rstan} 経由で実行しました。 最後のChainの計算に9000秒も掛かっていますね……

Elapsed Time: 1043.98 seconds (Warm-up)
               7958.47 seconds (Sampling)
               9002.44 seconds (Total)

b[w]の50%ベイズ信用区間をプロットしたものが下記グラフです。

f:id:mii_kun_02:20180720123956p:plain

考察

ここで、単純に勝率を集計したとき*5のブキ順位と今回の b[w] 推定値での順位を比較してみます。

f:id:mii_kun_02:20180720153329j:plain

ここから以下のようなことが読み取れます。

  • ラピエリデコ、バレデコ、竹甲、わかば、キャンシェル、ジェッカス、ホッカスが上位に入っている
    • 個人的にはラピ、バレ、キャンシェル、ジェッカス、ホッカスは納得感がある。
  • フデ系統(パブロヒュー、ホクサイ)が高めなのはなんでだろう?
  • クーゲルはかなり下に位置している
    • まだ野良で使いこなせている人は多くなかった?
  • スパイガジェット、クアッド、スシコラがかなり順位を落としている
    • クアッド、スシコラは意外だったが、これはモデルの制約の都合かもしれない。
    • このモデルはキル数が個人スキルと結びつくようになっているので、個人スキルが高い(=キルを多く取る)プレイヤーばかりがこれらのブキを使用していて、かつ勝率が極端に高いわけではない場合、ブキそのものの強さが低く出てしまう。
    • とはいえ、「キルを取らないと勝てないブキ」と解釈することもできそう。

おわりに

stat.inkから取得できるデータをもとにベイジアンモデリングを実施し、様々な考察を行いました。 当然データには偏りがあるため、ここで出た結果が当時の環境を正確に表現できているわけではありませんが、一部に関しては確かにそうかもしれないと納得できる箇所もありました。

今回は勝敗とキル数しか使用しませんでしたが、デス数やSP発動回数なども勝敗に関わってくる要因だと思うので、この辺もモデルに組み込めば更にわかることも増えるかと思います。 また、キル数が個人スキルに影響を受けると仮定しましたが、そこにも議論の余地はあると思います。今後同様の検討をしてくださる方が増えることを望みます。

Github にモデルとコードを置きましたので、間違いがあればPRを送っていただけると助かります。 なお、取得したデータについては二次配布してよいかがわからなかったため、githubには上げていません。 また、スクレイピング部分のスクリプトに関してもいろいろ責任取れないので上げていません。 Rがわかる人向けに書くと、

json <- 
    content(get.json, "parsed", "application/json") %>% 
    purrr::keep(~ .$agent$name %in% c("splatnet2statink", "SquidTracks")) %>% 
    purrr::keep(~ .$game_version %in% c("3.1.0", "3.2.0")) %>% 
    purrr::keep(~ .$mode$key != "private") 

こんな感じでフィルタリングし、

list(
          battle_id = json[[i]]$id,
          version = json[[i]]$game_version,
  
          lobby_key = json[[i]]$lobby$key,
          mode_key = json[[i]]$mode$key,
          rule_key = json[[i]]$rule$key,
          map_key = json[[i]]$map$key,
          x_power =  json[[i]]$x_power,
          estimate_x_power =  json[[i]]$estimate_x_power,
          
          result_my_team = json[[i]]$result,
          
          user_order = n,
          user_name = json[[i]]$players[[n]]$name,
          splatnet_id = json[[i]]$players[[n]]$splatnet_id,
          team = json[[i]]$players[[n]]$team,
          is_me = json[[i]]$players[[n]]$is_me,
          
          level = json[[i]]$players[[n]]$level,
          star_rank = json[[i]]$players[[n]]$star_rank,
          rank = json[[i]]$players[[n]]$rank$key,
          rank_exp = json[[i]]$players[[n]]$rank_exp,
          
          weapon_base = json[[i]]$players[[n]]$weapon$main_ref,
          weapon = json[[i]]$players[[n]]$weapon$key,
          weapon_type = json[[i]]$players[[n]]$weapon$type$key,
          weapon_category = json[[i]]$players[[n]]$weapon$type$category$key,
          
          kill = json[[i]]$players[[n]]$kill,
          death = json[[i]]$players[[n]]$death,
          assist = json[[i]]$players[[n]]$kill_or_assist -  json[[i]]$players[[n]]$kill,
          special = json[[i]]$players[[n]]$special,
          point = json[[i]]$players[[n]]$point,
          
          is_top500 = json[[i]]$players[[n]]$top_500,
          
          start_at = json[[i]]$start_at$time,
          end_at = json[[i]]$end_at$time
        )

こんな感じでパースしています*6

このデータからこういうことが言えるのではないか?ということや疑問に思われたことがあれば、 Twitter へご連絡いただくか、この記事にコメントをしていただければと思います。

参考資料

*1:どうやら記事を書いている途中で鍵アカウントにされてしまったようです

*2:Twitter宛でもブログのコメントでも構いません。 炎上ラーニング的な。

*3:あくまで同じIDのユーザーは同一人物である、という情報であり、そのIDを使用してイカリング2からデータを不正に取得する等の行為はできません。

*4:http://iekomori.hatenablog.com/entry/2018/07/10/230451 など

*5:計算に用いた元データから投稿者自身のデータを除いたものを使用。また「4試合以上~」の条件は除き、stat.inkの統計データDLページから取得できるデータにできるだけ近くしています。

*6:なんかspecialの値がおかしかったので、どっか間違っている可能性があります